The requirement
All transaction endpoints must be idempotent. If the same
transactionId arrives again, do not apply the operation a second time — return 200 OK with valid data.The key
transactionId is your idempotency key. It is generated by Zero-Dash, unique per transaction, and present on every money-moving callback.
Implementing it
The database does the work. A unique constraint plus one atomic transaction is both correct and fast; application-level “check then write” is a race waiting to happen.Constrain the ledger
transaction_id as the primary key makes a duplicate physically impossible.Insert and update atomically
Replay the stored result on conflict
200 OK. Same shape, same values, no second movement.A reference handler
Idempotency and rollback
A rollback is only valid against a bet that exists.Testing it
Same request twice in a row
Same request twice in a row
Send an identical debit twice. The balance must move once, and both responses must be identical
200 OKs.Same request concurrently
Same request concurrently
Fire both at the same instant, on different connections. This is what the unique constraint is for — an application-level check-then-write will apply both.
Duplicate after a later round
Duplicate after a later round
Debit, settle, play another round, then replay the first debit. The response must carry the original
balanceAfter, not the current balance.Duplicate credit for a zero payout
Duplicate credit for a zero payout
A losing round credits
0. Replaying it must still be a no-op and still return 200 OK.