Designbench

Lesson 6.4 · 25 min

Payments ledger

Drive a $120 payment through authorize, capture, refund and payout with every entry summing to zero, then run 10,000 payments through timeouts and crashes until no one is charged twice.

The idea in plain words

A payments ledger is an accountant’s book that is never erased: every movement of money is written as a new line, and every line moving money out of one account has a matching line moving it into another.

The problem it solves

Moving money has to be exactly right: a retry must never charge twice, a crash must never lose a record, and the books must balance to the cent.

How it works

  1. Record every movement in a ledger that is only ever added to, never edited.
  2. Write each movement as double entry: money leaving one account and entering another, always summing to zero.
  3. Store amounts as whole cents to avoid rounding errors.
  4. Give every payment request an idempotency key so retries are safe.
  5. Move each payment through clear states (authorised, captured, refunded) and check every day against the bank and the payment processor.

Example

A customer pays $120 with a 2.5% fee: customer −12,000 cents, merchant +11,700, platform fee +300. Total: 0. Any balance is just the sum of its lines, so a mismatch shows up immediately.

Key terms

Ledger
The append-only record of every money movement.
Double-entry
Each transaction is recorded as equal debits and credits, so the books always sum to zero.
Idempotency key
A unique id per payment request so a retry never charges twice.
Minor units
Storing money as whole cents (1999) instead of decimals (19.99) to avoid rounding errors.
Reconciliation
Comparing your records with the bank’s and the processor’s to catch differences.

In this lesson you'll learn

Sections

  1. Requirements and numbers
  2. API and data
  3. Architecture
  4. Deep dive: the ledger
  5. Deep dive: exactly once
  6. Design review

Cheat sheet

Ledgerdouble entry, sums to zero, append-only
Amountsinteger minor units + currency
Retriesidempotency key, stored first
Eventstransactional outbox + dedupe
Dailyreconcile against PSP and bank

The interactive simulations and the scored design review load with JavaScript.