Chapter 1. The three levels of reconciliation.
Making a sale is not the same as getting paid. Between the order confirmed on the website and the euro credited to the bank account come capture, clearing, fees, refunds, chargebacks, reserves, and aggregation into payouts. Reconciliation means proving, line by line, that nothing was lost along that chain, in amount or in timing. The requirement is accounting (a true and fair view for the statutory auditors), tax, and operational. When the merchant’s cash flow depends on it, a missing payout has to be caught within days, not months.
Three questions, three reconciliations
- Transaction reconciliation. Does every order have its payment? Matches the e-commerce back office (orders) against PSP transactions (authorized, captured). Catches missing captures, duplicates, and amount mismatches.
- Settlement reconciliation. Is the net payout correct? Matches captured transactions against the PSP settlement report (gross − fees − refunds − chargebacks − reserves), then that report against the transfer actually received at the bank.
- Accounting (bank) reconciliation. Does the general ledger reflect the bank? Matching of statement entries (camt.053, MT940, CFONB120) against the books, and clearing of suspense and transit accounts (471, 511, and 580 in the French chart of accounts).
| Level | Sources compared | Frequency | Typical discrepancies |
|---|---|---|---|
| 1. Transaction | Order back office ↔ PSP transactions | Daily (or even continuous) | Missed capture, duplicate, changed amount |
| 2. Settlement | Transactions ↔ settlement report ↔ bank transfer | At each payout | Unexpected fees, chargeback debited, reserve, FX difference |
| 3. Accounting | Bank statement ↔ general ledger | Daily + period-end close | Unmatched entry, aging suspense account |
Chapter 2. PSP settlement reports: rebuilding the net.
The settlement report (also called a payout report) is the central document at level 2. It lists every transaction included in a payout, with a breakdown of the fees deducted from each one. Its net total must match the transfer received at the bank to the cent. Its batch key (batch or payout ID) must appear in the transfer’s narrative. Otherwise, the only way to tie a bank credit to its report is the amount.
- Gross: the amount paid by the customer, signed (negative for refunds and chargebacks).
- PSP commission: the provider’s markup, either rolled into a single rate (blended) or broken out (interchange++).
- Interchange: passed on to the issuer, capped in the EU by the Interchange Fee Regulation (IFR, 2015/751) at 0.2% (debit) and 0.3% (credit) for consumer cards.
- Scheme fees: charges from Visa, Mastercard, and CB (Cartes Bancaires, France’s domestic card scheme), typically 0.02% to 0.15% depending on the network and transaction type.
- Adjustments: chargeback fees, reserves (rolling reserve), corrections, and currency conversion with its rate.
Company Account,Merchant Account,Psp Reference,Merchant Reference,Payment Method,Type,Gross (EUR),Commission (EUR),Scheme Fees (EUR),Interchange (EUR),Net (EUR),Batch
MyCompany,MyCompany_ECOM,QFQTKPMMK7HXWN82,ORD-88412,visa,Settled,49.90,0.11,0.03,0.10,49.66,191
MyCompany,MyCompany_ECOM,LZXCVBNMK2JD93PA,ORD-88413,mc,Settled,120.00,0.14,0.05,0.24,119.57,191
MyCompany,MyCompany_ECOM,PPWOEIRUT5GH28QZ,ORD-88301,visa,Refunded,-35.00,0.05,0.00,0.00,-35.05,191
MyCompany,MyCompany_ECOM,MMBBCCDDEE11FF22,ORD-87990,mc,Chargeback,-29.90,15.00,0.00,0.00,-44.90,191
MyCompany,MyCompany_ECOM,,,,Fee,0.00,49.00,0.00,0.00,-49.00,191
Batch 191 net: 49.66 + 119.57 - 35.05 - 44.90 - 49.00 = 40.28 EUR
-> to match against the "ADYEN NV PAYOUT-191" transfer received at the bank.The Refunded line refunds the customer and does not return the commission. The Chargeback line reverses the amount and adds €15 in dispute fees. The Fee line carries monthly fees billed as they accrue, not tied to any specific transaction. The batch net, €40.28, is the only amount that will show up at the bank. Every other line exists only in the report.
| Model | Breakdown | Total cost | Reconciliation |
|---|---|---|---|
| Blended 1.2% | Single all-in rate: €1.20 | 1,20 € | Simple but opaque: no way to audit interchange and scheme fees |
| Interchange++ | Interchange €0.20 + scheme fees ≈ €0.08 + PSP markup €0.25 | ≈ 0,53 € | Three lines to match per transaction, but costs you can audit and negotiate |
A few pitfalls come up again and again. Multi-currency payouts require one report per settlement currency, and the conversion rate has to be archived with the report itself so the FX difference can still be explained later. Refunds are sometimes charged to a batch that comes after the sale, and reserves are released months later. Finally, a PSP may change its report format: treat that as an interface change, with regression testing. A format that shifts breaks matching.
Chapter 3. MT940 / MT942: the legacy SWIFT statement.
The MT940 (Customer Statement Message) is the end-of-day account statement of the SWIFT FIN world, category 9. Designed in the 1980s, it is still everywhere in multi-bank treasury departments, where it serves as the lowest common denominator between banks. The MT942 is its intraday counterpart (provisional entries since the last statement), and the MT941 is a simple balance report. The format relies on colon-delimited tags in plain text, with lines capped at 65 characters, a constraint that limits everything the bank can put in it.
| Tag | Name | Content |
|---|---|---|
| :20: | Transaction Reference | Message reference, unique per statement |
| :25: | Account Identification | Account concerned (BIC/IBAN or bank code, branch code, and account number) |
| :28C: | Statement Number | Statement number / sequence number |
| :60F: / :60M: | Opening Balance | Opening balance (F = final, M = intermediate): debit/credit mark, date, currency, amount |
| :61: | Statement Line | One line per transaction; the core of the format |
| :86: | Information to Account Owner | Free-text narrative, unstructured, up to 6 lines of 65 characters |
| :62F: / :62M: | Closing Balance | Closing booked balance |
| :64: / :65: | Available / Forward Balance | Available balance, forward balances |
Breaking down field :61:
- Value date: 6 digits, YYMMDD (e.g.,
260710). - Entry date: 4 digits, MMDD, optional (e.g.,
0710). - Debit/credit mark:
Ccredit,Ddebit, withRC/RDfor reversals. - Amount: decimal comma required (e.g.,
15230,50). - Transaction type: one letter (
Nstandard,SSWIFT,Ffirst advice) + a 3-letter code:TRFtransfer,CHGcharges,DDTdirect debit,CHKcheck,MSCmiscellaneous, givingNTRF,NCHG,NDDT, and so on. - Reference for the account owner: 16 characters max (
PAYOUT-4521, orNONREF). - `//` Bank reference, then, on the next line, supplementary details (34 characters).
:20:STMT-20260710-0191
:25:30004 00012 00012345678
:28C:191/1
:60F:C260709EUR125430,17
:61:2607100710C15230,50NTRFPAYOUT-4521//BQE889123
ADYEN NV BATCH 191
:86:SEPA CT RECEIVED ADYEN NV
REF PAYOUT-4521 CARD SALES JUL 09
:61:2607100710D1250,00NCHGNONREF//BQE889124
:86:CARD PROCESSING FEES JUNE 2026
:62F:C260710EUR139410,67
:64:C260710EUR139410,67
Check: 125430.17 + 15230.50 - 1250.00 = 139410.67 -> balances agree.
:61: line no. 1: value date July 10, credit, EUR 15,230.50, transfer (NTRF),
customer reference PAYOUT-4521 (matching key), bank reference BQE889123.?20, ?21, etc.), others free text. Every MT940 parser is therefore a patchwork of bank-specific rules that breaks easily: a simple change of wording on the bank’s side is enough to throw it off. That is why camt.053 exists.The MT942 shares the :61: line but adds a timestamp (:13D:) and the :90D:/:90C: counters, the number and total of debits and credits. It is used to take a cash position at 2 p.m. and decide the day’s balancing transfers, or to spot an expected transfer as soon as it arrives. MT942 entries are provisional. Only the end-of-day MT940 is authoritative for accounting reconciliation, and only it can serve as the supporting record for matching.
Chapter 4. camt.052 / 053 / 054: the ISO 20022 statement.
ISO 20022’s camt (cash management) family is gradually replacing the MT9xx messages. camt.052 covers intraday reporting (≈ MT942), camt.053 the end-of-day statement (≈ MT940), and camt.054 debit/credit notifications and the detail behind aggregated batches. The gain is more than cosmetic: structured XML, end-to-end references, standardized transaction codes, and native multi-currency support, all of which an engine can use without bank-specific rules.
GrpHdr: message identifier and timestamp.Stmt(053) /Rpt(052) /Ntfctn(054): one block per account.Bal: typed balances, withOPBDopening booked,CLBDclosing booked,ITBDinterim,CLAVclosing available,FWAVforward available.Ntry: an entry (amount,CRDT/DBITindicator,BOOK/PDNGstatus, BTC code).NtryDtls→TxDtls: transaction details, namely references (EndToEndId!), counterparties, original amounts and fees, and the return reason (RtrInf).
Bank Transaction Codes: standardized meaning
Every entry carries a three-level BTC code drawn from an external ISO code list: Domain / Family / Sub-family. The MT940 offered the catch-all NTRF, which lumped all transfers together. camt natively distinguishes an incoming SEPA credit transfer from a returned direct debit, and the matching engine routes the entry without having to read its narrative.
| Area | Card type | Sub-family | Meaning |
|---|---|---|---|
| PMNT | RCDT | ESCT | SEPA credit transfer received |
| PMNT | ICDT | ESCT | SEPA credit transfer sent |
| PMNT | IDDT | ESDD | SEPA direct debit batch (creditor side) |
| PMNT | RDDT | ESDD | SEPA direct debit paid (debtor side) |
| PMNT | IDDT | UPDD | Direct debit returned |
| PMNT | CCRD | CWDL | Cash withdrawal by card |
<BkToCstmrStmt>
<GrpHdr>
<MsgId>CAMT053-20260710-0001</MsgId>
<CreDtTm>2026-07-11T06:00:00+02:00</CreDtTm>
</GrpHdr>
<Stmt>
<Id>STMT-2026-0191</Id>
<Acct><Id><IBAN>FR7630004000120001234567890</IBAN></Id></Acct>
<Bal><!-- opening booked balance -->
<Tp><CdOrPrtry><Cd>OPBD</Cd></CdOrPrtry></Tp>
<Amt Ccy="EUR">125430.17</Amt>
<CdtDbtInd>CRDT</CdtDbtInd>
<Dt><Dt>2026-07-09</Dt></Dt>
</Bal>
<Ntry><!-- the Adyen payout -->
<Amt Ccy="EUR">15230.50</Amt>
<CdtDbtInd>CRDT</CdtDbtInd>
<Sts><Cd>BOOK</Cd></Sts>
<ValDt><Dt>2026-07-10</Dt></ValDt>
<BkTxCd><Domn>
<Cd>PMNT</Cd>
<Fmly><Cd>RCDT</Cd><SubFmlyCd>ESCT</SubFmlyCd></Fmly>
</Domn></BkTxCd>
<NtryDtls><TxDtls>
<Refs><EndToEndId>PAYOUT-4521</EndToEndId></Refs>
<RltdPties><Dbtr><Pty><Nm>ADYEN NV</Nm></Pty></Dbtr></RltdPties>
<RmtInf><Ustrd>CARD SALES JUL 09 BATCH 191</Ustrd></RmtInf>
</TxDtls></NtryDtls>
</Ntry>
</Stmt>
</BkToCstmrStmt>| Criterion | MT940 | camt.053 |
|---|---|---|
| Structure | Text tags, free-text :86: | Fully structured XML (XSD schema) |
| Transaction identification | 3-letter code (NTRF…) | 3-level standardized BTC |
| References | 16 characters, often truncated | EndToEndId, 35 characters, carried end to end |
| Counterparties | In the free text | Dedicated fields (name, IBAN, BIC) |
| File size | Compact | 5 to 10× larger (negligible today) |
| Parsing | Bank-specific rules, fragile | Generic, robust |
RtrRsnInf reason code. The 053 + 054 pair is the target setup for automated reconciliation: the first carries the end-of-day balance, the second the granularity.Since November 2025, MT/MX coexistence has ended on SWIFT for interbank payment instructions. MT103 and MT202 have been replaced by pacs.008 and pacs.009 under CBPR+. MT940/942 statements are still tolerated between banks and corporates, with no firm retirement date; each bank sets its own migration timeline. But banks are actively pushing camt, and any new integration should default to it rather than build another rule-based parser.
Chapter 5. CFONB120: France’s account statement format.
CFONB120 is France’s legacy account statement format. It was standardized by the Comité Français d’Organisation et de Normalisation Bancaires, which now operates as a committee within the FBF (French Banking Federation). The format uses fixed-width 120-character records, with no tags. Each field is identified by its position and length, never by a name. Despite its age, it is still widely delivered over EBICS to French treasury departments and accounting software, many of which accept no other input format.
- Record 01: the account’s previous balance (opening balance).
- Record 04: a transaction (one entry), the core of the file.
- Record 05: additional information on the preceding transaction (extra narrative lines); several 05 records can follow a 04.
- Record 07: new balance (closing balance).
| Market position | Length | Scope |
|---|---|---|
| 1–2 | 2 | Record code (04) |
| 3–7 | 5 | Bank code |
| 12–16 | 5 | Branch code |
| 17–19 | 3 | ISO currency code |
| 20 | 1 | Number of decimal places in the amount |
| 22–32 | 11 | Account number |
| 33–34 | 2 | Interbank transaction code (AFB code list: transfer, direct debit, card, check…) |
| 35–40 | 6 | Booking date (DDMMYY) |
| 43–48 | 6 | Value date (DDMMYY) |
| 49–79 | 31 | Meaning |
| 91–104 | 14 | Amount; last character = digit and sign combined |
| 105–120 | 16 | Reference field |
The format’s signature is the amount signed with a letter (overpunch, inherited from punched cards and EBCDIC), a classic trap for early hand-written parsers. The last character of the amount encodes both the last digit and the sign: { = 0 credit, A–I = 1 to 9 credit; } = 0 debit, J–R = 1 to 9 debit. With 2 decimal places, 0000000152305{ is therefore +15,230.50, and 0000000012500} is −1,250.00. It all comes down to the last letter.
01 30004 00012 EUR 2 00012345678 090726 0000001254301G
04 30004 00012 EUR 2 00012345678 05 100726 100726 SEPA CT ADYEN NV 0000000152305{ PAYOUT-4521
05 30004 00012 EUR 2 00012345678 05 100726 CARD SALES JUL 09 BATCH 191
04 30004 00012 EUR 2 00012345678 18 100726 100726 CARD PROCESSING FEES 0000000012500} FEES-JUNE
07 30004 00012 EUR 2 00012345678 100726 0000001394106G
Reading:
- 01: previous balance 1254301G -> G = 7 credit -> +125,430.17 EUR
- 04: credit transaction 152305{ -> { = 0 credit -> +15,230.50 EUR
- 05: extra narrative attached to the preceding 04
- 04: debit transaction 12500} -> } = 0 debit -> -1,250.00 EUR
- 07: new balance +139,410.67 EUR (consistent with 01 + transactions)Chapter 6. pain.001 / pain.008 and the channels: EBICS, SwiftNet, API.
The pain (payment initiation) family covers the outbound leg: pain.001 to initiate credit transfers (SCT), pain.008 to collect direct debits (SDD), and pain.002 for status reports (accepted, or rejected with a reason code). The loop closes when the EndToEndId set in the pain.001 comes back unchanged in the camt.053 of both the beneficiary and the originator. That continuity is the golden key of reconciliation: it spares you from inventing matches by amount and date.
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09">
<CstmrCdtTrfInitn>
<GrpHdr>
<MsgId>PAIN001-20260711-0001</MsgId>
<CreDtTm>2026-07-11T09:30:00+02:00</CreDtTm>
<NbOfTxs>1</NbOfTxs>
<CtrlSum>12435.20</CtrlSum>
<InitgPty><Nm>MYCOMPANY SAS</Nm></InitgPty>
</GrpHdr>
<PmtInf>
<PmtInfId>BATCH-SUP-20260711</PmtInfId>
<PmtMtd>TRF</PmtMtd>
<BtchBookg>false</BtchBookg><!-- false = one statement entry per transfer -->
<ReqdExctnDt><Dt>2026-07-13</Dt></ReqdExctnDt>
<Dbtr><Nm>MYCOMPANY SAS</Nm></Dbtr>
<DbtrAcct><Id><IBAN>FR7630004000120001234567890</IBAN></Id></DbtrAcct>
<DbtrAgt><FinInstnId><BICFI>BNPAFRPPXXX</BICFI></FinInstnId></DbtrAgt>
<CdtTrfTxInf>
<PmtId>
<InstrId>INSTR-2214</InstrId>
<EndToEndId>INV-2026-2214</EndToEndId><!-- carried through to the camt.053 -->
</PmtId>
<Amt><InstdAmt Ccy="EUR">12435.20</InstdAmt></Amt>
<Cdtr><Nm>SUPPLIER SARL</Nm></Cdtr>
<CdtrAcct><Id><IBAN>FR7610096000301234567890189</IBAN></Id></CdtrAcct>
<RmtInf><Ustrd>INVOICE 2026-2214</Ustrd></RmtInf>
</CdtTrfTxInf>
</PmtInf>
</CstmrCdtTrfInitn>
</Document>BtchBookgfalse = one bank entry per transfer (granular reconciliation); true = one aggregated entry per batch (compact statement, but the detail has to be retrieved from camt.054).- pain.008 adds the mandate mechanics:
MndtId(the UMR, or unique mandate reference), signature date,FRST/RCUR/OOFF/FNALsequence, and the creditor identified by its SCI (SEPA creditor identifier). Returns (R-transactions) come back in pain.002 or camt.054 with standardized reason codes (AM04insufficient funds,MD01no mandate…). - The
EndToEndId(35 characters) survives the entire interbank chain: put a business key in it (invoice number, payout ID), never free text.
Three channels for exchanging these files
| Criterion | EBICS | SwiftNet (SCORE) | API |
|---|---|---|---|
| Region | France, Germany, Switzerland, Austria | Global, multi-bank | Depends on the bank / PSD2: EU |
| Indicative cost | Low (a few tens of euros a month per bank) | High (SWIFT membership, BIC, service bureau) | Low to medium (premium APIs are paid) |
| Formats | Files: pain, camt, CFONB | FIN (MT) + FileAct (any file) | Real-time JSON + files |
| Security | Personal signatures; TS profile with a signature separate from the order | SWIFT PKI, non-repudiation | OAuth2, eIDAS certificates (QWAC/QSeal) |
| Typical use case | French multi-bank SME or mid-cap | International group, centralized treasury | Real-time balance, instant credit notification |
FDL/FUL) with BTFs (Business Transaction Formats), which explicitly describe the format, version, and purpose of the file exchanged. Migration has been universal in France since 2025. PSD2 APIs (AIS), meanwhile, remain limited for treasury: their scope is restricted to payment accounts, with 4 queries a day when the customer is not present. Beyond that, banks sell premium APIs outside the regulatory framework, at their own prices.Chapter 7. Matching engine, exceptions, and KPIs.
The reconciliation engine applies a cascade of rules, from the strictest to the most lenient. Each unmatched bank line drops down one level, with manual matching as the last tier. The goal is not to match everything automatically. It is to show people only qualified exceptions, each with a suggested diagnosis and the first lead to check. Everything else should go through on its own.
- Exact 1-to-1: identical reference (
EndToEndId, payout ID) + exact amount + date window. Should catch 85–95% of lines. - N-to-1: sum of N transactions in the PSP report = 1 bank transfer (aggregated payout); key = batch ID + total to the cent.
- With tolerance: amount within ± X (bank charges deducted as they occur, FX conversion differences). The resulting difference must be booked, not ignored.
- Fuzzy: narrative similarity (originator, purpose), date proximity; reserve it for small amounts, with sample-based review.
- Learning: every manual match becomes a candidate rule (same counterparty, same narrative structure), validated before it goes into production.
| Exception | Typical cause | Processing |
|---|---|---|
| Expected payout missing | Bank holiday, PSP payout threshold not reached, frozen account | Automatic alert at D+1; escalate to the PSP at D+2 |
| Transfer with no report | Settlement report late, or format change | Suspense account + follow-up; never match “on the aggregate” |
| Difference of a few cents | FX rounding, per-transaction fees | Tolerance rule with a dedicated variance entry |
| Chargeback / return debited | Card dispute, SDD R-transaction (AM04, MD01…) | Route to the disputes team with the original reference |
| Reserve withheld or released | PSP’s contractual rolling reserve | Dedicated “PSP reserves” account, with release schedule tracking |
| Duplicate entry | File replayed, bank incident | Automatic block: same reference + same amount + same date |
| KPI | Definition | Typical target |
|---|---|---|
| Auto-match rate | Lines matched without intervention / total | > 95% by volume, > 98% by amount |
| Exceptions open > 5 days | Aging of the discrepancy backlog | ≈ 0; any exception > 10 days escalated |
| Suspense account balance | Unapplied cash | Trending to zero at each close |
| Time to certify cash | Days from month-end until bank = books | ≤ D+3 |
| Differences absorbed by tolerance | Sum of small differences written off to expense | Tracked monthly; drift signals a fee or FX problem |
EndToEndId values in every pain.001, turn on camt.053 + camt.054 instead of MT940, and ban refunds issued outside the system. A mediocre engine with clean references beats a brilliant engine on data that says nothing.One architectural point remains. Keep raw files (PSP reports, statements) immutable and archived, for the audit trail and for tax audits of computerized accounting records. All reprocessing lives in the engine and can be replayed identically on the original files. Reconciliation runs daily, not as a month-end job. The close is just a snapshot of it.