KibiPay
HomeBlog › Infrastructure

Idempotency and Reliability Patterns in Payments

7 min read Infrastructure
idempotencyreliabilitypayments engineering

Moving money is unforgiving of duplicates. If a request to email a newsletter runs twice, someone gets an extra message. If a request to transfer funds runs twice, someone loses real money. Payment systems live in a world where networks drop connections, timeouts fire before responses arrive, and clients retry — so the central engineering challenge is making operations safe to repeat. The primary tool for that is idempotency.

What idempotency means

An operation is idempotent if performing it multiple times has the same effect as performing it once. Reading a balance is naturally idempotent. Setting a balance to a specific value is idempotent. But incrementing a balance, or creating a payment, is not — each call changes the world again. The job of a payments API is to take these inherently non-idempotent actions and make them safe to retry.

Why retries are unavoidable

Consider a client that sends a payment request and never receives a response. The failure is ambiguous: the request may have been lost on the way in, or it may have succeeded and the response was lost on the way back. The client cannot tell which. Its only sensible options are to retry or to leave the payment in limbo — and limbo is unacceptable for money. So clients retry. Reliable systems assume at-least-once delivery: any message may arrive more than once, and the server must cope.

Idempotency keys

The standard solution is the idempotency key: a unique token, generated by the client, attached to a request that creates or mutates state.

This converts an at-least-once transport into effectively-once processing. The payment happens exactly one time no matter how many times the request arrives. The key must be tied to the operation, not the retry — a fresh key on a retry defeats the entire mechanism, because the server sees a brand-new request.

Getting the server side right

The subtlety lives in the race conditions. Two copies of the same request can arrive concurrently — for instance, a client retrying just as the original is still being processed. Naive "check if key exists, then insert" logic has a gap between the check and the write where both requests slip through. Robust implementations lean on the database:

  1. Reserve the key atomically. Use a unique constraint on the idempotency key so the first request inserts a record and any concurrent duplicate fails the insert rather than proceeding.
  2. Record request and response together. Persist the key, the result, and a status in the same transaction that performs the payment, so the stored outcome and the actual effect can never disagree.
  3. Handle in-flight duplicates. If a second request arrives while the first is still running, return a "processing" signal or make it wait, rather than starting a parallel execution.
  4. Validate the payload. If a reused key arrives with a different request body, reject it — that indicates a client bug, not a legitimate retry.

Exactly-once is a property of effects, not messages

Engineers often chase "exactly-once delivery." In a distributed system with unreliable networks, exactly-once delivery is impossible — you cannot guarantee a message is delivered precisely one time. What you can achieve is exactly-once processing: messages may be delivered many times, but their effect on your state applies only once. Idempotency keys, deduplication tables, and idempotent state transitions are how that is done.

GuaranteeWhat it meansPractical for payments?
At-most-onceNever duplicated, but may be lostNo — losing a payment is unacceptable
At-least-onceNever lost, but may duplicateYes — the realistic transport
Exactly-once (effect)At-least-once delivery + idempotent processingYes — the goal, via idempotency keys

Beyond the key: designing for reliability

Idempotency is necessary but not sufficient. A reliable money-movement system usually pairs it with several habits:

These patterns compose. Idempotency keys make individual requests safe; state machines make sequences safe; reconciliation makes the whole system honest over time.

Takeaway

In payments you should assume every request can arrive more than once and design so that repetition is harmless. Client-generated idempotency keys, backed by atomic server-side deduplication and an explicit payment state machine, turn an unreliable at-least-once world into effectively-once execution — which is what keeps a single tap from becoming a double charge.

See these rails in motion

KibiPay connects UK Faster Payments, Bacs, CHAPS, Mojaloop mobile money and Solana behind one API, with ISO 20022 messaging and real-time fraud & AML screening.

Open the live console How it works