Interview prep notes

Revision a9ac6a88d172dbe9cd2e5089e5f367592c05d190

payment.md Raw

System Design Interview: Designing a Payment System

"Design a payment system like Stripe checkout" is less about scale and more about correctness: money must never be charged twice or lost.

Requirements

  • A customer pays for an order with a card; the merchant sees a final status (succeeded, failed).
  • Exactly-once charging from the user's point of view, even with retries and timeouts.
  • An auditable record of every money movement.
  • Scale is modest (a few thousand payments per second at peak), so correctness dominates.

Core components

  1. Payment service: accepts a payment request, stores a payment row in PENDING, and calls the payment service provider (PSP).
  2. PSP adapter: talks to the card processor; never stores raw card numbers (the PSP's hosted form tokenizes them, which keeps you out of most PCI scope).
  3. Ledger: an append-only, double-entry table. Each payment writes balanced debit and credit entries, so the books always sum to zero.
  4. Wallet / balance service: merchant balances derived from the ledger.
  5. Reconciliation job: compares the PSP's daily settlement file against your ledger and flags mismatches.

Idempotency is the key idea

The client sends an idempotency key (for example the order ID) with every request. The payment service stores it with a unique constraint:

  • First request: insert the key, process the payment, store the result.
  • Retry with the same key: return the stored result instead of charging again.

Pass the same key through to the PSP so its side is idempotent too.

Handling timeouts

If the PSP call times out you don't know whether the charge happened. Don't retry blindly:

  • Keep the payment in PENDING.
  • Query the PSP for the status, or wait for its webhook.
  • Only mark FAILED when the PSP confirms it.

Consistency between services

Writing the payment row and publishing an event to other services should use the outbox pattern: write the event into an outbox table in the same database transaction, then a relay publishes it. This avoids "charged but order never updated".

Follow-ups interviewers like

  • Refunds: a new ledger entry that reverses the original, never an edit.
  • Currency: store amounts as integer minor units (cents) plus a currency code, never floats.
  • Fraud checks: a risk service before authorization, with rules plus a score.

Disclosure: I'm part of the team at Linkjob AI, an AI interview assistant.