Payment Ledgers, Reconciliation, And State Machines
Asked of: Software Engineer
Last updated

What's being tested
These problems test whether you can model money movement as explicit state transitions rather than as ad hoc mutations to a balance. You need to preserve monetary precision, deterministic ordering, idempotency, and invariants while handling rejection, retries, cancellation, capture, overdraft, and settlement. Stripe cares because a small correctness bug in a ledger can create duplicated funds, missing funds, or unreconcilable records; interviewers are probing both implementation discipline and your ability to explain tradeoffs clearly.
Core knowledge
-
Integer fixed-point money stores the smallest currency unit, such as cents, rather than floating-point values. For a percentage fee, calculate with integer arithmetic and document rounding:
fee = floor(amount * rate / denominator)or the explicitly specified half-up rule. -
A ledger should be append-oriented: record immutable entries such as debit, credit, fee, reversal, and settlement, then derive balances. A mutable balance cache improves reads, but it must be reproducible from entries and updated atomically with the authoritative write.
-
Use a state machine with enumerated states and an explicit transition table. For example,
accepted -> captured -> settled, whileaccepted -> canceledmay be valid; reject illegal transitions such assettled -> canceledunless a separate reversal operation exists. -
Keep acceptance, authorization or limit usage, and settlement distinct. A payment can be accepted by business validation, consume an account limit, and remain unsettled; collapsing these into one boolean makes refunds, retries, and reconciliation ambiguous.
-
Idempotency means repeating the same logical command has the same effect as applying it once. Key commands by a stable operation or idempotency key, persist the result, and reject reuse with different payloads rather than silently applying a second mutation.
-
Define ordering precisely: sort by timestamp only when timestamps are guaranteed unique; otherwise use
(timestamp, sequence_number)or another stable tie-breaker. Never rely on input order unless the contract explicitly guarantees it. -
Maintain invariants such as and, for a double-entry ledger, total debits equaling total credits. Assert these during tests and expose violations rather than repairing balances silently.
-
Reconciliation compares two independently produced views, such as internal ledger entries and processor settlement records. Match on stable identifiers, amount, currency, and lifecycle status; classify unmatched, duplicated, amount-mismatched, and timing-difference cases for deterministic remediation.
-
Process each account-currency pair independently. A balance for
USDmust not offset a balance forEUR, and an account identifier alone is insufficient as a key; use a composite key such as(account_id, currency). -
For overdrafts, make the coverage rule explicit: consume the account’s available balance first, then debit a platform or reserve account for the deficit, or reject atomically. Preserve the invariant that the resulting account balance and platform coverage reflect the same accepted transaction.
-
Transactional atomicity matters whenever a command changes multiple records. In
Postgres, a database transaction with row-level locking can protect a balance update; optimistic concurrency with a version column can work when conflicts are expected to be low. -
Parsing is part of correctness: preserve empty fixed-position fields, validate field count and types before indexing, and separate syntax errors from semantic rejection. A parser should produce a typed command; the state machine should decide whether that command is legal.
Worked example — Design a Ledger with Separate Acceptance, Limit, and Settlement States
For “Design a Ledger with Separate Acceptance, Limit, and Settlement States,” I would first clarify the command grammar, whether deposits can be retried, whether timestamps determine ordering, and whether query output must reflect accepted commands only or settled funds. I would state that amounts are integer minor units, account balances are keyed by (account_id, currency), and commands are processed deterministically in the specified order. I would organize the solution around four pillars: parsing and validation, account initialization, explicit acceptance/limit/settlement state, and balance-query generation. I would represent each command’s lifecycle separately rather than using one status, because acceptance can succeed while settlement is delayed or limit usage remains active. I would also define the invariant that rejected commands have no financial side effects and that repeated command identifiers cannot create additional balance. A key tradeoff is whether to store only current balances or append immutable ledger entries plus a balance projection; the latter is easier to audit and replay, while the former is simpler and faster for a small coding exercise. I would explain atomic updates if acceptance changes both an account and a platform limit, using a transaction or a version check. If I had more time, I would add property-based tests for illegal transitions, duplicate commands, zero or negative amounts, currency isolation, and replaying the same input to obtain identical results.
A second angle
For “Design a Fixed-Position Payment Command State Machine,” the same principles apply, but the primary risk shifts from aggregation to parsing and transition legality. I would first preserve empty fields and validate exact field positions, then convert the parsed record into a typed command before applying business rules. The state table should distinguish approval, capture, cancellation, and retry, including whether a retry is idempotent or represents a new attempt. The important transfer is that every mutation needs a clear precondition, postcondition, and duplicate-command behavior, even when no balance is changed yet.
Common pitfalls
Analytical mistake: Treating a payment status as a single linear enum such as pending -> paid -> failed is tempting, but it cannot represent independent authorization, limit, and settlement dimensions. Use separate state or ledger concepts and document which transitions affect funds, limits, and reporting.
Pitfall: Using floating-point arithmetic or silently rounding fees can produce reconciliation differences of one or more minor units.
Communication mistake: Jumping directly into maps and loops without clarifying ordering, retries, currencies, or rejection semantics makes a correct implementation look accidental. State assumptions explicitly, write the invariants on a whiteboard, and call out what the prompt does not specify.
Depth mistake: Saying “wrap it in a database transaction” is not enough. Explain which records are locked or versioned, how duplicate commands are detected, what happens after a timeout, and how immutable entries or audit data let operators reconstruct the result.
Connections
An interviewer may pivot to double-entry bookkeeping, idempotency keys, optimistic concurrency control, or event sourcing and projections. They may also ask how to test invariants with property-based testing or how to diagnose a reconciliation break without mutating historical records.
Practice questions
- Split-Tender Payment Events with Gift Card Expiry, ZIP Surcharges and RefundsStripe · Software Engineer · Technical Screen · hard
- Match Payments to Invoices by Memo ID, Exact Amount, then Amount ToleranceStripe · Software Engineer · Onsite · medium
- Design a Ledger with Separate Acceptance, Limit, and Settlement StatesStripe · Software Engineer · Technical Screen · hard
- Reconcile Monthly Bank-Account Balances Across Two DatasetsStripe · Software Engineer · Technical Screen · medium
- Find the Latest Balance for a Bank AccountStripe · Software Engineer · Technical Screen · medium
- Design a Fixed-Position Payment Command State MachineStripe · Software Engineer · Online Assessment · hard
- Calculate Transaction FeesStripe · Software Engineer · Technical Screen · medium
- Match Payments to Invoices by ID, Exact Amount, then a Forgiveness RangeStripe · Software Engineer · Technical Screen · medium
- Compute account balances with rejection and overdraftStripe · Software Engineer · Onsite · medium
- Design payment-to-invoice matcher with prioritiesStripe · Software Engineer · Technical Screen · medium
Related concepts
- Banking Ledgers And Cashback OperationsSystem Design
- Wallets, Payments, And Refund LedgersSystem Design
- Stripe PaymentIntent Lifecycle And State Machines
- Payment Systems: Ledgers, Idempotency, and Reconciliation
- Payment Processing And Ledger SystemsSystem Design
- Reliable Payment Processing And IdempotencySystem Design