{
  "id": 1338836,
  "title": "Same Request Sent Twice: How Idempotency Prevents Duplicate Payments",
  "url": "https://urgent.news/2026/08/16/same-request-sent-twice-how-idempotency-prevents-duplicate-payments",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-08-16T19:51:05.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/suyash_dhakal/same-request-sent-twice-how-idempotency-prevents-duplicate-payments-3g8i"
  },
  "original_language": "en",
  "account": "A user opens a payment app, enters an amount, and taps \"Pay.\" The spinner appears, but nothing happens – no success screen, no error, just a frozen loader. The user taps \"Pay\" again or the app retries silently in the background. The issue arises when the first request reaches the server, and the payment is processed, but the response gets lost on its way back. If the user retries, it sends the same payment request again. This isn't a rare case; mobile connections drop, servers slow down under load, and requests time out. Any system moving money over a network faces this problem.\n\nThe solution is idempotency. An operation is idempotent when running it twice has the same effect as running it once. In other words, the outcome should not change if you repeat the request. Pressing an elevator button five times doesn't call five elevators; the building registers your request once. Similarly, a payment should work the same way: pressing the \"Pay\" button multiple times shouldn't lead to multiple charges.\n\nPayments are more sensitive to this issue than most systems because the thing being repeated moves real money. If a retried request reloads a page or re-fetches data, nobody gets hurt. But in payments, the thing being repeated is the actual money movement. Here's an example of a failure scenario:\n\n1. User → Payment API → Payment processed → Response is lost → User retries → Same payment request reaches the server again.\n\nThe problem lies in the server not recognizing the repeated request as a repeat but instead treating it as a new request. This is where idempotency keys come into play. The server generates a unique value and attaches it to the payment request, usually as an HTTP header:\n\nPOST /payments\nIdempotency-Key: abc123\nContent-Type: application/json\n{\namount: 100,\ncurrency: NPR\n}\n\nIf a retry occurs with the same key, the server recognizes it as a repeat and processes it only once. The key ensures the server links the retry back to the original attempt, preventing duplicate charges.",
  "summary": "A user opens a payment app, types in an amount, and taps \"Pay.\" The spinner shows up. Then nothing — no success screen, no error, just a frozen loader. So they tap \"Pay\" again. Or the app quietly retries in the background because it never got a response. But here's the actual problem: what if the first request actually reached the server and the payment already went through, and only the response…",
  "key_points": [
    "User retries payment request after initial request fails",
    "Idempotency keys prevent duplicate charges by linking retries to original requests",
    "Idempotent operations have same effect when repeated, like pressing an elevator button"
  ],
  "editors_take": null,
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}