Rapprochement des paiements et des rapports PSP
TL; DR
Le rapprochement est la couture automatisée quotidienne de votre ledger et des événements (auth/capture/refund/payout) avec les rapports PSP/acquéreurs/banques. La clé du succès : un modèle de données unique, des clés d'appariement déterministes, une idempotence stricte, des tolérances par les montants/FX/temps, une file DLQ pour les cas controversés et des corrections automobiles de pleybooks. KPI: Recon Mismatch Rate↓, Aging of Unreconciled↓, Auto-match%↑.
1) Pourquoi et ce que nous vérifions
Objectifs : confirmation des recettes et des commissions, détection des prises/pertes, mise en place correcte des jours et des devises, contrôle de la refund-to-source, conformité à l'audit/régulateur.
Objets de rapprochement :- Deposits: `auth → capture → settle`
- Refunds : full/partial, statuts et montants
- Payouts/Withdrawals : paiements sortants par méthode
- Fees & Adjustments : commissions PSP, schémas d'interchange, corrections
- Chargebacks/Disputations : hors de votre initiative
- FX/Conversions : cours, spreads, moment de fixation
2) Sources de données
Événements internes (les vôtres) : bus d'événements/Kafka, 'payments _ flat', 'refunds', 'payouts', votre Ledger.
PSP Reports (SFTP/API/webhook dump):- Transactions (relevés opérationnels)
- Settlements/Batches (ventilation des inscriptions T + N)
- Fees/États (commissions, ajustements)
- Chargebacks/Disputes
- Registres payants/OCT/RTP/SEPA
- États de la Banque : MT940/CSV/ISO20022 CAMT, lifting des inscriptions.
3) Clés de mappage (clés de matching)
Arbre de clé prioritaire (précision décroissante) :1. provider_txid ↔ provider_txid (ID PSP unique)
2. idempotency_key/ merchant_reference (si les PSP sont stables)
3. (amount, currency, timestamp_bucket, last4/bin, auth_code)
4. Couche fuzzy : ± de tolérances par somme/temps, BIN/issuer country, status family
Recommandations :- Gardez les deux : 'payment _ id' et 'provider _ txid'.
- Pour partial/refund : Ajoutez 'sequence _ index' ou 'refund _ line _ id'.
- Для payouts — `payout_batch_id + line_id`.
- Pour FX, "exec _ id' (conversion) et la source du cours.
4) Modèle de données (couche normalisée)
json
{
"source": "INTERNAL PSP_TX PSP_SETTLEMENT BANK",
"provider": "Acquirer_A",
"payment_id": "pay_123",
"provider_txid": "psp_tx_789",
"kind": "AUTH CAPTURE REFUND PAYOUT FEE SETTLEMENT CHARGEBACK",
"sequence": 0,
"amount": 100. 00,
"currency": "EUR",
"fee_amount": 1. 20,
"fx_rate": 1. 0000,
"fx_src": "PSP ECB BANK",
"status": "APPROVED CAPTURED SUCCESS FAILED SETTLED",
"event_ts": "2025-11-03T12:00:00Z",
"settlement_date": "2025-11-05",
"account": "PSP_MERCHANT_CARD_A",
"matching_keys": {
"provider_txid": "psp_tx_789",
"merchant_ref": "mr_456",
"idem_key": "idem_abc"
},
"hash_row": "sha256(...)"
}
5) Processus de rapprochement (ETL/orchestration)
1. Ingest : nous récupérons les rapports PSP (SFTP/API), nous validons le schéma/les signatures, nous enregistrons dans 'raw'.
2. Normalize : mappim de champs dans un format unifié (currency ISO, decimals, timzone UTC).
3. Match : algorithme de mappage par arbre de clés avec tolérances.
4. Post-match : nous formons le diff (divergences) et les entrées de journal pour le ledger/corrections.
5. Settle : coudre 'PSP _ SETTLEMENT ↔ BANK' (crédités sur le compte), étaler par jour/trampoline.
6. Rapport : dashboard, alertes ; Controversé dans DLQ sur l'analyse manuelle/le jeu automatique.
Idempotence : pour chaque fichier/page, "ingest _ id'. Le redémarrage ne modifie pas le résultat.
6) Tolérances et règles
Temps : '± 15 min' pour les transactions, '± 1 dn'pour le settlement.
Montant : '≤ 0. 01 'monnaie de base ou' ≤ 10 bps 'pour les différences FX/fee.
FX : nous admettons une divergence avec la banque si la source du cours est différente ; on fixe 'fx _ src'.
Partial/Multiple : la somme des lignes partial/refund doit être égale au solde intérieur.
7) Traitement des écarts (diff taxonomy)
8) Ledger & Accounting (câblage)
Capture: `DR Accounts Receivable / CR Revenue` и `DR Cash (upon settle) / CR Accounts Receivable`
Fee: `DR Fees / CR Cash or Payable`
Refund : câblage inverse proportionnel à partial
Chargeback : comptes séparés et réserve pour les litiges
FX reval : réévaluation quotidienne du solde AR/cache au taux 'fx _ src _ policy'
9) KPI et objectifs
Auto-match % = chaînes mappées automatiquement/toutes les lignes (cible ≥ 95 %)
Recon Mismatch Rate = diff-chaînes/toutes les chaînes (≤ 1-2 %)
Aging of Unreconciled : p50/p95 jours en DLQ (p95 ≤ 3 dn)
Temps d'arrêt : proportion de trampolines cousues avec la banque D-day (≥ 99 %)
Fee Accuracy : divergences de commissions sur les fournisseurs (≤ 0. 1 % du chiffre d'affaires)
Duplicate/Orphan Incidents : vise 0
10) tranches SQL
10. 1 Comparaison de base par provider_txid
sql
WITH i AS (
SELECT provider, provider_txid, kind, amount, currency, event_ts
FROM internal_norm
),
p AS (
SELECT provider, provider_txid, kind, amount, currency, event_ts
FROM psp_norm
)
SELECT
COALESCE(i. provider_txid, p. provider_txid) AS txid,
COALESCE(i. provider, p. provider) AS provider,
i.kind AS kind_internal, p. kind AS kind_psp,
i.amount AS amount_internal, p. amount AS amount_psp,
i.currency, p. currency,
CASE
WHEN i.provider_txid IS NULL THEN 'MISSING_INTERNAL'
WHEN p. provider_txid IS NULL THEN 'MISSING_PSP'
WHEN ABS(i. amount - p. amount) > 0. 01 THEN 'AMOUNT_MISMATCH'
ELSE 'MATCHED'
END AS recon_status
FROM i
FULL OUTER JOIN p USING (provider, provider_txid);
10. 2 Settlement ↔ Bank
sql
SELECT s. settlement_date, s. batch_id, s. currency,
s. amount_settled, b. amount_bank,
(b. amount_bank - s. amount_settled) AS diff
FROM psp_settlements s
LEFT JOIN bank_statements b
ON b. value_date = s. settlement_date
AND b. currency = s. currency
AND ABS(b. amount_bank - s. amount_settled) <= 0. 5;
10. 3 Aging DLQ
sql
SELECT diff_type,
COUNT() AS cnt,
PERCENTILE_CONT(0. 5) WITHIN GROUP (ORDER BY AGE(NOW(), created_at)) AS p50_age,
PERCENTILE_CONT(0. 95) WITHIN GROUP (ORDER BY AGE(NOW(), created_at)) AS p95_age
FROM recon_dlq
GROUP BY diff_type
ORDER BY cnt DESC;
11) Caractéristiques par rails/mallettes
Cartes : différences entre 'auth' et 'capture', corrections tardives 'settlement', interchange/diagramme fee - lignes séparées.
A2A/Open Banking/RTP : confirmations instantanées, mais « reversal » possible ; vérifiez 'payout' et les retours.
Portefeuilles : souvent parfait 'provider _ txid', rapide 'refund' ; regardez les lignes fee.
Bons : il n'y a pas de refund symétrique - reflétez correctement dans la politique et les rapports.
Crypto : Il-chain hash ↔ provider_txid ; N ; la comptabilisation des commissions du réseau et des éventuels rebonds ; le cours est au moment de la conversion.
12) Playbooks opérationnels
Sursaut de MISSING_INTERNAL : vérifier la perte de webhooks/rétroactions, rejouer l'ingestion, activer l'API polling.
AMOUNT_MISMATCH un PSP : comparer les arrondis/TVA/modèle fee, demander un statut correctif.
Settlement n'est pas cousu avec la banque : vérifier la valeur de la date, les commissions de la banque, les retards T + N ; mettre temporairement dans le « Compte Suspense ».
Les REFUND_OVER de masse : arrêt immédiat des auto-refands, vérification de l'idempotence, correction manuelle.
FX_DRIFT : fixer la politique de la source du cours (BCE/PSP/BANK), recalculer la différence P & L.
13) Contrôle et sécurité
Idempotence d'ingestion : 'file _ id + checksum' et journal de téléchargement.
Accès (RBAC) et contrôle à 4 yeux : corrections manuelles/câblage de magazine.
Audit-trail : tous les matchs/difs/corrections sont dans un journal inchangé.
Qualité des données : schémas, champs obligatoires, validation des devises/skale.
14) Dashboard et alertes
Widgets : Auto-match %, taux de Mismatch, Aging DLQ, Settlement Timel...., Fee Accuracy, top PSP sur les diphères, carte de type diff.
Alert :- 'Auto-match % <90 % 'par fournisseur/jour → P1
- ` Aging p95> 3 дн ` → P2
- `AMOUNT_MISMATCH spike` → P1
- 'Bank≠Settlement' par montant/devise → P0
15) Cas de test (UAT/Prod)
1. Téléchargez à nouveau le même fichier → 0 effets secondaires (idempotence).
2. Refands partiels (3 lignes) → somme correspond, match par séquence.
3. Conversion FX : divergence de cap dans le tolerance → match correct.
4. Les doublons provider_txid dans le rapport → dedup et alert.
5. Le webhook de capture manquant → le polling a fermé le gap, le statut est aligné.
6. Settlement batch avec la ligne fee → la répartition correcte sur Revenue/Fee/Net.
16) Erreurs fréquentes et comment éviter
Mélanger les bases de comparaison (compare 'attempt'vs' capture ') → garder la même granularité.
L'absence de 'provider _ txid' dans le journal → perd la précision du match.
Ignorer le décalage temporel → les dates de settlement.
Pas de DLQ/rétrogrades → des divergences « éternelles ».
Les modifications manuelles sans journal → incompatibles avec l'audit.
Des tolérances floues → soit un match à la plume, soit « tout le monde dans la DLQ ».
17) Chèque de vérification de la mise en œuvre
- Système unique de normalisation et manuels PSP/méthodes/comptes
- Arbre des clés de mappage (txid → merchant_ref → fuzzy)
- Tolérances par somme/temps/FX, politique de la source du cours
- Ingestion idempotente, DLQ, retraits, alertes
- Rapprochement des Settlement↔Bank, Politique de compte suspensif
- Dashboard KPI, rapports pour la finance/audit
- Pleybooks et SLA de l'analyse des cas diff
Résumé
Le rapprochement est une discipline d'ingénierie : normalisation, clés fiables, tolérances, matchs automatiques et corrections transparentes. Avec un tel circuit, vous stabiliserez les recettes et les commissions, minimiserez les « trous noirs », accélérerez la fermeture de la période et passerez l'audit sans douleur : Auto- match%↑, Mismatch↓, Aging↓.