invocation.io

Marketplaces · Guide

In-app wallets and ledgers: marketplace bookkeeping that survives

Andrej Dragojevic

Andrej Dragojevic

August 27, 2026 · 9 min read

Every marketplace eventually holds money that belongs to someone else: seller earnings awaiting payout, buyer funds in escrow, wallet balances, reserves. The system recording that money is doing bookkeeping whether you designed it to or not — the only choice is whether it does bookkeeping well. I have built these ledgers for production marketplaces and audited the failures in others; this is the design that survives.

The invariants that matter

  • Every movement is a balanced entry. Money moves from an account to an account, debits equal credits, no exceptions — including fees, refunds, and corrections. The moment one code path increments a balance without a balancing side, you have a money printer with a bug for a trigger.
  • Entries are append-only; balances are derived. The entry log is the truth; a balance is a sum (cached for speed, rebuildable from scratch). If rebuilding balances from entries gives a different number than the cache, you want to know today, not at tax time.
  • Integer minor units, currency-tagged. Floating-point money fails slowly and then suddenly; rounding rules live in one place, applied at defined boundaries.
  • Idempotent postings. Every entry carries the ID of the business event that caused it, unique-constrained. Retries, replays, and double-fired webhooks then cannot double-post — the same property that protects webhook handling protects the ledger.
  • Escrow is an account, not a flag. Funds under hold move into a distinct escrow account and out on release or refund. A boolean on an order row cannot answer "how much is in escrow right now?" — an account balance can, instantly and audibly.

The anti-patterns I keep finding in reviews

  • Two sources of truth in one function — a real processor transfer and a wallet float credited side by side, each rounding differently, drifting apart cent by cent until nobody knows which number is the money.
  • Swallowed transfer errors— the payout API call fails, the exception is logged as a warning, and the ledger records the seller as paid. The database says one thing, the bank another, and the seller's support ticket eventually says a third.
  • Balance updates outside the entry transaction — cache and log written separately, so a crash between them leaves money that exists in one place and not the other.
  • Corrections by UPDATE. Fixing a wrong entry by editing it destroys the audit trail. Corrections are new, balancing entries that reference what they correct — that is the whole point of the design.

Reconciliation: the ledger's immune system

The ledger records intent; the processor and the bank record settlement. A scheduled reconciliation compares them — per day, per account, per rail — and treats every mismatch as an incident with a cause, never as a rounding note. This is the discipline that made a $100M+ payment migration provable, and it is the same muscle at marketplace scale: counts and sums that either match or page someone.

Already have a wallet and a bad feeling? The independent review audits ledger integrity with evidence-graded findings — the sample report shows a real findings table. Building fresh, the platform engagement starts with this architecture instead of retrofitting it.


Frequently asked questions

Do I need a double-entry ledger for an in-app wallet?

If the wallet holds real money — yes, without exception. Single-balance columns that get incremented and decremented cannot answer where money came from, cannot be audited, and drift silently from your processor's records. Double-entry is not accounting pedantry; it is the property that every unit of money has exactly one location at any moment, which is the property every dispute, refund, and reconciliation depends on.

How do I keep my ledger in sync with my payment processor?

You don't 'keep it in sync' — you reconcile continuously and treat divergence as an alarm. The ledger records what should be true from your business events; the processor reports what actually settled. A scheduled job compares the two per account and per day, and any difference is investigated, never adjusted away. Ledgers that get 'corrected' to match the processor stop being evidence and start being decoration.

What are the most common wallet implementation mistakes?

Floating-point money (use integer minor units, always), mutable balance columns instead of append-only entries, updating a cached balance and writing a ledger row in separate transactions, no idempotency on postings (retries double-credit), and holding escrow as a flag on a row instead of as funds moved to a distinct escrow account. Every one of these appears constantly in reviewed codebases, including well-funded ones.

Written by Andrej Dragojevic, Stripe Certified Professional Billing Architect.

Does your ledger tell the truth?

I build and audit marketplace ledgers professionally. A review finds the drift before your accountant does.

I reply within one business day. Now booking new engagements.