Reliable Payment Processing And Idempotency
Asked of: Software Engineer
Last updated

What's being tested
Interviewers expect you to design a reliable, auditable money flow that avoids duplicate charges, recovers from partial failures, and keeps customer-visible state correct. They'll probe your knowledge of idempotency, transactional data models (ledgers vs mutable balances), coordination with external payment processors, and practical tradeoffs between latency, consistency, and operational complexity. At OpenAI they care because money flows must be correct under retries, outages, and scale, and engineers own the code that enforces those invariants.
Core knowledge
-
Money representation: store amounts in the smallest currency unit (e.g., cents) as integer types, never binary float; for multi-currency, record currency code and rounding rules separately.
-
Append-only ledger: prefer an append-only ledger (debits/credits) per account to make state auditable and replayable; balances are derived by summing ledger rows, or maintained as a cache with careful reconciliation.
-
Idempotency vs exactly-once: exactly-once across distributed systems is infeasible without strong coordination; instead guarantee idempotent processing using keys and deduplication to achieve “effectively once” for user-visible effects.
-
Idempotency key pattern: require clients to supply an idempotency key (UUID or hash) and persist (key → outcome) with a unique constraint (e.g.,
UNIQUE (user_id, idempotency_key)) and TTL (24–72 hours) to bound storage. -
Durable response storage: store the full response/decision for an idempotency key and return it on retries; never recompute payment if key exists and outcome is terminal.
-
Database constraints & isolation: use a unique constraint + a short transaction to create a canonical request row; prefer
SERIALIZABLEor optimistic concurrency with retries for race conditions—SELECT … FOR UPDATEis useful for account-locking but can limit concurrency. -
External gateway communication: treat gateway calls as at-least-once; keep an internal state machine (e.g., INIT → PENDING → CONFIRMED → SETTLED → FAILED) and persist the external provider’s transaction id to detect duplicates.
-
Async callbacks & webhooks: verify webhook authenticity, dedupe webhooks using provider transaction id, and reconcile webhook state against your ledger; design idempotent webhook handlers.
-
Retries & backoff: use exponential backoff with jitter; limit total retry window so idempotency key TTL covers retries; retry formula example: retry_delay = base * 2^n + random_jitter.
-
Sagas & compensation: for multi-step flows (authorization, capture, fulfillment), use saga patterns to record steps and run compensating transactions (refunds) rather than distributed two‑phase commit.
-
Scale and data growth: append-only ledgers grow linearly; plan partitioning or sharding when rows exceed ~100M for a single node; maintain daily aggregates and archival strategies.
-
Monitoring and reconciliation: surface mismatches between ledger and external processor with daily reconciliation jobs; instrument
p99latency, failed charge rate, duplicate-charge counters, and reconciliation drift.
Tip: design APIs so retry semantics are explicit (idempotency header, signed requests) and document TTL/behavior for clients.
Worked example — Design a Reliable Payment Processing System
First 30s framing: ask throughput (TPS), expected duplicate/retry behavior, supported flows (auth-only vs immediate capture), external gateways, and SLA for user-visible latency. Declare assumptions: external gateway is at-least-once, 500 req/s peak, and idempotency keys are provided by clients. Skeleton answer pillars: (1) data model (append-only ledger, payments, idempotency table), (2) API semantics (idempotency-key required, synchronous vs async responses), (3) state machine and persistence of external transaction ids, (4) failure/retry handling (dedupe, webhook reconciliation), (5) monitoring and reconciliation plan. One design choice to flag: whether to perform authorization and capture in one transaction (simpler) or split them (needed for places where fulfillment is delayed); splitting requires stronger saga/compensation logic and careful idempotency across steps. Close by promising next steps: sketch partitioning/DB choice (e.g., Postgres primary for correctness, Kafka for eventing), and say you'd prototype the idempotency uniqueness flow and webhook dedupe to validate edge cases.
A second angle — Design a Digital Game Distribution Platform
Here payments are one part of a broader system with entitlements, offline licenses, and large promotion spikes. The same idempotency and ledger ideas apply, but constraints shift: entitlements must be granted atomically with payment confirmation (or compensated on failure), and offline license delivery means you may need signed receipts recorded in the ledger. Add a separate entitlement service that consumes payment-confirmed events from an event stream (e.g., Kafka) and is idempotent on the payment transaction id. For promotion spikes, decouple synchronous checkout from heavy work (download tokens, entitlement indexing) with async workers to keep checkout latency low. Emphasize designing the payment-to-entitlement handoff as an event with idempotent consumers and explicit replay support.
Common pitfalls
Pitfall: relying on floating-point money types.
Usingdoubleorfloatcauses rounding errors and subtle bugs; always use integer smallest-unit or fixed-decimal types.
Pitfall: assuming the external gateway enforces uniqueness.
Gateways may accept duplicate requests; if you don't persist an external transaction id and dedupe, retries can create duplicate charges.
Pitfall: trying to use distributed two-phase commit across services.
Two‑phase commit increases latency and operational complexity; prefer local transactions + sagas/compensation and design for eventual consistency while keeping user-visible invariants intact.
Connections
Payment processing often leads to pivots on event-driven architecture (idempotent consumers, replay), observability (reconciliation dashboards, SLOs), and data partitioning/sharding for scale. Interviewers may ask about optimizing read patterns (caching derived balances) or about message durability (Kafka vs queue).
Further reading
-
Designing Data-Intensive Applications — Martin Kleppmann — strong grounding in logs, replication, consistency patterns used in payment systems.
-
Stripe: Idempotency keys — a production-grade pattern description for idempotent APIs and TTL semantics.
Practice questions
- Design a Digital Game Distribution PlatformOpenAI · Software Engineer · Technical Screen · hard
- Design a Reliable Payment Processing SystemOpenAI · Software Engineer · Onsite · medium
- Design IDE Sandbox and PaymentsOpenAI · Software Engineer · Onsite · hard
- Prevent Duplicate Request ProcessingOpenAI · Software Engineer · HR Screen · hard
- Design a Payment SystemOpenAI · Software Engineer · Technical Screen · easy
- Design a scalable payment systemOpenAI · Software Engineer · Technical Screen · medium
- Design a scalable payment systemOpenAI · Software Engineer · Technical Screen · hard
- Design webhook, POI, chat, CI/CD, paymentsOpenAI · Software Engineer · Onsite · medium
Related concepts
- Payment Processing And Ledger SystemsSystem Design
- Payment Systems: Ledgers, Idempotency, and Reconciliation
- Fintech Ledger Idempotency and Money Movement
- Donation And Payment PlatformsSystem Design
- Idempotency, Deduplication, and Delivery SemanticsSystem Design
- Wallets, Payments, And Refund LedgersSystem Design