Double-Entry Bookkeeping, Explained Through a Digital Wallet
A ledger sits at the core of any fintech system — it’s the source of truth for who has what and who is owed what. Working in that space, it’s worth understanding double-entry bookkeeping properly rather than treating balances as opaque numbers in a database row. This is a writeup of that exploration: the fundamentals of double-entry, worked through a virtual wallet platform, with the points that are easy to get wrong called out explicitly.
What this covers: how to read and write a journal entry, how to post it to an account, and why the books always balance — using a wallet system as the running example.
The mental model
The whole thing rests on one sentence:
Money never appears or disappears. It only moves between buckets.
The buckets are called accounts. “Double-entry” means every time money moves, it’s written down in two places: where it came from and where it went to. And there’s one iron law that makes the system trustworthy:
In every transaction, the total leaving equals the total arriving.
If the two sides don’t match, there’s a mistake. That self-checking property is the reason the system has outlived every alternative — and the reason it’s the right model for a ledger.
A wallet balance is a liability, not an asset
This is the first thing worth getting straight, because intuition usually says the opposite. A user tops up their wallet with Rs. 100 from their bank. From the platform’s point of view, two accounts move:
Cash/Bank— the real money the company now holds (an asset)User Wallet— what the company now owes the user (a liability)
The user’s balance is a debt the platform owes them, not money the platform owns. The real cash is the asset; the user’s balance is the IOU sitting against it. Keeping these separate is what makes the rest of the ledger coherent.
“Debit” and “credit” are not “money out” and “money in”
Everyday banking language trains the wrong intuition here. Debit and credit are simply labels for the left and right side of an entry. Whether a debit increases or decreases an account depends on the account’s type:
| Account type | A debit does… | A credit does… |
|---|---|---|
| Asset (Cash/Bank) | increases ↑ | decreases ↓ |
| Liability (User Wallet) | decreases ↓ | increases ↑ |
They’re mirror images. The thing to internalize is one rule, not four facts: assets and liabilities react to a debit in opposite directions.
Walking through it
Journaling: deciding the two sides
Writing the entry is called journaling. The top-up (user adds Rs. 100):
Debit Cash/Bank 100 (asset ↑ — real money came in)
Credit User Wallet 100 (liability ↑ — we owe them more now)
Debits = 100, Credits = 100. Balanced. By convention the debit is written first, the credit indented.
Now the opposite move — the user withdraws Rs. 30 back to their bank:
Debit User Wallet 30 (we owe them less → liability ↓ → debit)
Credit Cash/Bank 30 (real money left → asset ↓ → credit)
Compare the two entries. On the top-up, both accounts went up. On the withdrawal, both went down. Yet the debit/credit labels flipped completely. That demonstrates the labels track account type, not “money in vs. out.”
An internal transfer — user sends Rs. 40 to a merchant who also has a wallet:
Debit User Wallet 40 (owe user less → liability ↓ → debit)
Credit Merchant Wallet 40 (owe merchant more → liability ↑ → credit)
Cash/Bank never appears. No real money entered or left the company — it just shifted who it owes. Total liabilities are unchanged; only the distribution moved. That’s exactly what an internal transfer is.
Posting: just copying
The journal is a diary — entries in time order. But it can’t answer “how much is in Cash/Bank right now?” without scanning every line. So a second record is kept, organized by account: each bucket gets a running tally. Copying each journal line onto its account’s page is called posting, and the tool for it is a T-account — debits on the left, credits on the right.
Here’s User Wallet posted from all three transactions above (one credit of 100, one debit of 30, one debit of 40):
User Wallet (Liability)
Debit | Credit
-------+--------
30 | 100
40 |
-------+--------
left=70 | right=100
Balance: 100 − 70 = 30, on the credit side
A credit balance on a liability is normal — it means we owe the user Rs. 30. Sanity check in plain words: they put in 100, withdrew 30, sent 40 away → 30 left. The books agree.
Why it all balances
Post every account and add them up:
- The platform owes: User 30 + Merchant 40 = Rs. 70 (liabilities)
- The platform holds: Cash/Bank Rs. 70 (assets)
Rs. 70 = Rs. 70. Every transaction kept that true, because every entry’s debits equalled its credits. That’s the accounting equation in its simplest form: Assets = Liabilities.
Where profit comes from: the Income account
Everything so far was money shuffling — the platform never got richer. Charging a fee changes that. The user sends Rs. 40, the platform keeps Rs. 2, the merchant receives Rs. 38:
Debit User Wallet 40 (owe user less → liability ↓ → debit)
Credit Merchant Wallet 38 (owe merchant more → liability ↑ → credit)
Credit Fee Income 2 (platform earned it → income ↑ → credit)
Debits = 40. Credits = 38 + 2 = 40. Still balanced — a journal entry can have more than two lines, and the law never changes.
Fee Income is special: it’s the first account not owed to anyone. That Rs. 2 is the platform’s to keep. After this entry, assets no longer exactly equal liabilities — and the gap is precisely the Rs. 2 earned. That gap is Equity (the owners’ stake), which gives the full equation:
Assets = Liabilities + Equity
Liabilities are what’s owed to others; equity is what’s owed to yourselves. Income feeds equity over time. Notably, nothing added a “profit counter” — profit falls out of the same debit-and-credit machinery.
Points worth getting straight
These are the spots where the model is easy to misread — worth flagging clearly because each one quietly breaks the whole picture if left wrong.
A wallet balance is a liability, not an asset. The cash backing it is the asset. Conflating the two makes the ledger feel arbitrary; separating them makes it click.
“Debit” is a side, not a direction of money. On a liability, a debit makes the balance go down. The banking-app intuition that “debit = spending” leaks in easily and has to be set aside.
Journaling and posting are two separate jobs. The debit/credit decision is made once, during journaling. Posting is pure copying — a line that said “Debit” goes on the debit side, full stop. Trying to re-derive debit vs. credit while posting is the most common way to stall; splitting the two jobs makes posting trivial.
Amounts come from the journal line, not from memory. When posting, every number is read straight off its entry. Carrying a figure over from an earlier calculation is an easy slip that quietly unbalances things.
When this matters (and when it doesn’t)
Double-entry is overkill for a personal “money in / money out” spreadsheet. It earns its keep the moment money flows between parties and there’s accountability for it — which is exactly a wallet or payments platform. The self-balancing property becomes a built-in correctness check: if assets ever stop equalling liabilities + equity, there’s a bug, and it surfaces before users see it. For a ledger implementation, model accounts with explicit types and enforce debits = credits as an invariant on every transaction write.
Takeaways
- Money only moves between buckets (accounts); record every move in two places.
- The iron law: total debits = total credits, every time.
- Debit/credit are left/right labels; their effect depends on account type.
- A user’s wallet balance is a liability to the platform; the backing cash is the asset.
- Journaling decides the sides; posting is just copying them into per-account tallies.
- Profit isn’t counted separately — it emerges as equity from
Assets = Liabilities + Equity.