Backend coding interview
Wallet Transaction and Refund System Coding Problem
What this interview round tests
The payment wallet is the signature fintech interview round — the PayPal / Paytm-style LLD question where the domain is money and the bar is exact. Credits, debits, and refunds sound like arithmetic until the invariants arrive: a balance can never go negative, refunds can never exceed what was actually captured, and every balance must be explainable from a ledger.
Interviewers ask it because wallets concentrate the habits that separate backend engineers who can be trusted with money from those who cannot: append-only ledgers instead of mutated balance fields, idempotency keys so a retried debit applies once, and refund accounting that survives partial refunds across multiple attempts. It is a favourite at fintech companies and in SDE2 rounds anywhere payments exist.
The scenario
You are handed a wallet service: users hold balances, transactions credit and debit them, and refunds reverse debits partially or fully. Every operation lands in a ledger. The happy path works — and the test suite exposes how much else does not.
Balances go negative under valid-looking sequences, replayed requests double-credit and double-debit, refunds exceed the original transaction when issued in parts, failed operations leave orphan ledger entries, and the ledger no longer sums to the balances it supposedly explains. Your job is to restore every invariant.
What you’ll practice
- Ledger discipline: append-only entries, balances derived and always reconcilable
- Balance invariants: no overdrafts, validated before money moves
- Idempotency keys: replayed credit/debit/refund requests apply exactly once
- Refund caps: cumulative partial refunds never exceeding the original amount
- Atomic failure: a rejected operation leaves balance and ledger untouched
- Money handling in integer minor units with exact accounting
How to approach it
Anchor everything on the ledger: operations append entries, balances derive from them, and any state you cache must reconcile back to the entries. That single decision answers the audit questions (why is this balance what it is?) and makes the failure rule enforceable — a rejected operation appends nothing.
Then treat every mutation as validate-then-commit under an idempotency key: check the key first (a replay returns the recorded outcome), validate funds or remaining refundable amount against live state, and commit the entry and derived balance as one step. Refunds need cumulative tracking per original transaction — the classic bug is validating each partial refund alone and letting three 40% refunds through.
The starter repository ships with a failing test suite and a bundled verify.sh. Reviewed reference solutions are part of Gronex Pro — this page stays spoiler-free on purpose.
FAQ
How is a wallet problem different from the bank transfer problem?
Bank transfer is a concurrency problem: two accounts, racing transfers, deadlock prevention. The wallet is a correctness-and-audit problem: ledger consistency, idempotent retries, and refund caps. Fintech interview loops frequently ask both — they test different muscles.
Why insist on a ledger instead of a balance column?
A lone mutable balance cannot answer disputes, cannot cap cumulative refunds, and cannot prove a replay was already applied. Append-only entries with derived balances give you audit, idempotency, and refund accounting almost for free — which is why reviewers in fintech rounds look for it first.
What retry scenarios do the tests cover?
The suite replays credits, debits, and refunds with the same idempotency key and expects the original outcome back with no double effect. It also interleaves partial refunds to check the cumulative cap and audits that the ledger sums to every balance.