System Design · Design Question
Design a Payment System
Move money safely: idempotency, ledgers, PSP integration, webhooks, and reconciliation — correctness over cleverness.
1. Requirements
- Charge a customer for an order (card / UPI / wallet abstraction).
- Exactly-once money effect despite retries (idempotent APIs).
- Refunds and partial captures (if auth+capture).
- Ledger of balances; audit trail; reconciliation with providers.
- PCI: do not store raw card data — use tokens / PSP hosted fields.
2. API
3. Data model (ledger-centric)
Prefer append-only ledger entries over mutating a single balance row blindly. Balances = sum of entries (materialized for speed).
4. High-level design
5. Idempotency deep dive
- Network timeout after PSP charged but before you saved → webhook + reconcile heal.
- Never invent “exactly once” without durable keys and ledger.
6. State machine
7. Saga with orders
Reserve inventory → charge payment → confirm order. On payment fail, release inventory. On confirm fail after charge, refund. Orchestrator owns the workflow; each step idempotent.
8. Scaling
- Payment API stateless; DB is source of truth.
- Shard by payment_id / merchant_id; careful with ledger uniqueness.
- Async webhooks and notifications; sync path only for charge.
9. Security & compliance
- Tokenize cards; TLS everywhere; verify webhook signatures.
- Least-privilege service accounts; audit logs immutable.
- PII minimization in logs.
10. Auth and capture
Some flows authorize first (hold funds) then capture later (after shipment). Model both as ledger states. Uncaptured auths expire — release holds with a job. Captures must reference the auth id idempotently.
11. Multi-party marketplace (brief)
- Platform charge → split to seller + fee via separate ledger accounts.
- Payout schedule separate from customer charge.
- Still one idempotent customer payment intent.
Quick revision
- Idempotency keys on API and PSP calls prevent double charges.
- Double-entry ledger is the source of financial truth.
- Webhooks + reconciliation fix lost responses.
- Strict payment state machine; ignore illegal transitions.
- Saga with inventory/orders; compensations = refund/release.
- Never store raw card PAN; use PSP tokens.
- Correctness and auditability beat micro-optimizations.