04QuestionsPayment system
04 · Worked prompt
Payment system
Design payment intents, idempotency, ledgers, authorization, settlement, reconciliation, and webhooks.What a passing answer must show
100 points · 45 minutes
- 20pts
Scope the problem
0–5 minPrioritize the core flows, state the scale, and name the non-goals.
- 15pts
Define contracts
5–10 minIdentify durable entities, APIs, idempotency, and the source of truth.
- 30pts
Complete the diagram
10–25 minTrace one write path and one read path. Label the commit boundary and async work.
- 20pts
Lead one deep dive
25–38 minChoose the highest-risk trade-off and explain the mechanism, alternative, and cost.
- 15pts
Prove reliability
38–45 minWalk a failure, recovery, metric, bottleneck, and evolution path.
One complete box-and-arrow design
Design payment intents, idempotency, ledgers, authorization, settlement, reconciliation, and webhooks.

Write
Merchant clients enters through Payments API. Payment orchestrator owns validation and commits the durable record to Payment + ledger DB.
Propagate
Transactional outbox separates the committed write from background work. Reconciliation workers can retry safely while it builds Settlement view.
Read
Payment status API serves from Settlement view, then checks authoritative state whenever freshness, policy, or correctness requires it. It also consults Payment processor as an explicit dependency.
Say this first: The internal ledger is exact; processor outcomes are idempotent, versioned, and reconciled asynchronously.
Open the full whiteboard ↗Explain every boundary before adding more boxes.
The internal ledger is exact; processor outcomes are idempotent, versioned, and reconciled asynchronously.
Design payment intents, idempotency, ledgers, authorization, settlement, reconciliation, and webhooks.
Exact money · asynchronous rails · zero duplicate charges · long audit retention. State average and peak load, stored bytes, bandwidth or open connections, and the growth horizon before choosing a partitioning strategy.
End-to-end walkthrough
Trace the architecture in this order.
- 01
Enter and classify the request
Merchant clients → Payments APICreate, capture, refund enters over HTTPS / RPC. Payments API handles identity, admission, routing, and request context; it deliberately does not own domain truth.
- 02
Validate, then cross the commit boundary
Payments API → Payment orchestrator → Payment + ledger DBPayment orchestrator receives the idempotent command, checks invariants and retry identity, then uses ACID + outbox to update Payment + ledger DB. The user-visible mutation is accepted only after this boundary succeeds.
- 03
Move replayable work off the request path
Payment orchestrator → Transactional outbox → Reconciliation workers → Settlement viewPayment orchestrator emits publish after commit; Reconciliation workers uses consume and project / update to build Settlement view. Consumers must tolerate duplicate delivery and stale retries because this path is asynchronous.
- 04
Serve reads from the right authority
Payments API → Payment status API → Settlement view / Payment + ledger DBPayment status API uses optimized read for the common, read-optimized path and strong read when correctness or repair requires authoritative state. The API must state the freshness promise instead of hiding it.
- 05
Contain the dependency boundary
Payment orchestrator → Payment processorstable provider ref crosses into Payment processor. Treat timeouts as ambiguous, use a deadline and idempotent retry or reconciliation, and keep the core state recoverable when the dependency is unavailable.
- 06
Deliver without changing the source of truth
Reconciliation workers → Webhook delivery → Merchant clientsReconciliation workers uses signed webhook; Webhook delivery returns updates over stream / push. Sequence IDs, reconnect cursors, and backpressure make delivery resumable without turning a socket into durable state.
Ownership ledger
Why each box exists—and what it must defend.
| Component | Owns | Why it exists | Interviewer probe |
|---|---|---|---|
| Payments APIAuth + idempotency scope | Identity, admission, routing | Protects the system edge and attaches trusted context before domain work begins. | Timeout budgets, quotas, regional routing |
| Payment orchestratorState machine + provider call | Write invariants and retry identity | Serializes or conditionally applies state changes before acknowledging success. | Concurrent writes, deduplication, hot ownership |
| Payment + ledger DBIntent and balanced entries | Authoritative durable state | Provides the one record used to resolve disputes, recover, and rebuild projections. | Partition key, replication, consistency |
| Transactional outboxCommitted merchant events | Durable asynchronous handoff | Absorbs bursts and lets slow or optional work retry independently of the request. | Ordering key, lag, retention, dead letters |
| Reconciliation workersWebhooks + statements | Replayable processing | Runs expensive, fan-out, or side-effecting work with leases and bounded retries. | Idempotency, poison work, autoscaling |
| Settlement viewAttempts, disputes, payout | Rebuildable query state | Shapes data for the dominant reads without weakening the write-side invariant. | Freshness, versioning, rebuild time |
| Payment status APIIntent + ledger + settlement | Read composition and freshness policy | Chooses authoritative or derived state and returns a stable client contract. | Fan-out, cache policy, partial results |
| Payment processorAuthorize/capture/refund | External capability, not local truth | Keeps a specialized or third-party concern behind a replaceable contract. | Ambiguous timeout, circuit breaking, fallback |
| Webhook deliverySigned at-least-once events | Connection and delivery state | Separates open connections and fan-out pressure from durable domain state. | Reconnect, ordering, slow consumers |
Physical design
Name the database, shard key, indexes, and guarantees.
- Database + storage
- PostgreSQL/distributed SQL with strong transactions owns payment intents and a double-entry ledger; queues own provider workflows/webhooks.
- Partitioning / sharding
- Partition by merchant or ledger account while keeping both ledger sides in one transaction domain; coordinate true cross-shard transfers.
- Indexes
- Unique (merchant_id, idempotency_key), provider reference, ledger (account_id, sequence), provider_event_id, and unsettled-attempt age.
- Replication + consistency
- Internal ledger balance and intent transitions are serializable. External settlement is asynchronous and reconciled; timeout is ambiguous.
- Cache, queue + recovery
- Transactional outbox/inbox, signed callbacks, append-only corrections, statement comparison, and explicit repair cases make money observable.
- Capacity math
- Estimate payments/sec, entries/payment, currencies, provider p99, retry volume, settlement files, and reconciliation backlog.
- Alternative rejected
- NoSQL event storage scales but complicates balance invariants; start with a transactional ledger and shard only with understood account locality.
Deep-dive candidates
Pick one risk and explain the mechanism, alternative, and cost.
Ledger boundary
Post debits and credits atomically and derive balances from entries
Mutable balance columns cannot explain money movementAmbiguous timeout
Query the processor by stable merchant reference before creating another attempt
Retries without inquiry cause duplicate chargesReconciliation
Compare provider statements to attempts and ledger, then queue explicit adjustments
Webhooks alone are not proof of settlementFailure pressure test
Show detection, containment, recovery, and evidence.
Processor timeout
Mark attempt unknown and inquire before retry
unknown payment ageDuplicate webhook
Deduplicate provider event ID and make handler idempotent
webhook duplicate rateLedger posting fails
Do not report capture final; retry internal posting from durable provider result
unposted captures- Functional requirements and non-goals
- Peak traffic, storage, bandwidth, and growth
- Entities, APIs, idempotency, and pagination
- Source of truth and consistency promise
- Partition key, replicas, caches, and hot spots
- Retries, backpressure, failover, and reconciliation
- Latency, saturation, correctness, and recovery metrics
- Security, migration, cost, and multi-region evolution
A four-part talk track
- Scope
“I’ll prioritize authorize, capture, refund, and query payments and never create a duplicate customer charge.”
- Scale
“The design changes around exact money · asynchronous rails · zero duplicate charges · long audit retention.”
- Decision
“Keep immutable ledger truth separate from mutable workflow state.”
- Risk
“The first failure I want to pressure-test is: A timeout can mean the provider charged while the caller believes it failed.”
Reference details
Open these only after you can explain the diagram above without reading.
01Requirements and state lifecycle4 requirements
- Authorize, capture, refund, and query payments
- Never create a duplicate customer charge
- Keep an immutable auditable balance history
- Resolve delayed and out-of-order processor callbacks
Each transition must be durable, observable, and safe to retry.
02Data model and APIs4 entities · 4 interfaces
Core entities
payment_id, merchant_id, amount, state, versionOwner: Payment serviceentry_id, transaction_id, account, debit, creditOwner: Ledgerattempt_id, payment_id, provider_ref, stateOwner: Adapterprovider_event_id, payload_hash, processed_atOwner: Webhook inboxExternal interfaces
/v1/payment_intentsCreate intent under merchant idempotency key
/v1/payment_intents/{id}/captureConditionally capture an authorized amount
/v1/refundsCreate a refund against settled payment balance
/v1/payment_intents/{id}Read workflow and reconciliation state
03Deep dives and trade-offsChoose one
Ledger boundary
Post debits and credits atomically and derive balances from entries
Mutable balance columns cannot explain money movementAmbiguous timeout
Query the processor by stable merchant reference before creating another attempt
Retries without inquiry cause duplicate chargesReconciliation
Compare provider statements to attempts and ledger, then queue explicit adjustments
Webhooks alone are not proof of settlement04Failures, recovery, and evidence3 scenarios
Processor timeout
Mark attempt unknown and inquire before retry
unknown payment ageDuplicate webhook
Deduplicate provider event ID and make handler idempotent
webhook duplicate rateLedger posting fails
Do not report capture final; retry internal posting from durable provider result
unposted captures05What makes the answer seniorInterviewer signals
- Say what customer-visible success means at each state
- The ledger and payment state machine solve different problems
- A strong answer embraces eventual external settlement without weakening internal accounting
- Primary trade-off: Keep immutable ledger truth separate from mutable workflow state.
Can you redraw it from memory?
- Name the source of truth.
- Trace the write and read paths.
- Defend one trade-off.
- Recover from one failure.