A retry creates two payments
“The client timed out, retried the request, and both attempts reached the payment provider.”
ERROR CODES YOU MAY SEE
A code is a clue. Use its meaning and surrounding evidence to narrow the cause.
23505PostgreSQL SQLSTATE · standardA write violated a uniqueness constraint (unique_violation).
Inspect the constraint name. A collision on an idempotency key can identify a retry, but other unique constraints can fail too; it does not prove a payment succeeded.
Official reference for 23505 (opens in new tab)Code definition reviewedECONNRESETNode.js system errors · platformThe peer forcibly closed the connection.
Correlate client, proxy, and server logs. A reset alone does not reveal whether an operation completed before the connection ended.
Official reference for ECONNRESET (opens in new tab)Code definition reviewed
THE PRINCIPLE
At-most-once UX requires idempotent server behavior, not merely a disabled submit button.
FIRST MOVES
- Generate one idempotency key when the user creates the intent; reuse it for every retry.
- Persist the key, normalized request hash, status, and response atomically.
- Return the stored response for an identical retry; reject reuse with different parameters.
- Reconcile ambiguous provider timeouts instead of starting a second payment.
TOOLS: THEN → NOW
Client-only button locksDatabase uniqueness plus provider idempotency keys
Cron-only recoveryDurable workflows and reconciliation workers
PATTERN SNAPSHOT
INSERT INTO payment_attempts (idempotency_key, request_hash, status)
VALUES ($1, $2, 'created')
ON CONFLICT (idempotency_key) DO NOTHING;
-- Fetch the existing attempt before doing external work.CLOSE THE AI. EXPLAIN THIS.
What should the API do if the same idempotency key arrives with a different amount?Guide reviewed