codejump
MediumPythonJavaScriptTypeScript

System Design — Idempotency Keys

System DesignIdempotencyPayments

A mobile client times out on "charge 100" and retries. Without protection, the account is
charged twice. Payment APIs fix this with an idempotency key: the client sends a unique key
with each logical request, and the server remembers the outcome.

solve(args, ops) builds new Ledger() and replays ops:

  • charge(key, account, amount) — add amount to the account balance (unknown accounts start

at 0) and return [status, balance]:
- first time the key is seen → apply the charge, return ["created", newBalance];
- the key was seen with the same account and amount → apply nothing and return
["replayed", theBalanceReturnedTheFirstTime] (the original response, not the current balance);
- the key was seen with a different account or amount → apply nothing and return
["conflict", null]. Reusing a key for another request is a client bug.

  • balance(account) — the current balance (0 for an unknown account).

Example

charge("k1","acc1",100) → ["created",100]    charge("k1","acc1",100) → ["replayed",100]
charge("k2","acc1",50)  → ["created",150]    charge("k1","acc1",999) → ["conflict",null]

Sample tests

Test #1Replays and conflicts never change the balance
Input: [[],[["charge","k1","acc1",100],["charge","k1","acc1",100],["charge","k2","acc1",50],["charge","k1","acc1",999],["balance","acc1"],["charge","k1","acc2",100],["balance","acc2"]]]
Output: [["created",100],["replayed",100],["created",150],["conflict",null],150,["conflict",null],0]
Test #2Unknown account
Input: [[],[["balance","nobody"]]]
Output: [0]
Test #3A replay returns the original balance, not the current one
Input: [[],[["charge","a","x",10],["charge","b","x",10],["charge","a","x",10],["balance","x"]]]
Output: [["created",10],["created",20],["replayed",10],20]