Free preview

Reconciliation and Disputes

In one line: reconciliation is the design's best-specified component, and it is the mechanism that makes every other correctness bug in this chapter eventually visible.

Reconciliation

Reconciliation is the process of matching financial records to ensure accuracy and identify discrepancies. The system compares the internal ledger against transaction records from the payment gateway. At the end of each day, the PSP sends a settlement file summarizing the day's transactions... For example, if a merchant's ledger shows three payments totaling $225, the system verifies that the PSP's settlement file matches those payments. Any mismatch is flagged for investigation.

Reconciliation exists because every other mechanism can fail silently

It is tempting to read reconciliation as accounting hygiene. It is the system's last line of correctness defence, and it is the only component that can catch failures nothing else notices.

Walk the failures this chapter has already found and ask what surfaces them:

FailureDetected by
Duplicate consumer application (Lesson 7)Reconciliation — our total exceeds theirs
Dual-write divergence (Lesson 7)Reconciliation — ledger and balance disagree with the file
Lost update on a mutable balance (Lesson 3)Reconciliation — our total is short
Authorization that never captured (Lesson 1)Reconciliation — in our ledger, absent from settlement
A PSP-side bug or an unexpected feeOnly reconciliation

Every one of them is invisible in real time and visible in the daily comparison. That is the argument for the component: not that it is required by accounting convention, but that distributed systems fail in ways their own monitoring cannot see, and comparison against an independent record is the only detection that does not depend on the thing being correct.

Note what makes it independent: the settlement file comes from outside your system, produced by a party with its own records. That is why it can catch bugs your own consistency checks cannot — your checks share your assumptions.

When correctness cannot be guaranteed in the moment, verify it afterwards against a record you did not produce.

The chapter's own worked example is not executable against its schema

"If a merchant's ledger shows three payments totaling $225…"

That requires summing amounts in the ledger. Per Lesson 3, the design's ledger is:

Ledger: transaction_id, transaction_date, token_id, action, description

No amount column. The reconciliation procedure the chapter describes cannot run against the schema the chapter specifies.

This is the concrete cost of the missing double-entry design from Lesson 5, and it is worth noticing how the defects compound rather than sitting independently:

Ledger has no amounts (L3)
  -> balances cannot be derived or verified (L5)
  -> the internal invariant SUM(entries)==0 is unavailable (L5)
  -> reconciliation has nothing to total (L9)
  -> the duplicate credits from at-least-once delivery (L7)
     are never detected by anything

With a proper ledger you get two independent checks: SUM(entries) == 0 continuously, catching your own bugs within seconds, and the settlement comparison daily, catching disagreements with the outside world.

A self-checking invariant is worth more than external comparison because it is available now and it localizes the fault to your own system. Reconciliation is the backstop, not the primary defence.

The response to a mismatch is well judged

If a mismatch is found, the system alerts and logs the discrepancy. The issue could be missing transactions, duplicate entries, timing delays, or data corruption. The system may hold settlements until resolved, trigger reprocessing, or notify the operations team — depending on severity.

Three graded responses, and the grading is right.

Holding settlement is the conservative move and the correct default when the discrepancy affects money owed. Paying out against numbers you do not trust is how a reconciliation problem becomes an unrecoverable one — it is far easier to delay a payout than to claw one back.

Reprocessing suits the case where the cause is known and mechanical — a dropped event, a failed consumer batch. Note this is only safe because the operations are idempotent: reprocessing a payment that did apply must change nothing.

Human escalation is the honest admission that some discrepancies are not automatable. A payment present at the PSP and absent from your ledger could be a lost event, a race, or fraud, and the correct action differs.

The category the list gets right that people often miss is timing delays — a transaction authorized at 23:59 may land in tomorrow's settlement file. That is not a discrepancy, it is a boundary, and a reconciliation system that alerts on it produces noise that trains people to ignore real alerts.

Distinguish "not yet" from "wrong." Most naive reconciliation implementations do not, and drown in false positives.

Disputes and chargebacks

This service manages disputes between customers, merchants, and banks. It handles chargebacks, ensures compliance with financial regulations, and maintains dispute records to prevent fraud.

A chargeback is a completed payment reversing itself months later

This is the component that most distinguishes a payment system from anything else in this module, and the design gives it two sentences.

A chargeback is a customer disputing a charge with their issuer — not with the merchant. The issuer forcibly reverses the transaction, and the merchant loses both the funds and a fee. The timeline is the striking part:

Day 0:    payment authorized and captured
Day 2:    settled — money in the merchant's account
Day 90:   customer disputes with their bank
Day 92:   funds pulled BACK out of the merchant's account
Day 120:  merchant submits evidence
Day 180:  the issuer decides

A transaction is not final for months. Every other system in this module reaches a terminal state in seconds; here Settled can transition to Disputed a quarter later.

Three design consequences follow, none of which the chapter draws:

Records must be retained and queryable for years, which is the retention constraint from Lesson 2 arriving with a concrete reason. And not just the transaction — the evidence: device fingerprint, IP, shipping confirmation, the customer's authentication at the time. Evidence you did not capture at payment time cannot be reconstructed at dispute time.

The ledger must accept entries against old transactions. A chargeback is a new balanced set referencing a transaction from three months ago — which is exactly the reversal pattern from Lesson 5, and it is why an immutable append-only ledger is not optional. Editing the original would destroy the evidence you need to contest the dispute.

Chargeback rate is an operational limit, not just a cost. Card networks impose thresholds — commonly around 1% of transactions — above which a merchant faces penalties or loses the ability to accept cards at all. This is why fraud prevention has real teeth: the cost of fraud is not only the fraudulent transactions, it is the risk of losing card acceptance entirely.

A payment system's state machine does not terminate at "settled", and designing as though it does is what leaves you unable to contest disputes.

MechanismCatchesLatencyIndependent?
SUM(entries) == 0Our own bugs — lost, duplicated, or half-applied writesSecondsNo — internal
Settlement reconciliationDisagreement with the PSP — missing, extra, or differing amountsDaily✅ Yes — external record
Dispute managementCustomer-initiated reversalWeeks to months✅ Yes — issuer-initiated
Fraud detectionMalicious activity before it completesReal time

Four checks at four timescales, and the design's own admission about ordering

Read the table as a defence in depth spanning six orders of magnitude in time — real-time fraud scoring, second-scale invariant checks, daily reconciliation, and a months-long dispute tail.

The property worth naming: each layer catches what the faster one cannot. Fraud detection cannot catch a bug in your consumer. The ledger invariant cannot catch a PSP-side fee you did not expect. Reconciliation cannot catch a customer who genuinely did not receive their goods.

Correctness in a financial system is not one mechanism but a set of independent checks at different timescales, and the slow ones are not redundant — they are the only ones that see certain classes of failure.

This is also the strongest argument for the double-entry design in Lesson 5. Without it, the fastest of the four checks does not exist, and the earliest you learn about a duplicate credit is the next day, from a file produced by someone else.

Telling the merchant what happened

Payment outcomes arrive asynchronously — sometimes seconds later, sometimes after a batch window — so the merchant needs to be told rather than made to poll. That is a webhook, and it is a small distributed system of its own.

Four properties make this safe, and each answers a specific failure.

Sign every payload. The merchant is receiving a claim that money moved, from an endpoint anyone on the internet can POST to. A signature over the body with a shared secret is what makes it trustworthy, and including a timestamp in the signed material is what stops an old valid message being replayed.

Retry with exponential backoff and jitter. Merchant endpoints go down, and a retry storm from every pending webhook at once is what turns their brief outage into a long one.

Deliver at least once, and say so. A network failure after the merchant processed the event but before the acknowledgement is indistinguishable from a real failure, so the same event will sometimes arrive twice. Give every event a stable id and tell integrators to deduplicate on it — the same contract you rely on from the card network.

Dead-letter what cannot be delivered, and expose it. An endpoint broken for a day should leave a visible backlog the merchant can inspect and replay, not a silently discarded set of events.

The honest framing: a webhook is your reliability problem sitting inside someone else's uptime, which is why every mechanism here is about surviving a counterparty you do not control.

Key takeaway

Reconciliation is the last line of correctness defence, and it is the only mechanism that catches the failures nothing else can see — duplicate consumer application, dual-write divergence, lost updates, uncaptured authorizations — because it compares against a record you did not produce. Its worked example is not executable against the chapter's own schema, which is where the missing amounts in the ledger finally compound into an undetectable bug. The right structure is two independent checks: SUM(entries) == 0 continuously, which localizes faults to your own system, and settlement comparison daily. And a payment's state machine does not terminate at settled — a chargeback reverses a completed transaction months later, which is why records and evidence captured at payment time must be retained, why the ledger must accept reversing entries against old transactions, and why fraud rate is an operational limit rather than merely a cost.

Next: the evaluation.

Enjoying the preview?

Create a free account to unlock the rest of this course, the in-browser judge, and live AI mock interviews.

Sign up free to continue