developer
Recovering From a UPI Create-Order Network Timeout
Handle an unknown create-order result with stable idempotency, backend lookup, bounded retry, customer-safe status, and no duplicate payment attempt.
A network timeout answers only one question: the caller did not receive a complete response in time. It does not prove whether the payment order was created. Treating timeout as failure and immediately generating a new reference can produce two valid attempts for one purchase.
The recovery design starts before the request is sent.
Recognize an Unknown Outcome
A timeout can occur:
- Before the request reaches your backend.
- After your backend creates the local order but before calling the gateway.
- After the gateway accepts the attempt but before its response reaches you.
- After your backend commits the result but before the browser receives it.
- During a proxy or client retry that duplicates the request.
These cases look similar to the browser but need one common rule: preserve the original identity and query durable state.
Log a safe request ID, merchant order reference, client_txn_id, stage, duration, and application version. Do not log API keys, secrets, or complete provider credentials.
Create a Stable Idempotency Boundary
Before the external call:
- Calculate price on the server.
- Generate one
client_txn_idfor the payment attempt. - Insert the pending local order with a database uniqueness constraint.
- Store expected amount and request status.
- Call the create-order API using that identity.
When the same request arrives again, return or resume the existing attempt instead of inserting another. Serialize concurrent updates with a lock or compare-and-set state transition.
The Dynamic UPI QR API guide shows the create flow, while the PostgreSQL idempotency article covers event-side uniqueness.
Reconcile Before Replacing
After timeout, inspect the local record. If it contains a gateway order ID and customer-safe payment data, return that existing result. If the external outcome remains unknown, retry the create request with the same supported identity or use the authenticated lookup/status capability for that known order.
Use bounded retries with increasing delay and jitter. Do not run an endless tight loop. After the interactive window, move the order into a scheduled reconciliation queue with an owner and next-check time.
Do not create a replacement until the original is resolved or explicitly closed. Keep a relationship between original and replacement attempts so a late event cannot produce duplicate fulfilment.
Give the Customer an Honest State
Show “We are confirming your payment order” rather than “Payment failed.” Display the customer-safe order reference and tell the customer not to pay again while the result is unknown.
Offer:
- A status refresh for the same order
- A resume link that reveals no secret
- A support case after a documented wait
- A new attempt only after backend approval
If the original result appears later, update the existing page. Do not require the same browser session for signed event processing or reconciliation.
Test the Commit Boundary
Inject failures before local insert, after local insert, before external response, after external success, and after final database commit. Drop the response to the client at each stage, then retry.
For every test, assert one client_txn_id, at most one active gateway attempt, no paid-state downgrade, and one fulfilment effect. Also test two application instances receiving the retry simultaneously.
Monitor create timeouts, successful same-ID recovery, unresolved attempts, replacements created, and late success after replacement. The best timeout handler is not the fastest retry; it is the one that preserves financial identity under uncertainty.
Create an operational record when recovery exceeds the interactive window. Include tenant, safe order reference, client_txn_id, last completed stage, next retry time, attempt count, owner, and final resolution. This turns an unknown result into a bounded queue instead of an invisible abandoned order.
Alert on sudden timeout increases by application version, provider connection, endpoint, and region. Compare gateway latency with your own database and proxy timings before assigning cause. Keep the customer message neutral until evidence identifies the failing boundary.
Document the maximum retry window and who can close an unresolved attempt. The decision should not be left to a browser timer or an unreviewed support action.
Direct answers
Frequently asked questions
- Does a create-order timeout mean the gateway rejected the order?
- No. The request may have failed before arrival, succeeded with a lost response, or still be processing. Treat the result as unknown until reconciled.
- Should a retry use a new client transaction ID?
- No. Retry an uncertain create request with the original stable identity so the backend and gateway can return or reconcile the same attempt.
- When is it safe to create a replacement payment attempt?
- Only after the original result is resolved or formally closed through the backend policy, with a linked replacement reference and a check for late success.
Build your payment flow
Explore the API and browser-only merchant tools.
Create UPI checkout orders, verify signed events, or test the free calculators and generators without exposing credentials.