Lesson 18 / 25
Design a Payment System and Ledger
Double-entry, idempotency, reconciliation.
Correctness first
A payment system must never lose or duplicate money. Components: a payment service that accepts requests with an idempotency key, a payment executor that calls external providers (card networks, banks via a payment service provider), a ledger, and a reconciliation job. The ledger uses double-entry bookkeeping: every transaction writes at least two entries whose debits and credits sum to zero, so balances can be derived and audited. Entries are append-only; corrections are new reversing entries, never edits. Store amounts as integers in minor units (or a decimal type) with a currency, never floating point. Reconciliation compares the internal ledger against provider settlement files and flags mismatches for investigation.
Double-entry data model
Illustrative schema and a transfer.
accounts(id, owner, currency, type) -- e.g. user wallet, merchant, fees
transactions(id, idempotency_key UNIQUE, status, created_at)
entries(id, transaction_id, account_id, amount_minor, direction DEBIT|CREDIT)
Transfer 50.00 from customer to merchant with a 1.00 fee:
txn T1 (idempotency_key = "pay-8842")
DEBIT customer_wallet 5000
CREDIT merchant_account 4900
CREDIT platform_fees 100
sum(debits) = sum(credits) = 5000 -- checked in the same DB transaction
Balance(account) = sum(credits) - sum(debits) (or a maintained balance row
updated in the same transaction, for liability-style accounts)
Retry with same key -> UNIQUE constraint hit -> return the original resultExpect unknown outcomes
Calls to a provider can time out with the result unknown. Mark the payment as pending, retry with the same idempotency key, and let webhooks and reconciliation settle the final state.
Quick check: How is a mistaken ledger entry corrected in a double-entry ledger?
- By appending a reversing entry, keeping history intact
- By editing the original amount
- By deleting the transaction row
- By changing the account balance directly
Answer
By appending a reversing entry, keeping history intact — Ledgers are append-only for auditability.