NPCI processes well over 16 billion UPI transactions in a typical month, and the published technical decline rate hovers around 0.7 to 1 percent. That number sounds comfortable until you sit with a merchant’s own dashboard, where checkout success on a bad evening reads 88 percent. The gap between the rail’s decline rate and the merchant’s success rate is almost entirely engineering, and most of it is about time.
A UPI payment is not a request and a response. It is a request, a human action on a phone that may take thirty seconds or three minutes, a bank debit, an NPCI switch hop, and a callback that arrives whenever it arrives. Teams that model it as a synchronous call ship a checkout that lies to customers.
Where The Seconds Actually Go
An intent flow on a good network completes in 8 to 15 seconds from app switch to callback. A collect request has a mandate window that can stretch to five minutes, and customers routinely open the app, get distracted, and approve at minute four. Meanwhile the PSP webhook may be delayed by seconds or by minutes during peak load, typically the 8pm to 11pm window when salary-day traffic and quick commerce orders overlap.
The failure modes that show up in production logs are consistent:
- The premature failure screen. The checkout gives up at 30 seconds, tells the customer the payment failed, and then the debit lands. The customer pays twice or calls support angry.
- The missing callback. The PSP fires the webhook once, your server is redeploying, and the order sits in
pendingforever because nothing ever asks again. - The duplicate order. A retry without an idempotency key creates a second payment object against the same cart.
- The stale mandate. A collect request expires server-side but the UI still shows a spinner, so the customer approves nothing and leaves.
Build For Reconciliation, Not For Speed
The fix is not a faster integration. It is treating the payment status as something you own and continuously converge on. The instinct that says a credit decision needs a replay button applies here too: you should be able to reconstruct, weeks later, exactly what you knew about this payment and when you knew it. Three habits do most of the work.
Poll on a decaying schedule. Fire a status check at 5, 10, 20, 40, and 90 seconds, then every 30 seconds until the mandate window closes. NPCI and PSP status APIs are cheap compared to a support ticket, and rate limits are generous enough for this pattern at SME volumes.
Make every write idempotent. One key per cart attempt, enforced with a unique index in the database rather than an application check, so that a retry storm during a network blip cannot mint two orders.
Treat the webhook as a hint, not a source of truth. When it arrives, verify against the status API before releasing goods. Signed payloads get spoofed less often than they get replayed.
What Customers Should See
Reliability that customers perceive is mostly about honest UI states. Show “waiting for your approval” with the actual remaining mandate time rather than an indefinite spinner. Distinguish “we do not know yet” from “this failed”, because a wrong failure message triggers a second payment attempt and turns one problem into a refund cycle that takes three to five working days. It is the checkout version of onboarding drop-off being a design choice rather than a compliance cost: the rules did not lose the customer, the screen did.
For a merchant doing 20,000 orders a month, moving checkout success from 88 to 95 percent is 1,400 recovered orders. None of that required a change on the rail.
The rail’s decline rate and the merchant’s success rate are two different numbers, and every point of distance between them belongs to whoever wrote the checkout. That is a difficult thing to own and a useful one, because it means the recovery is available without anybody else’s cooperation — no bank, no PSP, no NPCI. Most of the fintech payment work worth doing lives in that gap.