Design Payment System
Problem Statement
Design a payment system that safely creates charges, handles retries, prevents duplicates, and records immutable ledgers. The system must prioritize correctness and auditability over raw throughput.
Functional Requirements
- Create payment intents / charges
- Confirm and capture payments
- Handle refunds and chargebacks
- Maintain a ledger for auditing
Non-Functional Requirements
- Strong correctness (no double charge, no lost charge)
- Idempotent APIs for client retries
- Auditability and immutable history
- Secure handling of sensitive data
Idempotency Is Mandatory
Clients will retry on timeouts. Every write endpoint must accept an idempotency key and return the same result for the same key within a defined window. This prevents duplicate side effects even under replay.
Ledger as the Source of Truth
Use an append-only ledger for financial events. Do not “update balances” directly as the primary record; compute balances from ledger entries or maintain derived balances with strict reconciliation.
Workflow and State Machine
Payments are multi-step. Model states explicitly (created, authorized, captured, failed, refunded). Enforce valid transitions and make each transition idempotent.
External Provider Integration
Payment providers can be slow or return ambiguous outcomes. Handle “unknown” states by recording pending status and reconciling via webhooks and periodic polling. Never assume a timeout means failure.
Security Constraints
Use tokenization and avoid storing raw card data. Encrypt at rest and in transit. Strictly control access to keys and secrets. Redact logs aggressively.
Production-First Takeaway
Payment systems are correctness-first. Design idempotency into every write, maintain an immutable ledger, model states explicitly, and reconcile external ambiguity with webhooks and controlled retry logic.