Design a Payment System
Design a payment processing system that handles credit card charges, refunds, and payouts.
Related Concepts: Idempotency, Double-Entry Bookkeeping, Saga Pattern, Event Sourcing, PCI Compliance, Retry with Backoff, Reconciliation, State Machines, Distributed Transactions
Step 1: Requirements and Scope
Functional Requirements
- Process credit/debit card payments
- Handle refunds (full and partial)
- Support multiple payment methods (cards, bank transfers, digital wallets)
- Payout to merchants
- Transaction history and receipts
- Handle disputes/chargebacks
Non-Functional Requirements
| Requirement | Target | Rationale |
|---|---|---|
| Availability | 99.99% | Every failed charge = lost revenue |
| Latency | Under 2 seconds | Users will not wait |
| Durability | Zero data loss | Money cannot disappear |
| Consistency | Strong | Double charges are unacceptable |
| Compliance | PCI-DSS Level 1 | Legal requirement for card data |
Scale Estimation
- 10 million transactions per day
- Average transaction: $50
- Daily volume: $500 million
- Peak: 3x average (Black Friday, holidays)
Step 2: High-Level Design
Step 3: Core Design Decisions
Decision 1: Idempotency
Network failures, retries, and duplicate requests are common in payment systems.
Idempotency Key TTL: 24-48 hours (stored in DynamoDB with TTL attribute)
Decision 2: Payment State Machine
Every payment follows a strict state machine:
Decision 3: Double-Entry Bookkeeping
Every transaction creates balanced ledger entries. For a $100 payment:
- Customer Card receives a $100 credit (representing money owed to us)
- Merchant Balance receives a $97.10 debit (representing money we owe the merchant)
- Platform Fee Revenue receives a $2.90 debit (representing our earned revenue)
The key principle: debits must equal credits for every transaction. In this example, total debits ($97.10 + $2.90 = $100) equal total credits ($100), ensuring the books balance to zero.
Decision 4: Handling External Payment Processors
Communication with Visa/Mastercard can fail at any point:
Step 4: Refund Flow
Refunds are separate transactions, not reversals:
Step 5: Database Schema
The payments table (immutable after creation) stores:
- id: UUID primary key
- idempotency_key: Unique constraint ensuring no duplicate processing
- merchant_id: Which merchant receives the funds
- amount_cents: Payment amount in smallest currency unit (cents/pence)
- currency: Three-letter currency code (USD, EUR, etc.)
- status: Current payment state
- payment_method: JSON containing card or bank details (tokenized)
- created_at and updated_at: Timestamps
- metadata: Additional JSON data from the merchant
The ledger_entries table (append-only, never update or delete) stores:
- id: UUID primary key
- payment_id: Links to the parent payment
- account_id: Which account is affected
- entry_type: Either DEBIT or CREDIT
- amount_cents: Entry amount
- currency: Currency code
- created_at: When the entry was created
A balance check constraint (debits equal credits per payment) is enforced at the application level or via database triggers.
Step 6: Failure Handling
| Failure Scenario | Detection | Recovery |
|---|---|---|
| Network timeout to processor | Timeout exception | Query status API, retry if safe |
| Database failure mid-transaction | Transaction rollback | Retry from idempotency store |
| Service crash after charge | Health check | Reconciliation job matches with processor |
| Processor declines card | Decline response | Return error to user, no retry |
| Duplicate request | Idempotency key match | Return cached response |
Reconciliation Process
Step 7: Security and Compliance
PCI-DSS Requirements
| Requirement | Implementation |
|---|---|
| Never store CVV | Tokenize card data at edge |
| Encrypt card data | Use PCI-compliant vault (Stripe, Braintree) |
| Network segmentation | Cardholder data in isolated subnet |
| Access logging | Audit log every access to payment data |
| Regular audits | Annual PCI assessment |
Fraud Prevention
Real-World Systems
| System | Notable Design Choice |
|---|---|
| Stripe | Idempotency keys, complete API |
| PayPal | Two-sided marketplace, buyer protection |
| Square | Hardware integration, POS systems |
| Adyen | Single platform for global payments |
Summary
| Decision | Recommendation | Rationale |
|---|---|---|
| Consistency | Strong (ACID transactions) | Money cannot have eventual consistency |
| Idempotency | Mandatory, 24h TTL | Network retries are common |
| Ledger design | Append-only, double-entry | Auditable, catches errors |
| Card storage | Tokenization via PCI vault | Compliance requirement |
| Failure handling | Reconciliation + manual review | Some failures need human judgment |