Case studies
Double-entry ledger
How a ledger records every money movement as balanced, immutable journals in integer cents, including refunds and payouts, and why balances should be derived from journals.
A ledger is the system of record for money inside your product. It answers "who moved what, and why?" with a permanent trail. The method is double-entry bookkeeping. Every movement is a journal with equal debits and credits. Money is never created or destroyed inside the books. It only moves from one account to another.
The big idea
Each journal has at least two entries. One debits an account, and one credits another, and the two amounts are equal. If the books stop balancing, something is wrong. The check is built into the design.
Entries are never edited. A mistake is fixed by posting a new journal that reverses it. That keeps the history honest, which is what auditors need.
Walking through it
A customer pays $20.00 for order 55. Amounts are in cents, so that is 2000:
Journal j_1001 (payment of 2000 cents):
DR cash_psp_clearing 2000
CR revenue_orders 2000
Refund of $5.00, as a new journal, not an edit:
Journal j_1002:
DR revenue_orders 500
CR cash_psp_clearing 500After both journals, revenue_orders holds 2000 − 500 = 1500 cents, which is $15.00. Both journals stay in the history.
Here is the same idea for a later refund. A $25.00 capture (2500 cents) and then a $10.00 refund (1000 cents) leave 1500 cents available to refund. That number is computed from the journals, not stored in a mutable column.
The properties that matter in engineering:
- Immutable entries. Never update a posted amount. Reverse it with a new journal.
- Derived balances. A balance is the sum of entries for an account. If you maintain a cached balance, update it in the same transaction as the insert.
- Integer minor units. Store
amount_centsas abigintplus a currency code. Never store floats. - Append-only tables. A
journalstable and anentriestable, withentriespointing atjournals.
Refunds and payouts are journals too. A refund reverses revenue. A marketplace payout moves money from the platform's receivable to the seller's payable, then to the PSP. Never write "seller_balance += x" as the only record.
Trade-off
Storing only the PSP's state is faster to build, but it fails audits, disputes and marketplace payouts. A full ledger with PSP references costs schema and operational discipline. It becomes the default once two parties' money, or an audit, is involved.
Square's Books service is a public example of an immutable double-entry store, built for the same reasons at marketplace scale.
Common mistakes
- Updating a posted amount. It erases what happened, and the audit trail breaks.
- Storing money as a float. Rounding errors appear as small drifts.
- Keeping only a balance column. Without journals, you cannot explain how the balance changed.
Check yourself
-
A $25.00 capture is followed by a $10.00 refund. How much is available to refund?
Answer: 1,500 cents ($15.00), computed from the journals.
-
Why reverse an entry with a new journal instead of editing it?
Answer: the history stays intact, and auditors can follow every change. Editing a posted amount erases what happened.
Key takeaways
- Every money movement is a balanced, immutable double-entry journal.
- Store money in integer minor units, and never use floats.
- Derive balances from journals, and correct mistakes with reversing journals.
- Refunds and payouts are journals too.
Further learning
Square's Books service describes an immutable double-entry store used at marketplace scale:
Square: Books, an immutable double-entry accounting database serviceTracking financial transactions at scaleSquare Corner Blog
Next, see how the ledger stays in step with the payment provider, in webhooks and outbox.
Related
Case study: payment system
Moving money correctly, with payment intents, idempotency at every hop, a double-entry ledger, webhooks and reconciliation when the network lies. Start here for the payments map.
Distributed transactions
Keeping data consistent when one business operation spans several databases or services, using two-phase commit, sagas, the transactional outbox and reservations, and redrawing boundaries first.
Reconciliation
How to compare your ledger with the PSP's settlement records on a schedule, match each payment, flag the gaps and treat unexplained differences as bugs.
Cash flow and the three statements
Why profit ≠ cash, how the balance sheet and cash flow statement work, deferred revenue from annual prepay, and how the three statements link.