A timeout does not tell you the payment failed. It tells you that you did not hear back. The safe response is to retry the same request with the same idempotency key, or to look the payment up and reconcile, before you create anything new. A fresh request risks charging twice.
Why a timeout is an unknown, not a failure
Your server sent a request. The provider may have received it, processed it, and sent a reply that never reached you. Or it may never have arrived. From your side the two cases look identical.
Payment provider documentation draws the same line. One provider's error handling guide separates three situations: parameters or authentication are wrong (fix the request), a payment is declined (ask the customer to act), and a connection fails or a request is still executing (retry safely with the same idempotency key and parameters). A server error that leaves the outcome unknown is a fourth case, and the guidance there is to reconcile the request before starting another operation.
What to do, in order
| What you see | Next action |
|---|---|
| Authentication or validation error | Fix the key or the request, then send again |
| Card declined | Show the decline reason and ask for another method |
| Connection dropped or request still running | Retry with the same idempotency key and same parameters |
| Server error, outcome unknown | Look up the payment, reconcile, then decide |
Steps for the unknown case:
- Generate the idempotency key before the first attempt and store it with the order, so a retry can reuse it.
- On timeout, retry with that same key and identical parameters. The provider compares parameters to the original request and returns an error if they differ.
- If retries keep failing, look up the object by the identifier you stored, or by metadata you attached, and read its real state.
- Only create a new payment after you have confirmed the first one does not exist or has failed.
- Mark the order as pending in your own system until the provider's event or lookup confirms the result.
The idempotency window matters
For that provider's API v1, saved results are kept for at least 24 hours, and keys can be pruned after that. If you reuse a key after pruning, a new request is generated. So a retry job that wakes up two days later should not assume the key still protects you. It should reconcile first.
Idempotency keys should be random enough to avoid collisions, such as a V4 UUID. The same documentation advises against sensitive values like email addresses as keys.
What to tell the customer
Do not show "payment failed" when you do not know. Show something like "we are confirming your payment", keep the cart, and disable the pay button for that order. Then send a confirmation or a clear failure once you know. The wording matters because a customer who sees "failed" will try again, and that is exactly how double charges happen.
Close the loop with webhooks and logs
Webhook events are the second signal. If the provider processed the payment, an event will usually follow even though your original call timed out. Treat the event as one input to reconciliation, not the only one. See how to handle duplicate and replayed webhook events and idempotency keys for payment APIs.
Log every attempt on your side with the order reference, the idempotency key and the outcome class. Your own logs help when the provider cannot log your request, for example when your keys were wrong. Keep card details and personal data out of those logs.
Where Safeguard fits
The payment SDK in your dependency tree is code you ship. Safeguard's SBOM and dependency scanning shows which version you run and flags known vulnerabilities, and you can try a free scan.
Quick check
Should I retry a timed out payment request?
Yes, but with the same idempotency key and identical parameters. That lets the provider return the original result instead of creating a second charge.
How long does an idempotency key stay protective?
In the provider documentation cited below, at least 24 hours for API v1, after which the key can be removed. After that window, reconcile instead of retrying.
Sources
- Error handling, Stripe documentation
- Idempotent requests, Stripe API reference
Last reviewed: 19 October 2026.