C# PDF-owe pokwitowania i rejestry transakcji dla aplikacji FinTech
Problem zgodności, a nie tylko problem formatowania
Większość aplikacji FinTech przechowuje dane transakcyjne w relacyjnej bazie danych i generuje potwierdzenia na żądanie — gdy klient o to poprosi, aplikacja ponownie wysyła zapytanie do rekordu i wyświetla wynik. Takie podejście ma zasadniczą wadę: wynikowy plik PDF lub "paragon" odzwierciedla aktualny stan danych, a nie to, co klient widział w momencie zakończenia transakcji.
Rekordy bazy danych mogą być korygowane, zmieniane lub aktualizowane w ramach normalnych procesów operacyjnych. Ponownie wygenerowany paragon nie jest dokumentem historycznym, lecz aktualnym zrzutem ekranu z datą z przeszłości. Gdy operator płatności ma do czynienia z obciążeniem zwrotnym, gdy zespół ds. zgodności neobanku odpowiada organowi regulacyjnemu, gdy żądany jest dziennik audytowy platformy pożyczkowej lub gdy giełda kryptowalut musi wykazać integralność dokumentów przed inspektorem KYC, potrzebny jest dokument, a nie zapytanie.
Dokument PDF zapisany w momencie rozliczenia, z hashowaniem w celu wykrywania manipulacji i zapisany w niezmiennej pamięci, jest właśnie tym dokumentem. Dzięki temu istniejące dokumenty PDF zachowają swoją aktualność przez lata. Niezależnie od tego, czy masz do czynienia z raportami finansowymi, czy prostym pierwszym plikiem PDF, generowanie dokumentów musi być ostateczne.
Obowiązki dotyczące prowadzenia dokumentacji wynikające z norm PCI-DSS, SOX i AML nie wymagają konkretnie dokumentów PDF generowanych programowo, ale wymagają dokumentacji, którą można wykazać i poddać audytowi. Renderowany, zaszyfrowany i opatrzony sygnaturą czasową plik PDF spełnia ten wymóg w sposób, w jaki sam wiersz bazy danych tego nie robi. W dalszej części tego artykułu przyjrzymy się przykładowi IronPDF, aby zobaczyć, jak ten proces może wyglądać podczas tworzenia nowego dokumentu PDF.
Rozwiązanie w skrócie: Konwersja treści HTML do formatu PDF w języku C
Biblioteka IronPDF firmy Iron Software generuje potwierdzenie w formacie PDF dokładnie w momencie zakończenia transakcji, synchronicznie, w ramach procesu transakcyjnego. Bibliotekę IronPDF można zainstalować za pomocą menedżera pakietów NuGet lub konsoli menedżera pakietów w programie Visual Studio. Korzystając z dot NET CLI, wystarczy uruchomić instalację pakietu IronPDF.
Potwierdzenie jest generowane na podstawie szablonu HTML lub pliku HTML, opatrzone identyfikatorem transakcji i sygnaturą czasową, hashowane i zapisywane w niezmiennej pamięci. Będzie to dokument ostateczny. Nie ma definicji SSRS do utrzymania, nie ma API dokumentów stron trzecich do wywołania i nie ma przeglądarki bezinterfejsowej typu sidecar. IronPDF działa w ramach procesu jako biblioteka NuGet do zadań związanych z plikami PDF.
Kluczowe korzyści z automatyzacji przepływu pracy z dokumentami:
-
Zachowanie formatowania na wszystkich rozmiarach stron.
-
Obsługa podpisów cyfrowych w celu zapewnienia integralności dokumentów.
-
Możliwość tworzenia obiektów PDF na podstawie strony internetowej lub ciągu znaków HTML.
- Logo klientów i elementy identyfikacji wizualnej firmy Iron Software można łatwo wstawić
Jak działa proces od transakcji do paragonu
Scenariusz idealny
Płatność przebiega pomyślnie, a zapis transakcji jest zapisywany w bazie danych. Ten sam handler — przed zwróceniem odpowiedzi — wypełnia szablon potwierdzenia zawartości HTML szczegółami transakcji: identyfikatorem, sygnaturą czasową UTC, kwotą i walutą, identyfikatorami nadawcy i odbiorcy, zestawieniem opłat oraz treścią dynamiczną.
Renderer new ChromePdfRenderer (a konkretnie var renderer = new ChromePdfRenderer();) konwertuje zawartość internetową do formatu PDF. Powstała tablica bajtów PDF jest natychmiast hashowana przy użyciu algorytmu SHA-256.
using IronPdf
using System.Security.Cryptography;
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.MarginTop = 15;
renderer.RenderingOptions.MarginBottom = 15;
string receiptHtml = $@"
<h1>Transaction Receipt</h1>
<p><strong>Transaction ID:</strong> {tx.Id}</p>
<p><strong>Timestamp (UTC):</strong> {tx.CompletedAt:u}</p>
<p><strong>Amount:</strong> {tx.Amount:F2} {tx.Currency}</p>
<p><strong>Fee:</strong> {tx.Fee:F2} {tx.Currency}</p>
<p><strong>From:</strong> {tx.SenderRef} → <strong>To:</strong> {tx.ReceiverRef}</p>
<p><strong>Resulting Balance:</strong> {tx.ClosingBalance:F2} {tx.Currency}</p>";
var pdf = renderer.RenderHtmlAsPdf(receiptHtml);
string hash = Convert.ToHexString(SHA256.HashData(pdf.BinaryData));
await _db.StoreReceiptHashAsync(tx.Id, hash);
await _blobStorage.UploadImmutableAsync($"receipts/{tx.Id}.pdf", pdf.BinaryData);
using IronPdf
using System.Security.Cryptography;
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.MarginTop = 15;
renderer.RenderingOptions.MarginBottom = 15;
string receiptHtml = $@"
<h1>Transaction Receipt</h1>
<p><strong>Transaction ID:</strong> {tx.Id}</p>
<p><strong>Timestamp (UTC):</strong> {tx.CompletedAt:u}</p>
<p><strong>Amount:</strong> {tx.Amount:F2} {tx.Currency}</p>
<p><strong>Fee:</strong> {tx.Fee:F2} {tx.Currency}</p>
<p><strong>From:</strong> {tx.SenderRef} → <strong>To:</strong> {tx.ReceiverRef}</p>
<p><strong>Resulting Balance:</strong> {tx.ClosingBalance:F2} {tx.Currency}</p>";
var pdf = renderer.RenderHtmlAsPdf(receiptHtml);
string hash = Convert.ToHexString(SHA256.HashData(pdf.BinaryData));
await _db.StoreReceiptHashAsync(tx.Id, hash);
await _blobStorage.UploadImmutableAsync($"receipts/{tx.Id}.pdf", pdf.BinaryData);
Imports IronPdf
Imports System.Security.Cryptography
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.MarginTop = 15
renderer.RenderingOptions.MarginBottom = 15
Dim receiptHtml As String = $"
<h1>Transaction Receipt</h1>
<p><strong>Transaction ID:</strong> {tx.Id}</p>
<p><strong>Timestamp (UTC):</strong> {tx.CompletedAt:u}</p>
<p><strong>Amount:</strong> {tx.Amount:F2} {tx.Currency}</p>
<p><strong>Fee:</strong> {tx.Fee:F2} {tx.Currency}</p>
<p><strong>From:</strong> {tx.SenderRef} → <strong>To:</strong> {tx.ReceiverRef}</p>
<p><strong>Resulting Balance:</strong> {tx.ClosingBalance:F2} {tx.Currency}</p>"
Dim pdf = renderer.RenderHtmlAsPdf(receiptHtml)
Dim hash As String = Convert.ToHexString(SHA256.HashData(pdf.BinaryData))
Await _db.StoreReceiptHashAsync(tx.Id, hash)
Await _blobStorage.UploadImmutableAsync($"receipts/{tx.Id}.pdf", pdf.BinaryData)
Przykładowy wygenerowany dokument PDF
Nowy dokument PDF jest następnie zapisywany w niezmiennej pamięci. Niezależnie od tego, czy jest to Twój pierwszy plik PDF, czy też łączysz istniejące pliki PDF, proces pozostaje taki sam. Kopia jest wyświetlana w przeglądarce plików PDF dla klienta. Podczas prezentacji produktów Iron Software zespół często podkreśla, jak zaledwie kilka linii kodu pozwala obsługiwać dynamiczne raporty i zadania, takie jak konwersja HTML do PDF lub generowanie plików PDF.
Aby ułatwić korzystanie z interfejsu użytkownika (UI) i doświadczenia użytkownika (UX), na pulpicie nawigacyjnym mogą być widoczne klucze w niebieskich lub szarych kółkach, oznaczające zabezpieczone, zaszyfrowane pliki. Ikona w postaci strzałki w prawo przy linku do pobrania ułatwia użytkownikom nawigację.
Wypisany nagłówek lub stopka potwierdza pochodzenie dokumentu na każdej stronie:
var shortHash = hash[..12];
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
HtmlFragment = $@"
<div style='font-size:9px; color:#666; text-align:center;'>
Generated: {tx.CompletedAt:u} |
TX: {tx.Id} |
SHA-256: {shortHash}...
</div>"
};
var shortHash = hash[..12];
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
HtmlFragment = $@"
<div style='font-size:9px; color:#666; text-align:center;'>
Generated: {tx.CompletedAt:u} |
TX: {tx.Id} |
SHA-256: {shortHash}...
</div>"
};
Przykład stopki
Każda strona potwierdzenia zawiera identyfikator transakcji, znacznik czasu generacji oraz skrócony hash, co wystarcza, aby specjalista ds. zgodności mógł przeprowadzić wyrywkową kontrolę integralności bez konieczności uzyskiwania dostępu do bazy danych.
Przypadki skrajne
Anulacje i zwroty. Nieudana lub anulowana transakcja nadal wymaga dokumentu. Wygeneruj oddzielny plik PDF dla zdarzenia odwrócenia, odwołując się do oryginalnego identyfikatora transakcji i skrótu paragonu. Potwierdzenie anulowania stanowi samodzielny dokument opisujący zaistniałą sytuację; nie zastępuje ono ani nie modyfikuje oryginału.
Transakcje wielowalutowe. Szablon HTML musi poprawnie obsługiwać umieszczanie symboli walut, separatorów dziesiętnych oraz ujawnianie kursów wymiany zgodnie z ustawieniami regionalnymi. Większość tego zadania realizują ciągi formatujące w języku C#: {amount.ToString("F2", CultureInfo.GetCultureInfo("de-DE"))} dla niemieckich konwencji dziesiętnych, wyraźne umieszczanie symboli dla walut takich jak JPY, które nie używają miejsc po przecinku. Kurs wymiany walut i jego data powinny pojawić się jako osobna pozycja, a nie wynikać z kwot.
Znaczniki regulacyjne. Niektóre jurysdykcje wymagają umieszczenia określonego tekstu na niektórych typach dokumentów, np. "NIE JEST TO FAKTURA PODATKOWA", "NIEOFICJALNA KOPIA" lub sformułowania dotyczące ujawniania informacji specyficzne dla danej jurysdykcji. Są one czysto obsługiwane jako nakładka HTML w szablonie lub jako stylizowany pasek nagłówka, bez modyfikowania podstawowych danych transakcji.
Dlaczego ma to znaczenie poza kwestią zgodności
| Interesariusze | Co otrzymują |
|---|---|
| Zgodność z przepisami / Kwestie prawne | Niezmienne, zaszyfrowane potwierdzenia, które w ciągu kilku minut spełniają wymagania audytowe, bez ponownego wyszukiwania, bez odtwarzania, bez wyjaśniania, dlaczego aktualny wpis w bazie danych różni się od tego, co widział klient |
| Obsługa klienta | Dokładny dokument, który klient otrzymał w momencie transakcji, dzięki czemu rozstrzyganie sporów opiera się na faktach, a nie na interpretacji |
| Klienci | Profesjonalny, opatrzony logo firmy paragon w skrzynce odbiorczej w ciągu kilku sekund od każdej transakcji, ten sam dokument, który firma przechowuje w swoich aktach |
| Inżynieria | Jeden szablon HTML do utrzymania, renderowany w trakcie procesu bez zależności od usług zewnętrznych, bez konieczności monitorowania umowy API i bez konieczności śledzenia rozliczeń za poszczególne dokumenty |
| Finanse / Księgowość | Wyjście archiwalne w formacie PDF/A do długoterminowego przechowywania, zgodne z polityką przechowywania dokumentów i spełniające wymagania dotyczące prowadzenia dokumentacji finansowej bez konieczności stosowania oddzielnego procesu archiwizacji |
Zakończenie
Różnica między "przechowujemy dane transakcji" a "posiadamy rejestr podlegający audytowi" jest mniejsza niż się wydaje — to tylko jeden dodatkowy etap przetwarzania w procesie zatwierdzania transakcji. Ten krok pozwala uzyskać dokument o pochodzeniu, którego sam wiersz w bazie danych nie jest w stanie zapewnić: sygnaturę czasową, której nie można cofnąć, skrót wykrywający manipulacje oraz fizyczny artefakt, który wygląda tak samo niezależnie od tego, czy otwiera go klient, czy żąda go audytor.
IronPDF zapewnia zespołom .NET pełną kontrolę nad tym etapem, od renderowania potwierdzenia HTML po umieszczanie stempla w stopce, haszowanie danych wyjściowych i przesyłanie strumieniowe do niezmiennej pamięci masowej — wszystko to za pomocą jednej biblioteki dostępnej na stronie ironpdf.com. Jeśli tworzysz lub wzmacniasz potok transakcyjny, rozpocznij bezpłatny 30-dniowy okres próbny i sprawdź, czy generowane potwierdzenia są zgodne z Twoimi wymaganiami, zanim wprowadzisz rozwiązanie do produkcji.




