A customer clicks the Pay button, the server sends the request to a payment provider, and the charge succeeds. The trouble begins when the response never reaches the browser because the connection drops. The frontend sees a timeout and retries the same request, while the backend treats it as a brand-new payment. One customer action has now created two valid charges.
This kind of failure is easy to miss because every part of the system appears to behave correctly on its own. The browser retries because it never received a response, the API processes a valid POST request, and the payment provider accepts the instruction it was given. The defect sits between those systems, where no component has enough context to know that the second request is a repeat. The same pattern can create duplicate orders, emails, subscriptions, shipments, support tickets, or background jobs.
The common advice is to retry failed requests, but that advice is incomplete. A retry is safe only when the server can distinguish a repeated business operation from a new one. For payment APIs, that usually means introducing an idempotency key, storing the original result, and protecting the write path against race conditions. The rest of this article walks through that flow using Node.js, Express, and PostgreSQL.






