Files
ECC/docker/context-profiles/complex-eval/reference4/recurring-incident/docs/handoff.md
T

1.5 KiB

Handoff: refunds & payouts idempotency

What happened

Two incidents, one root cause family:

  • Refunds (INC-201, INC-214, INC-227 in docs/incidents.md): refund requests arriving without an idempotency key were double-processed whenever the storefront retried, refunding customers twice.
  • Payouts: finance's batch job is about to start retrying on timeouts, and keyless payout retries would double-pay vendors the same way.

The fix

Both entry points now route through a single shared helper, src/idempotency.js (deriveKey + once). src/refunds.js and src/payouts.js derive a stable key from the request payload when the caller sends none, claim it synchronously so concurrent retries share one execution, and persist the receipt in src/store.js so retries after a restart return the stored receipt. Gateway side effects all go through src/charge.js, so the ledger is the source of truth for "did this actually happen".

Regression coverage

test/idempotency.test.js covers keyless refund retries, restart durability, and a 20-way concurrent payout storm. The pre-existing test/refunds.test.js and test/payouts.test.js still cover the keyed contract. Everything is wired into npm test; run it before touching any of this.

Prevention

docs/runbooks/idempotency.md is the runbook: any new money-moving operation must go through src/idempotency.js, ship with a retry regression test, and log recurrences in docs/incidents.md. Do not bolt a second inline key-check into a new module — extend the helper instead.