Diagram · FinTech

Payment Flow and Reconciliation — Diagram

A payment drawn with its failure paths and the reconciliation loop underneath — where the ambiguous state lives, why idempotency sits where it does, and what the ledger is for.

SVG. No sign-up, no email.

Most payment diagrams show the happy path: request, authorise, settle, done. The happy path is not where payment systems fail.

The picture below includes the ambiguous state — the one where you do not know whether money moved — because designing for it is the difference between a system that reconciles and one that produces breaks nobody can explain.

One payment, including the paths people omit Request: Client generates key (one key per logical,payment) → Submit with key () → Server checks key (seen before? return stored,result) → Call processor (). Three outcomes: Authorised (record, write ledger pair), Declined (record reason, no ledger movement), Timeout (did it happen? query by our own,reference). Daily, regardless: Fetch processor file () → Compare to ledger () → Breaks queued (aged, owned, escalated) → Investigated (by a named person, on a,clock). Request Client generates key one key per logical payment Submit with key Server checks key seen before? return stored result Call processor Three outcomes Authorised record, write ledger pair Declined record reason, no ledger movement Timeout did it happen? query by our own reference Daily, regardless Fetch processor file Compare to ledger Breaks queued aged, owned, escalated Investigated by a named person, on a clock after cut-off Where idempotency does its work Outcome unknown — not the same as failed The control that catches what the code missed
The shaded box is the state that causes most serious incidents: the request timed out and the outcome is unknown. Everything below the line runs daily regardless of whether anything looked wrong.

The timeout is the whole problem#

A timeout is not a failure. It is an unknown, and treating it as a failure is how customers get charged twice — the client retries, the processor authorises again, and two payments exist for one order.

Two things handle it:

Idempotency, shown in the first lane. The client generates a key per logical payment; the server stores it with the result; a repeat of the same key returns the original result rather than performing the operation again. This is why the key is generated by the client and not the server — a server-generated key is new on every request, which defeats the purpose entirely.

Query by your own reference. Every operation must be answerable after the fact using an identifier you control. "No response" must never be the end of the story.

Why the ledger is a pair, not a balance#

Every authorisation writes a matching pair of entries, and balances are derived from those entries rather than stored and updated alongside them.

Storing a balance independently guarantees the two will eventually disagree, and when they do you cannot tell which is right. Deriving it means the balance cannot be wrong unless an entry is wrong, and entries are append-only — corrections are new entries, never edits.

The bottom lane runs whether or not anything looked wrong#

That is the point of it. Reconciliation is not an error handler; it is the control that catches what the code missed, including failure modes nobody anticipated.

The part that makes it a control rather than a report is the break process: breaks are aged, owned by a named person, and escalated on a threshold. A daily reconciliation whose output nobody investigates tells you only that the difference has existed for longer.

What is not drawn#

Refunds, reversals and chargebacks, each of which has its own path and its own ambiguous state. Build them deliberately rather than discovering them when the first customer asks for money back.

See fintech and the launch readiness checklist, whose reconciliation section exists because this loop is the one most often left until after launch.

Back to FinTech