Challenge 3: Fix a Non-Idempotent Retry Bug — Possible Solution ==================================================================== WHAT THE CLIENT NEEDS TO DO ------------------------------ Generate a unique idempotency key (typically a UUID) ONCE, before sending the initial payment request — not a new one on every attempt. Send that same key on both the original request and any automatic retry: POST /payments HTTP/1.1 Idempotency-Key: 7f3c9a10-52e1-4b8a-9c31-... Content-Type: application/json { "amount": 4999, "currency": "usd" } If the request times out and the client's retry logic fires automatically, it must reuse the SAME Idempotency-Key value on the retry — generating a fresh key per attempt would completely defeat the purpose, since the server would then treat each attempt as a genuinely new, distinct payment. WHAT THE SERVER NEEDS TO DO ------------------------------ Before processing a payment request, check whether the given Idempotency-Key has already been seen and successfully processed: - If it's a NEW key: process the payment normally, and store the key alongside the result (success or failure) for some retention window. - If it's a KEY ALREADY SEEN: don't process the payment again — instead, return the ORIGINAL result that was stored for that key, as if it had just been created (whether that was a success response or a specific error). This way, whether the client's very first request actually succeeded before the timeout, or genuinely failed and never reached the payment processor at all, the retry is now safe: it either creates exactly one payment, or safely returns the already-known outcome without ever creating a second charge. WHY THIS WORKS AS AN ANSWER ------------------------------ The root problem is that POST is not idempotent by default (Chapter 2), so retrying it naively can create duplicate side effects. The Idempotency-Key pattern doesn't change POST's fundamental nature — it layers a deduplication mechanism ON TOP of it, using a key the CLIENT controls to let the server recognize "this is the same logical request as before," which is exactly the guarantee idempotent methods get for free and POST does not.