System Design · Hard
Design a stripe payments
Process credit card payments with idempotency, authorization, capture, refunds, and PCI compliance.
Requirements
What the system must do, and how well it must do it.
Functional
Accept payment methods: cards, wallets (Apple Pay, Google Pay), bank redirects.
Two-step: authorize (reserve funds) → capture (settle) or cancel.
Idempotency keys on every mutating request.
Webhooks for async events (charge.succeeded, charge.refunded, dispute.created).
Support 3D Secure (3DS2) for SCA compliance in Europe.
Non-functional
p99 authorization latency under 500 ms (excluding 3DS challenge).
99.999% availability for the payments API.
Zero double-charge: idempotency guarantees exactly-once semantics.
PCI DSS Level 1: card data never touches application servers.
Estimation
Back of the envelope numbers that drive the design.
10M payments/day = 115 TPS average, 10K TPS peak (Black Friday).
PSP latency: 200-400 ms typical. 3DS challenge adds 5-15 sec.
Idempotency key store: 10M keys/day × 100 bytes = 1 GB/day, TTL 30 days.
Webhook delivery: at-least-once, exponential backoff, 72-hour retry.
Architecture
Design decisions
Idempotency
Every mutating endpoint (create payment, capture, refund) requires an Idempotency-Key header. The payments service checks the cache (Redis) before processing. If the key exists, it returns the stored response. If not, it processes, stores the response (status, body, headers) with a 30-day TTL, and returns it. This guarantees exactly-once semantics even with retries, network failures, or client bugs.
Authorization and capture
Authorize: the PSP reserves funds on the card (typically 7-day hold). Capture: the PSP moves funds from the hold to the merchant. The payments service exposes separate endpoints. Partial capture is allowed (for example, shipping only part of an order). If not captured within the hold window, the authorization expires automatically.
3D Secure (3DS2)
For SCA-regulated transactions, the PSP returns a 3DS challenge URL. The payments service redirects the buyer (or the client SDK renders a challenge iframe). After the challenge, the PSP completes the authorization. The payments service polls or receives a webhook for the final result. Frictionless flow (no challenge) is used when the issuer has enough data.
Webhooks and reliability
Async events (succeeded, failed, refunded, disputed) are delivered via webhooks. The dispatcher retries with exponential backoff (1m, 5m, 15m, 1h, 6h, 24h) for 72 hours. The receiver must be idempotent (use the event ID). Webhook signatures (HMAC) verify authenticity. A dead-letter queue captures permanently failed deliveries for manual review.
Questions and answers
Follow-ups an interviewer is likely to ask. Try answering before you open each one.
How do you handle a PSP outage?
Queue payments locally (Kafka) and replay when the PSP recovers. For authorizations, return a "pending" status to the client and process asynchronously. Never fail open (approve without PSP).
What about refunds after capture?
Refund is a separate idempotent operation. It creates a refund object linked to the charge. The PSP processes it asynchronously. Webhook notifies on completion. Partial refunds are allowed up to the captured amount.
How do you stay PCI compliant?
Use Stripe Elements / Payment Element — the card form is an iframe served from Stripe's domain. Card data never touches your servers. Your API only sees a payment method token. Annual PCI SAQ A attestation.