Idempotency: The Fancy Word That Matters When Payments Double-Charge

I only took idempotency seriously after one payment callback charged a user twice. In development everything looked clean. In production, retries and timeouts are normal traffic.

· · 9 min read

I did not learn to respect idempotency from theory. I learned it from a double charge. One payment callback arrived twice, the backend processed both, and suddenly a concept that sounded academic became painfully expensive.

Production systems live with retries, duplicate webhooks, flaky mobile networks, and clients that click twice. That means the same request being processed more than once is normal. If your backend treats every repeated request as new, it will eventually create duplicate side effects.

The human version of idempotency

If the same request arrives again, your system should not panic and should not create a new side effect. For payment APIs, that usually means using an idempotency key. The client sends a unique key, the server stores the result, and repeated requests with the same key return the previous response instead of charging again.

Why payment exposes the problem fastest

A common failure path is simple: the backend successfully charges the provider, but the client times out before receiving the response. The client retries. Without idempotency, the backend charges again. Webhooks have the same issue because many providers guarantee at least once delivery, not exactly once.

The practical pattern

Use an idempotency key, store it with a request hash, save the response body, and reject replays that reuse the key with a different payload. A key alone is not enough; otherwise the same key could be replayed with a different amount or order.

Common misconceptions

• Unique constraints help, but they do not protect external side effects like payment capture or email sending.
• HTTP method semantics are not enough; your code can still create duplicate side effects.
• Idempotency has a cost, but that cost is much cheaper than refunds and support incidents.

Recommended reading

• Stripe Docs - Idempotent requests
• PayPal Docs - PayPal-Request-Id
• AWS Builders' Library - Making retries safe with idempotent APIs
• RFC 9110 - HTTP Semantics

The real lesson is simple: duplicate requests are not weird. They are part of distributed systems. The question is whether your backend remembers it has already done the work.

---

*Written after one duplicated payment callback turned a quiet afternoon into log tracing, manual refunds, and an avoidable support mess.*