Reçus et relevés de transactions au format PDF en C# pour les applications FinTech
Un problème de conformité, pas seulement un problème de mise en forme
La plupart des applications FinTech stockent les données de transaction dans une base de données relationnelle et génèrent des reçus à la demande : lorsqu'un client en fait la demande, l'application interroge à nouveau l'enregistrement et affiche une vue. Cette approche pose un problème fondamental : le PDF ou " reçu " généré reflète l'état actuel des données, et non ce que le client a vu au moment où la transaction a été finalisée.
Les enregistrements de la base de données peuvent être corrigés, modifiés ou mis à jour dans le cadre des processus opérationnels habituels. Un reçu réinterrogé n'est pas un document historique, mais un instantané actuel comportant un horodatage passé. Lorsqu'un prestataire de paiement est confronté à un rejet de débit, lorsque l'équipe de conformité d'une néobanque répond à un organisme de réglementation, lorsque le journal d'audit d'une plateforme de prêt fait l'objet d'une assignation à comparaître, ou lorsqu'une crypto-bourse doit démontrer l'intégrité des documents à un contrôleur KYC, c'est un document qui est nécessaire, et non une requête.
Ce document est un document PDF enregistré au moment du règlement, haché pour la détection des altérations et écrit dans un stockage immuable. Cela garantit que les documents PDF existants resteront valides pendant des années. Que vous traitiez des rapports financiers ou d'un simple premier PDF, la génération de documents doit être irréprochable.
Les obligations de conservation des données imposées par les normes PCI-DSS, SOX et AML n'exigent pas spécifiquement que les documents PDF soient générés par programmation, mais elles exigent des enregistrements vérifiables et audités. Un fichier PDF rendu, haché et horodaté répond à cette exigence d'une manière qu'une simple ligne de base de données ne peut pas offrir. Plus loin dans cet article, nous examinerons un exemple IronPDF illustrant comment ce processus peut fonctionner lors de la création d'un nouveau document PDF.
La solution en bref : convertir du contenu HTML en PDF en C
La bibliothèque IronPDF d'Iron Software génère un reçu PDF au moment même où une transaction est finalisée, de manière synchrone, dans le cadre du pipeline de transaction. Vous pouvez installer IronPDF via le gestionnaire de paquets NuGet ou la console du gestionnaire de paquets dans Visual Studio. À l'aide de la CLI .NET, il suffit d'exécuter la commande install package IronPDF.
Le reçu est généré à partir d'un modèle HTML ou d'un fichier HTML, estampillé d'un identifiant de transaction et d'un horodatage, haché, puis écrit dans un stockage immuable. Ce document devient la version finale. Il n'y a pas de définition SSRS à gérer, pas d'API de document tierce à appeler, ni de navigateur headless en sidecar. IronPDF s'exécute en cours de traitement en tant que bibliothèque NuGet pour les tâches PDF.
Principaux avantages de l'automatisation des flux de travail documentaires :
-
Préserver la mise en forme sur toutes les tailles de page.
-
Prise en charge des signatures numériques pour garantir l'intégrité des documents.
-
Capacité à créer des objets PDF à partir d'une page web ou d'une chaîne HTML.
- Les logos et l'identité visuelle des clients d'Iron Software peuvent être facilement intégrés
Fonctionnement d'un pipeline de conversion des transactions en reçus
Le scénario idéal
Un paiement aboutit et l'enregistrement de la transaction est écrit dans la base de données. Le même gestionnaire — avant de renvoyer une réponse — remplit un modèle de reçu de contenu HTML avec les détails de la transaction : ID, horodatage UTC, montant et devise, identifiants de l'expéditeur et du destinataire, ventilation des frais et contenu dynamique.
Le rendu new ChromePdfRenderer (plus précisément var renderer = new ChromePdfRenderer();) convertit le contenu web au format PDF. Le tableau d'octets PDF obtenu est immédiatement haché à l'aide de 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)
Exemple de document PDF généré
Exemple de sortie PDF d'IronPDF Le nouveau document PDF est ensuite stocké dans un espace de stockage immuable. Que ce soit votre premier PDF ou que vous fusionniez des PDF existants, le processus reste le même. Une copie est affichée dans un lecteur PDF pour le client. Lors d'une démonstration du produit Iron Software, l'équipe de démonstration met souvent en avant le fait que quelques lignes de code suffisent pour gérer des rapports dynamiques et des tâches telles que la conversion HTML vers PDF ou la génération de fichiers PDF.
Pour faciliter l'interface utilisateur et l'expérience utilisateur, vous pouvez voir une clé dans un cercle bleu, une clé grise dans un cercle ou une clé bleue dans un cercle dans le tableau de bord pour indiquer un fichier sécurisé et haché. Une icône en forme de caret vers la droite associée au lien de téléchargement aide les utilisateurs à naviguer.
Un en-tête ou un pied de page estampillé renforce la provenance du document sur chaque page :
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>"
};
Exemple de pied de page
Exemple de pied de page Chaque page du reçu comporte l'identifiant de la transaction, l'horodatage de génération et un hachage tronqué, ce qui suffit à un responsable de la conformité pour vérifier l'intégrité par sondage sans avoir besoin d'accéder à la base de données.
Les cas limites
Annulations et remboursements. Une transaction échouée ou annulée nécessite tout de même un document. Générez un PDF distinct pour l'événement d'annulation, en faisant référence à l'ID de transaction d'origine et au hachage du reçu. Le reçu de contrepartie constitue un document distinct qui rend compte de ce qui s'est passé ; il ne remplace ni ne modifie l'original.
Transactions multidevises. Le modèle HTML doit gérer correctement le placement des symboles de devise, les séparateurs décimaux et l'affichage des taux de change en fonction des paramètres régionaux. Les chaînes de format C# gèrent la plupart de ces aspects : {amount.ToString("F2", CultureInfo.GetCultureInfo("de-DE"))} pour les conventions décimales allemandes, placement explicite des symboles pour les devises comme le JPY qui n'utilisent pas de décimales. Le taux de change et son horodatage doivent apparaître sous forme de ligne distincte, et non être déduits des montants.
Filigranes réglementaires. Certaines juridictions exigent l'ajout d'un texte spécifique sur certains types de documents, tel que " CE N'EST PAS UNE FACTURE FISCALE ", " COPIE NON OFFICIELLE " ou une mention de divulgation propre à la juridiction. Celles-ci sont gérées de manière propre sous forme de superposition HTML dans le modèle ou de bande d'en-tête stylisée, sans modifier les données de transaction sous-jacentes.
Pourquoi cela est-il important au-delà de la simple conformité ?
| Parties prenantes | Ce qu'ils obtiennent |
|---|---|
| Conformité / Juridique | Des reçus immuables et hachés qui répondent aux demandes d'audit en quelques minutes, sans nouvelle requête, sans reconstruction, sans avoir à expliquer pourquoi l'enregistrement actuel de la base de données diffère de ce que le client a vu |
| Service client | Le document exact que le client a reçu au moment de la transaction, ce qui rend le règlement des litiges factuel plutôt qu'interprétatif |
| Clients | Un reçu professionnel et personnalisé dans leur boîte de réception quelques secondes après chaque transaction, le même document que celui conservé dans les dossiers de l'entreprise |
| Ingénierie | Un seul modèle HTML à gérer, rendu en cours de traitement sans dépendance à un service externe, sans contrat API à surveiller et sans facturation par document à suivre |
| Finance / Comptabilité | Sortie d'archivage au format PDF/A pour la conservation à long terme, conforme aux politiques de conservation des documents et répondant aux exigences en matière de tenue des registres financiers sans workflow d'archivage distinct |
Conclusion
La différence entre " nous stockons les données de transaction " et " nous disposons d'un enregistrement vérifiable " est moins grande qu'il n'y paraît : il s'agit simplement d'une étape de rendu ajoutée au flux de validation de la transaction. Cette étape produit un document dont la provenance ne peut être fournie par une simple ligne de base de données : un horodatage qui ne peut être antidaté, un hachage qui détecte toute altération, et un artefact physique qui a le même aspect, que ce soit un client qui l'ouvre ou un auditeur qui le demande.
IronPDF offre aux équipes .NET un contrôle total sur cette étape, du rendu du reçu HTML à l'apposition du cachet en bas de page, en passant par le hachage de la sortie et la transmission vers un stockage immuable, le tout à partir d'une seule bibliothèque disponible sur ironpdf.com. Si vous mettez en place ou renforcez un pipeline de transactions, commencez votre essai gratuit de 30 jours et vérifiez que les reçus générés sont conformes à vos exigences de conformité avant de passer en production.



