← Engineering decisions

How do you stop a payment being processed twice?

An idempotency key enforced by a uniqueness constraint at the point of record, not a check-then-act in application code.

CorrectnessPaymentsConcurrency

“Check if it exists, then insert” is a race with itself. Under concurrency, two requests both check, both find nothing, and both insert.

The version that holds is a natural or derived idempotency key with a unique constraint in the database. The second write does not need to be prevented — it needs to fail, and be recognised as a duplicate rather than an error. The constraint violation is the signal.

Two consequences worth stating plainly:

  • Retries become safe. A caller that does not know whether its request landed can send it again. That is the whole point.
  • Reversal and dispute paths need the same treatment. They converge on the same records, so they need keys of their own or they reintroduce the problem through a side door.
Next decisionHow do you query a billion-row transaction table without falling over?