Most payment code is written for two outcomes: the payment succeeded, or it failed. Real systems have a third, and it causes most of the trouble: you asked, and no answer came back. The request might never have reached the provider. It might have reached it and been declined. Or the customer might have been charged a moment after your code stopped waiting.
How you handle that third outcome decides whether a slow afternoon at a provider becomes a few delayed confirmations or a pile of double charges and unpaid orders. This guide sets out what a timeout actually tells you, how to choose timeouts deliberately, which errors are safe to retry, and a routine for resolving unknown payments.
Timeout, decline, pending and error are four different things
The first rule is to keep these apart in your code and your data. Collapsing them into "failed" is the root of most timeout bugs.
| Outcome | What you know | What to do |
|---|---|---|
| Success | The provider confirmed the payment | Fulfil the order |
| Decline | The provider or bank answered no | Show the customer a clear message; another method may work |
| Pending | The provider answered: it's waiting, often for the customer or their bank | Wait for the webhook; check status on a schedule; don't ask the customer to pay again |
| Error with a response | The provider answered with an error, such as invalid request or rate limit | Depends on the error; see the retry table below |
| Timeout or lost connection | Nothing. The outcome is unknown | Resolve the status before doing anything else |
Pending deserves a special mention because it's often mistaken for a timeout. Many payments are asynchronous by design: a UPI collect request waits for the customer to enter their PIN, a card payment may wait for 3-D Secure, and a net banking payment waits for the customer on their bank's page. That's not a fault. The provider has told you it's waiting, and its webhook will tell you how it ended.
Where in the chain a timeout happens
A payment request crosses several systems before an answer comes back, and a delay at any of them can push your call past its timeout:
- 1Your serverCreates the payment
- 2ProviderGateway or aggregator
- 3Network or switchCard network or UPI switch
- 4BankIssuing or remitter bank decides
- 5ResponseTravels back the same way
You only see the first hop. If the issuing bank is slow, your provider is still waiting when your client gives up, and the payment may still complete. That's why a timeout on your side must never be read as a failure on theirs.
Not all timeouts are equal
Your HTTP client usually distinguishes two kinds of timeout, and they mean different things:
- A connect timeout means the connection to the provider was never established. The request wasn't sent, so the payment can't have been created. Retrying is safe, ideally with the same idempotency key anyway.
- A read timeout, or a connection dropped after the request was sent, means the provider may have received and acted on the request. The outcome is unknown.
- A gateway timeout response, such as HTTP 504 from a proxy in front of the provider, means some server in the path gave up waiting for the one behind it. The request may well have been processed. Treat it like a read timeout.
If your client library can't tell you which kind happened, assume the worse one: unknown.
Set a timeout budget on purpose
Timeouts are often left at a library default, which is either far too long (requests hang and exhaust your workers) or too short (you give up on payments that would have succeeded). Set them deliberately, as a budget from the customer inwards:
- Start with how long the customer will wait on the checkout screen before you show a "still confirming" state. Everything behind it has to fit inside that.
- Set the call from your server to the provider shorter than that, so your server can answer the page in time, even if the answer is "pending".
- Use your providers' documentation for how long a create call can legitimately take, and measure it: look at the slowest few percent of response times per provider and method, not the average.
- Keep the connect timeout short. A provider that can't accept a connection quickly is usually having a wider problem.
- Never let a retry loop exceed the overall budget. Two retries of a call that already takes most of the budget will blow straight through it.
Which errors to retry
Retrying blindly turns a provider's bad minute into an outage, and retrying the wrong error creates duplicates. A sensible default by response:
| Response | Meaning | Retry? |
|---|---|---|
| 400, 401, 403, 422 | Your request or credentials are wrong | No. Fix the request; retrying won't change the answer |
| 409 | Conflict, often the same idempotency key still in progress | Later, with the same key |
| 429 | Rate limited | Yes, after the delay in Retry-After if given, with the same key |
| 503 | Provider temporarily unavailable | Yes, after a delay, with the same key |
| 500, 502, 504 | Server or gateway error; the request may have been processed | Check status first, then retry with the same key |
| Connect timeout | Request never sent | Yes, with the same key |
| Read timeout | Unknown | Check status first |
Two rules apply to every retry. Use the same idempotency key, so a repeat can't create a second payment; our guide to duplicate payments shows how keys work. And wait between attempts with exponential backoff plus random jitter, so thousands of clients don't retry at the same instant and knock the provider over again as it recovers.
Resolving an unknown payment
When the outcome is unknown, the job is to find out what happened, not to try again. Every provider worth using offers a way to fetch a payment's status, by its own ID or by the reference you sent. That's why sending your own unique reference on every create call matters: it's how you find a payment whose response you never received.
- Succeeded: mark the order paid, exactly as if the original response had arrived.
- Failed: the attempt is over. It's now safe to offer the customer another try, or to route a retry to another provider.
- Pending: wait for the webhook and keep checking on a schedule. Don't retry elsewhere; a pending payment can still succeed.
- Not found: be careful. Some providers take a moment to make a new payment searchable. Only treat "not found" as "never created" when the provider documents that, and after its stated window has passed. Otherwise keep the attempt open and let reconciliation settle it.
type Outcome = "succeeded" | "failed" | "pending" | "not_found";
// Never creates a new payment: it only asks what happened to this one.
async function resolveUnknown(attempt: Attempt): Promise<Outcome> {
const delaysMs = [2_000, 5_000, 15_000, 60_000]; // example schedule
for (const base of delaysMs) {
await sleep(base / 2 + Math.random() * (base / 2)); // backoff with jitter
try {
const found = await provider.findByReference(attempt.reference);
if (found.status !== "not_found") return found.status;
} catch (err) {
if (!isTransient(err)) throw err; // e.g. bad credentials: alert a person
}
}
// Still unknown: leave it pending for the webhook, the scheduled
// status job and reconciliation. Don't charge again.
return "not_found";
}What you do with the answer is straightforward:
Backstops: webhooks, scheduled checks and reconciliation
Status checks at request time resolve most unknowns. Three backstops catch the rest:
- Webhooks deliver the final status when it happens, including for payments whose create call timed out. Verify and process them as our webhooks guide describes.
- A scheduled job checks every attempt still pending or unknown after a set time and asks the provider for its status.
- Daily reconciliation against the provider's reports finds anything both of those missed, such as a payment that succeeded after you marked it failed. Our reconciliation guide covers the routine.
What the customer sees
A timeout is an engineering problem, but the customer experiences it as uncertainty. Show a clear pending state, tell them not to pay again, and update the page when you know. If their account was debited and the payment ultimately failed, the money comes back: RBI's turnaround-time framework requires failed transactions to be reversed within set timelines, such as T + 5 days for a card-not-present payment where the account was debited but the merchant didn't receive confirmation, with compensation for delays beyond that. Our guide to money debited but the payment failed covers the timelines by payment method.
Measure timeouts, don't just handle them
Track timeouts and unknown outcomes per provider and payment method, alongside response-time percentiles. A rising timeout rate is often the first sign a provider or bank is struggling, well before success rates fall. Our guide to payment success rate explains how to keep timeouts and pending payments from distorting that number.
If you route across several providers, these signals also drive routing decisions. LILPE's smart retry page describes checking a timed-out attempt's status on the first provider before any retry, and not retrying pending attempts; its automatic failover page describes using timeout and error rates to pause an unhealthy provider for a method.
Frequently asked questions
Does a payment gateway timeout mean the payment failed?
No. It means your system didn't get an answer in time. The payment may have failed, may still be processing, or may have succeeded. Check its status with the provider, using your reference or the provider's ID, before showing a failure or trying again.
Should I retry a payment request after a timeout?
Only with the same idempotency key, and preferably after checking the payment's status. A retry without the key, or as a brand-new payment, can charge the customer twice if the first request went through.
What timeout should I set for payment API calls?
There's no single right number. Work back from how long the customer will wait at checkout, use your providers' documented processing times, and measure the slowest few percent of real responses per provider and method. Keep connect timeouts short and make sure retries fit inside the overall budget.
What is the difference between a pending payment and a timed-out one?
Pending is an answer: the provider told you the payment is waiting, usually for the customer or their bank, and will report the result. A timeout is the absence of an answer, so you have to ask for the status yourself.
What does HTTP 429 mean from a payment API?
Too many requests: you've hit the provider's rate limit. Wait, using the Retry-After value if the response includes one, then retry with the same idempotency key. Our guide to preparing payments for sale days covers rate limits in more depth.
Go deeper
Official sources
- IETF — RFC 9110, HTTP SemanticsRetry-After (10.2.3); 500, 502, 503 and 504 status codes (15.6); idempotent methods (9.2.2).
- IETF — RFC 6585, Additional HTTP Status Codes429 Too Many Requests (4), which may include a Retry-After header.
- Amazon Builders' Library — Timeouts, retries and backoff with jitterChoosing timeouts from observed latency, retrying only safe operations, and adding jitter to backoff.
- RBI — Harmonisation of Turn Around Time (TAT) and customer compensation for failed transactions (20 Sep 2019)Card-not-present (e-commerce), account debited but confirmation not received at the merchant's system: auto-reversal within T + 5 days; ₹100 per day of delay beyond that.
Last reviewed . Examples, amounts and screens marked illustrative are not LILPE figures.




