Fintech Ledger Architecture: Why a Balance Is Not a Column
Why a balance should be a derived value
A balance is not a column updated on a user row; it is a value derived from immutable movement records. With a column, two concurrent operations can overwrite each other, a faulty update cannot be undone, and the question "why is this balance what it is?" has no answer. In a movement-based ledger every change has a source, a date and a counter-entry.
What double-entry means in practice
Every transaction produces at least two lines whose sum is zero: one account is debited while another is credited. When a wallet is topped up, the user's wallet account increases and the bank or collection account increases on the other side. This structure gives three things directly: a system-wide sum of zero prevents an amount from appearing out of nowhere; every balance can be traced back to the movements that formed it; and corrections are made with a reversing entry rather than by deleting a record, so history stays intact.
Periodic balance summaries can be kept for performance, but the summary must be reproducible from the movements.
Do not store money in floating point
Amounts should be stored as integers in the smallest monetary unit, or in a fixed-decimal type. Floating point introduces rounding differences that turn into multi-day investigations during reconciliation. Every amount should carry a currency code, and adding different currencies should be prevented at the data-model level. The rounding rule also belongs in one place — at which step, in which direction, to how many digits — because in commission and tax calculations the order of rounding changes the result.
Retries and the state machine
Payment operations follow a defined flow: initiated → authorised → captured → refunded or charged back. Each transition should be declared together with the previous states it is allowed from, and invalid transitions rejected. Operation keys prevent retries from creating new transactions; this is covered in detail in our [API integration guide](/en/blog/api-integration-guide).
Reconciliation: your ledger against the provider's
Daily reconciliation compares your ledger with the provider or bank file. Three difference types are handled separately: present in yours but not theirs, usually an operation whose response was lost; present in theirs but not yours, a missed notification that periodic polling should recover; and present in both with differing amounts, usually commission, rounding or currency conversion. Items that do not clear automatically should land in a queue whose age is monitored. How collection data feeds the weekly cash forecast is covered in the [cash flow guide](/en/blog/cash-flow-tracking-guide).
Card data and scope reduction
The most effective security decision is not to store card data at all. With a provider-hosted field or on-device tokenisation, sensitive card data never passes through your system and the compliance scope narrows considerably. This is an architectural choice rather than a certification claim; the final scope assessment follows the requirements of the relevant standard and its assessment process.
Logging and traceability
In financial systems logging is part of the function, not an extra. Who performed which operation under which authority, which value changed from what to what, and which request that change was based on, all need to be stored — immutably, with a written retention period.
Frequently asked questions
Does deriving balances from movements make queries slow?
Periodic closing balances are kept and the query adds movements since the last close, preserving both speed and traceability.
What if a transaction must be deleted?
It is not deleted. A reversing entry is created and linked to the original. Altering history in financial records destroys auditability.
When should multi-currency support be added?
In the data model from the start, with a currency and rate recorded alongside every amount. Adding it later requires reinterpreting every existing movement.