πŸŽ‰ 75% of content is free forever β€” Unlock Premium from $10/mo β†’
CW
Search courses…
πŸ’Ό Servicesℹ️ Aboutβœ‰οΈ ContactView Pricing Plansfrom $10

Design a Payment System

System Design ProblemsFinancial Infrastructure🟒 Free Lesson

Advertisement

System Design Problems

Design a Payment System

A payment system processes financial transactions between buyers, sellers, and payment networks. Systems like Stripe, PayPal, and Square handle billions of transactions with strict correctness, idempotency, and regulatory compliance.

  • Financial Correctness β€” No lost or duplicate transactions; every penny accounted for
  • Idempotency β€” Retried requests must not cause double-charges
  • Compliance β€” PCI-DSS, SOX, and regional financial regulations

Payment systems are the most critical infrastructure in any business. A bug that loses money or double-charges customers can destroy a company. The design prioritizes correctness over performance.

Requirements

Functional Requirements

  • Process credit/debit card payments
  • Support refunds and chargebacks
  • Merchant onboarding and KYC (Know Your Customer)
  • Payment method management (saved cards)
  • Transaction history and reporting
  • Webhook notifications for payment events
  • Multi-currency support

Non-Functional Requirements

  • Correctness: Zero tolerance for financial errors
  • Durability: Transaction logs must never be lost
  • Idempotency: Retried requests must not cause duplicate charges
  • Availability: 99.999% for payment processing
  • Compliance: PCI-DSS Level 1, GDPR, SOX

Payment systems use double-entry bookkeeping: every transaction has a debit and a credit that must balance. This provides an auditable trail and mathematical verification of correctness.

Back-of-the-Envelope Estimation

Payment System Capacity

  • 10M transactions/day Γ— 100average=100 average =1B/day
  • QPS_write = 10M / 86400 β‰ˆ 116 TPS (transactions per second)
  • Peak TPS (10x): ~1,160 TPS
  • Transaction log: 10M Γ— 1 KB Γ— 365 days = 3.65 TB/year
  • PCI-DSS requires 7 years retention: ~25 TB

API Design

Architecture Diagram
POST /api/v1/payments
Request: {
  "amount": 5000,          // cents
  "currency": "USD",
  "source": "tok_visa_123",
  "description": "Order #12345",
  "idempotency_key": "order_12345_payment_1"
}
Response: {
  "payment_id": "pay_abc123",
  "status": "succeeded",
  "amount": 5000,
  "charge_id": "ch_xyz789"
}

POST /api/v1/refunds
Request: { "charge_id": "ch_xyz789", "amount": 2000 }
Response: { "refund_id": "ref_456", "status": "succeeded" }

GET /api/v1/payments/pay_abc123
Response: { "payment_id": "pay_abc123", "status": "succeeded", ... }

High-Level Architecture

ClientAPI Gateway+ Rate Limit+ ValidationPayment ServiceIdempotency CheckFraud DetectionCharge ProcessingLedger WriterWebhook SenderPayment Network(Visa/MC/Amex)Ledger(Double-entry)Transaction Log(Append-only)Payment System Architecture

Detailed Design

Idempotency

DfIdempotency

Idempotency ensures that processing the same request multiple times produces the same result as processing it once. For payments, this prevents double-charging when clients retry failed requests.

Idempotency Key Lookup

if EXISTS(idempotency:{key}):\nreturn cached_result\nelse:\nprocess_payment()\nSET(idempotency:{key},result,EXPIRY=86400)if\,EXISTS(idempotency:\{key\}):\n return\,cached\_result\nelse:\n process\_payment()\n SET(idempotency:\{key\}, result, EXPIRY=86400)

Here,

  • keykey=Client-provided idempotency key
  • cachedresultcached_result=Previously computed payment result

Idempotency keys must be stored with sufficient TTL (24+ hours). Without idempotency, a network timeout during payment processing could cause the client to retry, resulting in a double charge.

Double-Entry Ledger

Every payment creates two ledger entries that must balance:

Double-Entry Balance

βˆ‘all entriesamountdebit=βˆ‘all entriesamountcredit\sum_{all\,entries} amount_{debit} = \sum_{all\,entries} amount_{credit}

Here,

  • amountdebitamount_{debit}=Debit amounts (money leaving an account)
  • amountcreditamount_{credit}=Credit amounts (money entering an account)

Double-Entry Example

Customer pays $100 to merchant:

Architecture Diagram
Account              Debit    Credit
─────────────────────────────────────
Customer Wallet     -$100
Merchant Wallet                +$100
Processing Fee                    +$3
Platform Revenue                  +$97
─────────────────────────────────────
Total              -$100     +$200  ← Wait, that's wrong!

Corrected with offsetting entries:

Architecture Diagram
Customer Wallet     -$100
Merchant Wallet                +$100

Each transaction has equal debits and credits.

Payment Processing Flow

ClientValidateIdempotencyFraud CheckReserve(Hold funds)Status: pendingCaptureNetworkSettleLedgerWriteStatus: settledPayment lifecycle: validate β†’ reserve β†’ capture β†’ settle

Payment State Machine

DfPayment States

Payments transition through states: pending β†’ authorized β†’ captured β†’ settled. Failed states: failed (declined) or refunded (after settlement).

CreatedAuthorizedCapturedSettledRefundedFailed

Idempotency Design

Every payment request includes an idempotency key:

Architecture Diagram
POST /api/v1/payments
Headers:
  Idempotency-Key: order_12345_payment_1
  Content-Type: application/json

The server checks Redis for the idempotency key:

  1. Key exists β†’ return cached response (no reprocessing)
  2. Key doesn't exist β†’ process payment, store result with 24h TTL

The idempotency key should be deterministic: for example, {order_id}_{payment_attempt}. This ensures retries use the same key while new payments use different keys.

Fraud Detection

Integrate fraud detection in the payment flow:

Fraud Score

fraud_score=w1β‹…velocity+w2β‹…geo+w3β‹…amount+w4β‹…historyfraud\_score = w_1 \cdot velocity + w_2 \cdot geo + w_3 \cdot amount + w_4 \cdot history

Here,

  • velocityvelocity=Transaction frequency in short time window
  • geogeo=Geographic risk score
  • amountamount=Transaction amount relative to user average
  • historyhistory=User's historical fraud rate

Fraud detection must complete within 100ms (payment latency budget). Use pre-computed features and in-memory ML models for real-time scoring.

Practice Exercises

  1. Design: How would you implement a payment refund that partially refunds a multi-item order? Design the ledger entries for a partial refund.

  2. Reliability: If the payment network (Visa) is temporarily unavailable, design a retry strategy that doesn't double-charge the customer.

  3. Compliance: What data must be encrypted at rest and in transit for PCI-DSS compliance? Design the encryption strategy for card data.

  4. Scale: If the system processes 10,000 TPS, estimate the Redis cluster size for idempotency checks and the PostgreSQL cluster size for ledger storage.

Key Takeaways:

  • Idempotency keys prevent double-charging on retries; store with 24h+ TTL
  • Double-entry bookkeeping provides mathematical verification of financial correctness
  • Payment lifecycle: created β†’ authorized β†’ captured β†’ settled β†’ (refunded)
  • Fraud detection must complete within the payment latency budget (100ms)
  • Append-only transaction logs provide audit trails for regulatory compliance

What to Learn Next

-> Design Ticket Booking Inventory management and reservation patterns.

-> Databases ACID transactions and ledger storage patterns.

-> Message Queues Async payment processing and webhook delivery.

-> Security Patterns Encryption, authentication, and PCI-DSS compliance.

-> Design Email System Sending payment receipts and transactional emails.

-> Distributed Consensus Ensuring consistent ledger state across replicas.

⭐

Premium Content

Design a Payment System

Unlock this lesson and 900+ advanced tutorials with a Premium plan.

🎯End-to-end Projects
πŸ’ΌInterview Prep
πŸ“œCertificates
🀝Community Access

Already a member? Log in

Need Expert System Design Help?

Get personalized tutoring, project support, or professional consulting.

Advertisement