Payment processing platform inspired by Stripe-style payment processors: payment intents with idempotency keys, a card vault that limits PCI scope, a double-entry ledger, signed webhooks, settlement reconciliation, chargebacks and processor failover. A teaching design.
See how timeouts and retries turn into double charges.
What was missing: Nothing yet.
Why that's risky: If the response times out, the merchant retries and the customer is charged twice; a crash between charging and recording loses track of money.
What gets added: One server that charges the card through the processor and then records the payment.
Trade-offs: Simple, but money movement has no safety net.
Make every retry safe and every payment's state explicit.
What was missing: Retries charged twice and payments had no explicit state.
Why that's risky: Raw card numbers pass through every API server, putting the whole system in PCI scope.
What gets added: A payments API built around payment intents that move through explicit states (created, authorized, captured, refunded), with an idempotency key stored in the same transaction as the intent so a retry returns the original result.
Trade-offs: Authorizing and capturing separately (capture later, on shipping) adds a step but lets merchants cancel without refunds.
Shrink PCI scope to one small service.
What was missing: Card numbers flowed through every API server.
Why that's risky: Money movement isn't recorded as a balanced ledger, so balances and fees can't be proven correct.
What gets added: A card vault that swaps card numbers for tokens and is the only component that decrypts them (with keys from a key management service) to send them to the processor.
Trade-offs: An extra hop to the vault per charge, in exchange for most of the system never touching card data.
Record every movement of money so that it always balances.
What was missing: No ledger: balances were computed from payment rows.
Why that's risky: Merchants aren't told when payments succeed or get disputed, and nothing checks our records against the processor's.
What gets added: A double-entry ledger where every authorization, capture, refund and fee posts balanced debit and credit entries; merchant balances are sums of entries.
Trade-offs: Ledger entries are append-only; mistakes are fixed with reversing entries, never edits.
Tell merchants what happened, reconcile with the processor, and handle disputes.
What was missing: No webhooks, no reconciliation, no dispute handling.
Why that's risky: One processor outage stops all charging, and merchants' dashboards load the primary database.
What gets added: A payment event stream feeding a webhook sender (signed events, retried for days), a settlement and reconciliation worker that matches the processor's daily files to the ledger, and a chargeback handler that posts reversals and asks merchants for evidence.
Trade-offs: Webhooks are at-least-once, so merchants must deduplicate by event id.
Keep accepting payments through a processor outage, and protect and watch the platform.
What was missing: A single processor, dashboards on the primary, no merchant limits, no monitoring.
Why that's risky: Remaining risk is regional: the payments DB and ledger need synchronous replication to a second zone.
What gets added: Routing to a backup processor when the primary fails or declines abnormally, a read replica for dashboards and reports, rate limits per merchant key, and monitoring of authorization rate, processor errors, webhook backlog and reconciliation mismatches.
Trade-offs: Retrying a decline on another processor is only safe for technical failures, never for real card declines.
Require client-supplied idempotency keys and store them with final outcomes. For async steps, use outbox tables and idempotent workers keyed by (intent_id, step). Expire keys conservatively and reject conflicting replays.
Route through a provider gateway with health-checked providers and circuit breakers. Persist provider-of-record with each charge, and reconcile later if a failover provider captured the payment to avoid duplicates.
Double-entry ensures every debit has a credit, enabling invariant checks. Postings are immutable and append-only; balances derive from sums. Corrections are new reversing entries, never mutations.
Track auth/capture/refund success rates, tail latencies, queue depths, retry counts, provider error taxonomy, and ledger drift. Create SLOs with burn-rate alerts on user-facing metrics.
Model refunds as new negative postings tied to the original charge. Chargebacks create dispute holds and postings; evidence workflows run via background workers with strict idempotency and audit logs.
You're in the middle of an interview session. Leaving now will end your current attempt.
Explore concept overviews, real-system examples, key tradeoffs, and interview talking points for each roadmap section.
You've conquered this phase. These are the skills you now own: