Wallet products almost always start the same way: a single integer column called balance on a users table, incremented and decremented as money moves. It works perfectly in a demo. It fails quietly in production, and it fails loudly the first time a regulator, auditor or dispute team asks a question the schema cannot answer: "show me exactly how this balance was constructed, entry by entry, at any point in history."
Why a balance column is not a ledger
A mutable balance column has no memory. It tells you the current state but nothing about how that state was reached, whether an update was applied twice, or whether a refund correctly reversed the original transaction rather than creating an unrelated adjustment. Regulators overseeing electronic money and payment institutions expect safeguarding evidence that can be reconstructed independently of your application logs — a balance column simply cannot provide that.
Auditors ask a deceptively simple question every reporting cycle: can you prove that customer balances reconcile, in aggregate, to the funds actually held in safeguarding accounts? Without an immutable ledger, answering that question means manually reconstructing history from application logs, which is slow, error-prone, and unconvincing to anyone who has seen a real audit.
Double-entry fundamentals for payments
We recommend a double-entry ledger with explicit, typed account categories: customer accounts, suspense accounts, settlement accounts, fee accounts, and safeguarding accounts. Every movement of value is represented as a journal entry with at least two postings that net to zero, tagged with a type — top-up, transfer, capture, refund, fee, chargeback, adjustment — so that reporting can slice activity by cause, not just by amount.
Typed journals
A top-up journal debits an incoming-funds account and credits the customer account. A capture journal moves value from a hold state into a merchant-facing settlement account. A refund journal must reference the original capture journal explicitly — never simply create a new, unrelated debit — so that the full lifecycle of a transaction remains traceable from initiation to resolution.
Hold and release semantics
Holds are where most wallet implementations go wrong. A hold should never simply "disappear" from a balance; it must transition through explicit states — placed, captured, released, expired — each represented by its own journal entry. If a hold silently vanishes without a corresponding release or capture entry, you have lost the audit trail for that money, and no amount of application-level testing will catch it because the bug lives in the data model, not the code path.
Reconciliation as a first-class process
An immutable ledger is only half the solution; the other half is a reconciliation process that continuously proves the ledger matches external reality — bank statements, card scheme settlement files, and safeguarding account balances. Reconciliation breaks should generate exceptions that a human reviews, not silent auto-corrections that hide underlying partner or integration issues.
Design your reconciliation cadence around your settlement rhythm: daily for most card and account-to-account flows, intraday for high-volume wallet top-ups, and always with a clear escalation path when a break exceeds a defined threshold.
An implementation checklist
- Model accounts by type (customer, suspense, settlement, fee, safeguarding) rather than a single generic table.
- Every balance change must be represented as a journal entry with matching debits and credits.
- Holds transition through explicit states — never delete or silently zero a hold.
- Refunds and reversals must reference their originating journal entry, not stand alone.
- Build automated reconciliation against safeguarding and settlement accounts from day one.
- Keep the ledger append-only; corrections are new entries, never edits to history.
The cost of getting it wrong later
Remediating money-movement bugs after launch is among the most expensive engineering exercises in fintech, because it usually requires reconstructing historical customer balances from incomplete data while the product is live and customers are actively transacting. Every week of delay compounds the reconciliation gap.
If you cannot reconstruct every customer's balance from an append-only journal, you do not have a ledger — you have a number that regulators will eventually ask you to explain.
Performance considerations for append-only ledgers
Teams sometimes worry that a strict double-entry, append-only design will be too slow for high-frequency wallet activity. In practice, the performance cost is manageable with the right indexing and periodic balance snapshotting — materialising a customer's current balance as a cached, derived value that is recomputed from the journal on a schedule and verified against it, rather than requiring a full journal replay on every read.
The key discipline is that the cache is always demonstrably derived from the journal, never the other way around. If the two ever disagree, the journal wins, and the discrepancy becomes a reconciliation exception rather than a silent overwrite. This preserves both the audit guarantees of double-entry accounting and the read performance a consumer-facing wallet product needs.
Key takeaways
- Mutable balance columns cannot answer the audit and safeguarding questions regulators will ask.
- Double-entry journals with typed accounts give you a reconstructable, provable history.
- Holds must move through explicit states — placed, captured, released, expired.
- Reconciliation against external reality should surface exceptions, not hide them.
- Fixing ledger design after launch is dramatically more expensive than getting it right first.
Comments
0 comments
Sign in to leave a comment.
InloggenNo comments yet. Be the first to comment.