45. Design a Payment System (Stripe / UPI)
1
The interview prompt2
45.1 What do you ask first?3
45.2 How big is this, really?4
45.3 What are the doors?5
45.4 What do we store?6
45.5 What does the whole system look like?7
45.6 How does idempotency work end to end?8
45.7 How does a double-entry ledger work?9
45.8 How does reconciliation work?10
45.9 How do callbacks and webhooks work?11
45.10 How do you keep it consistent?12
45.11 What would you say in the last five minutes?13
Score yourself14
Variations to practise15
Sources16
Code lab45. Design a Payment System (Stripe / UPI)
45.5 What does the whole system look like?
3 min read+10 XP
Step 4: High-level design (10 minutes)
Figure: the final design. Every money movement is a balanced ledger transaction; every unclear outcome stays pending until checked.
A payment, step by step:
- The merchant creates a payment intent with an idempotency key (45.6). A retry returns the same intent.
- The payment engine sends the request to the right bank adapter (card network, UPI), passing its own idempotency reference.
- The answer is one of: succeeded, failed, or no answer / timeout. A timeout makes the intent pending, never failed (45.8).
- On success, the engine writes the intent's new state and a balanced ledger transaction (customer money in clearing, merchant owed, our fee) in one database transaction, plus an outbox event (45.7, 45.10).
- The outbox relay sends a signed webhook to the merchant (Chapter 24).
- Callbacks from the bank, and status checks we initiate, resolve pending payments (45.9).
- Every day, reconciliation compares our ledger with the banks' settlement files and flags any break (45.8).
Interviewer pushback:
- "The engine crashes after the bank says yes, before we record it." → The bank's success will arrive again via callback or a status check (we always check pending attempts), and the ledger transaction ID
capture:pi_1makes recording it idempotent. Reconciliation is the final safety net. - "A bank is down." → Its adapter's circuit breaker opens (Chapter 29); route to another acquirer where possible, or fail fast with a clear "try another method".
Memory code: "Intent, attempt, ledger, outbox; check what's pending; reconcile every day."
Offer: "The interesting parts are idempotency, the double-entry ledger, reconciliation, callbacks and webhooks, and consistency. Which would you like?"