| wallet A | −5000000 | customer's balance falls |
| cash:branch_ikeja:till_03 | +5000000 | the till now holds less cash, tracked as an account |
| Σ | 0 | end of day, the physical count must equal this account's balance |
Bonus round: “now design a different one”
Fifteen rounds designed one system. The value of that is not the system; it is the skeleton underneath it, which fits almost every money-moving business. This part strips the skeleton out and then reuses it four times at speed, so that when an interviewer changes the prompt you are adapting a structure rather than starting over.
The transferable skeleton
Every design in this part is the same nine decisions, answered differently. If you remember nothing else from the module, remember these.
| # | The decision | Our answer, and the one to adapt |
|---|---|---|
| 1 | What is the unit of truth? | A balanced, append-only entry. Almost never a balance column. |
| 2 | How is money represented? | Integer minor units, with the exponent read from a currency table. |
| 3 | What is the invariant? | Entries sum to zero per journal per currency, checked continuously. |
| 4 | How do duplicates become harmless? | A unique key on the intent, at the database level. |
| 5 | Where does value sit when the outcome is unknown? | A named suspense account, never nowhere. |
| 6 | How do other systems learn what happened? | An event log fed by a transactional outbox. |
| 7 | How is permission separated from money? | Holds, limits and facilities. Available balance versus ledger balance. |
| 8 | How do you prove it is right? | Internal invariant plus external reconciliation against every counterparty. |
| 9 | What degrades, in what order? | Decided in advance, with kill switches on the front door only. |
Designing a retail bank, in fifteen minutes
“Design the core of a licensed retail bank. Deposits, transfers, loans, branches, ATMs.”
The one we just built, so the value here is in what a licensed bank adds that our design did not need.
- Customer deposits are liabilities on your own balance sheet, not money held at a partner. You are the counterparty.
- Capital and liquidity requirements. You must hold reserves against deposits and report ratios to the central bank.
- Direct central bank settlement. An RTGS account rather than a relationship with a sponsor bank.
- Deposit insurance obligations and reporting per depositor.
- Far heavier regulatory reporting, which makes Part 11's four-way pinning mandatory rather than prudent.
- Cash is a ledger account. A branch till and an ATM cassette each get accounts, and cash movements are postings like any other.
- Cash reconciliation is physical. Someone counts the till and the count is reconciled against the ledger. The Part 10 machinery applies, with a human as the counterparty.
- Offline capability. A branch with a broken link must still serve customers, which is Part 8's stand-in problem in a different costume.
- Dual control. Large cash movements need two staff members, which is the four-eyes rule from Part 9 applied physically.
Designing a neobank: Cleva, Grey, and the USD problem
“Design a product that gives Nigerian freelancers a US dollar account they can receive international payments into, and convert to naira.”
- The USD accounts are provided by a partner bank in the US, usually as virtual accounts under one pooled omnibus account.
- So you hold a claim on the partner, and your customers hold a claim on you. Two layers, and both must reconcile.
- You are regulated as a money transmitter or payment service provider, not a deposit-taking bank, which shapes what you may promise.
- Your entire business is the FX spread, so Part 7's position management is not a side feature; it is the product.
the two-layer ledger, and why both are needed
partner bank omnibus account: $2,400,000
↑ this is ONE account at the partner
our ledger, customer sub-accounts:
customer A: $1,200 customer B: $840 … 18,000 more
Σ must equal $2,400,000
the daily reconciliation that matters most: our sum of customer
USD balances must equal the partner's omnibus balance, exactly.
a break here means we owe customers more than we hold.| partner_receivable:USD | −100000 | the partner now holds this for us |
| customer A (USD) | +100000 | our liability to the customer |
| Σ USD | 0 | and the USD leg balances independently, per Part 7 |
- The USD-to-NGN conversion is the revenue and the risk. Quote with expiry, hold a position, hedge or cap it. This is Part 7 chapter 84, and it is the business.
- Naira liquidity. You must actually have naira to pay out, which is a treasury operation rather than an engineering one, and the engineering must expose the position clearly enough to manage.
- Partner concentration risk. One partner bank is a single point of failure for the entire product, so the connector abstraction from Part 6 matters more here than anywhere.
- Compliance on inbound. Sanctions screening on every international credit, failing closed, per Part 9 chapter 109.
- Regulatory ambiguity. Cross-border FX for individuals is politically sensitive in Nigeria, so the compliance-as-data design from Part 7 is what lets you adapt to a rule change in days.
Designing a PSP: Paystack, Flutterwave, Monnify
“Design a payment processor. Merchants integrate an API, accept cards and bank transfers, and get settled the next day.”
- A collections engine across many channels: cards, bank transfer, USSD, virtual accounts.
- A merchant ledger holding what each merchant is owed.
- A settlement engine paying merchants on a schedule, net of fees.
- A risk engine deciding which merchants to onboard and which to hold funds from.
- An integration surface, which is the actual product: SDKs, a dashboard, and webhooks.
| acquirer_receivable | −1000000 | the acquirer owes us this |
| merchant_payable:M-88 | +985000 | what we owe the merchant |
| fee_income:collection | +15000 | our revenue |
| Σ | 0 | merchant_payable is the balance the dashboard shows them |
- Settlement timing and float. Money collected today is paid out tomorrow, so the PSP holds a large overnight balance. That float is both a funding advantage and a liability you must never spend.
- Chargeback risk. A card payment can be reversed months later, after the merchant has been paid. If the merchant has vanished, the PSP eats the loss, which makes merchant underwriting a core function rather than an onboarding formality.
- Rolling reserves. Hold a percentage of a risky merchant's volume for 90 days as protection. Implemented exactly as Part 5's holds.
- Merchant idempotency. Merchants will retry, double-submit and replay. Their reference is the idempotency key, and the API must be unbreakable by a careless integrator.
- Webhook delivery to merchants. Now you are on the sending side of Part 6's webhook problem, so you need signing, retries with backoff, and a replay endpoint for when they were down.
How a PSP differs from a bank, structurally
| Bank | PSP | |
|---|---|---|
| Who holds the money | You do, as a licensed deposit taker | A partner bank. You hold a claim |
| Customer balance means | A deposit, insured, withdrawable on demand | An amount owed, payable on a settlement schedule |
| Primary risk | Credit risk on lending | Chargeback and merchant fraud |
| Revenue | Net interest margin, plus fees | Transaction fees, plus float income |
| Balances change | Continuously, in both directions | Up during the day, to zero at settlement |
| Regulator cares about | Capital, liquidity, deposit protection | Safeguarding client funds, AML |
| Worst failure | A run on deposits | Settlement failure. Thousands of merchants unpaid simultaneously |
- Client funds are held in a segregated account, legally separated from the PSP's own money.
- They may not be used for operating expenses, however tempting during a cash crunch.
- The segregated balance must always equal or exceed the sum of merchant payables.
- That is a daily reconciliation with a hard pass/fail, and failing it is a regulatory incident rather than an engineering bug.
-- the single most important query in a PSP, run continuously.
-- it is the Part 10 machinery pointed at the one number that
-- determines whether the business is solvent and compliant.
SELECT
(SELECT balance FROM segregated_account_balance) AS held,
(SELECT SUM(-amount) FROM entries e JOIN accounts a
ON a.id = e.account_id
WHERE a.kind = 'merchant_payable') AS owed;
-- held MUST be >= owed. always. any shortfall is an immediate
-- escalation, not a ticket.Designing a POS network and its offline problem
“Design a POS network for 200,000 agents in Nigeria. Many operate where connectivity is unreliable.”
The genuinely distinctive problem, and the one worth the whole fifteen minutes. Everything else is a variation on what we have already built.
200,000 terminals × ~40 transactions/day = 8M/day
peak concentration: 17:00-20:00 ≈ 1,500/s
but the defining number is:
~8% of terminals have unreliable connectivity at any moment
= 16,000 terminals that may be offline mid-transaction
- Cash-out from a card, online. Normal authorisation. No problem.
- Cash-out, terminal offline. Must decline. The agent is handing over real cash against a card we cannot verify, so an offline approval is an unsecured loan to a stranger.
- Transfer, terminal offline. Queue it locally and submit when connectivity returns, with the customer told clearly it is pending.
- Balance enquiry, offline. Decline. A stale balance shown at a POS terminal causes disputes.
- An agent pre-funds a float account with us, and their cash drawer is the physical counterpart.
- A customer cash-out debits the customer and credits the agent's float, so the agent's cash decreases and their electronic balance increases.
- The agent's float balance is a hard limit: they cannot dispense more than they have pre-funded, which bounds our exposure to exactly zero.
- This is why offline cash-out is unnecessary as well as unsafe: the agent's float is the constraint, and validating it requires being online anyway.
| wallet A | −2000000 | customer's balance falls |
| agent_float:AG-4471 | +1990000 | agent's electronic balance rises |
| agent_commission | +10000 | the agent's earning on this transaction |
| Σ | 0 | the agent gave out cash and received electronic value plus commission |
Designing a lending platform on someone else's ledger
“Design a lending product where you do not hold the customer's account. You disburse into their bank account and collect from it.”
- You still need a ledger. It tracks what each borrower owes you, which is your asset, and it is entirely your own.
- Disbursement is an outbound payment over a Part 6 rail, with all of its uncertainty. The
unknownstate now means "we may have lent money and not recorded it". - Collection is a pull, via direct debit or a standing mandate, which can be reversed for a period after it succeeds.
- You cannot see the balance before collecting, so you are guessing when to attempt, which makes retry strategy a revenue lever.
- Your only leverage is the mandate and the credit bureau, rather than the ability to freeze funds.
the collection retry problem, which is the whole business:
attempt on the due date → ~62% succeed
retry the next day → +9%
retry on the likely salary date → +18%
retry at a partial amount → +6%
timing and amount, learned per borrower, are worth more than
any collections messaging. this is a prediction problem.- Inferring income timing. Observed credit patterns predict when money arrives, and attempting collection an hour after a salary credit is dramatically more effective than attempting at midnight.
- Partial collection. Taking what is available beats failing entirely, and it requires the allocation ordering from Part 5 chapter 60.
- Reversal exposure. A collection that is reversed weeks later means the loan was not really repaid, so the cleared balance concept from Part 5 applies to your own receivable.
- Mandate lifecycle. Mandates expire, are cancelled, and fail silently. Monitoring mandate health is monitoring your ability to get paid.
- Affordability and regulation. Aggressive collection is increasingly regulated, so the protected-floor idea from Part 14 chapter 164 is both ethical and compliant.
What stays the same in all of them
| Holds the money | Bears the loss | The one number | |
|---|---|---|---|
| Retail bank | Itself | Credit risk on lending | Capital adequacy |
| Neobank | A partner bank | FX position, partner failure | Omnibus versus sum of customer balances |
| PSP | A segregated account | Chargebacks, merchant fraud | Held versus owed |
| POS network | Agent pre-funded float | Almost none, by design | Agent float never negative |
| Lending platform | Nobody. It moves through | Default risk | Collected versus cleared |
- Double-entry with a zero-sum invariant. Every one of them.
- Integer minor units. Every one of them.
- Append-only, corrections as new entries. Every one of them.
- Idempotency on the intent. Every one of them.
- Suspense accounts for uncertainty. Every one that touches an external rail, which is all but the simplest.
- Available versus ledger balance. Every one with holds, limits or provisional funds.
- An event log with an outbox. Every one with more than two services.
- Continuous internal checks plus external reconciliation. Every one, and the counterparties differ.
- Designed degradation with kill switches. Every one that runs at 3am.