growth

UPI Checkout Loading-State Design That Prevents Duplicate Orders

Design create-order, app-launch, verification, and recovery loading states that explain progress, block unsafe repeated actions, and preserve one payment attempt.

VE VyaparGateway Engineering Payments Product & Engineering 4 min read
UPI Checkout Loading-State Design That Prevents Duplicate Orders guide
UPI checkout loading state duplicate order prevention payment progress UX checkout button state

“Loading” is not one payment state. A checkout can be calculating price, creating a local order, requesting a gateway attempt, opening a payer app, waiting for authenticated payment evidence, or recovering an unknown network result. One generic spinner hides these distinctions and encourages customers to tap again.

Design progress around what the system knows and what action remains safe.

Model Loading as Real States

Use explicit frontend states backed by server state where relevant:

UI stateMeaningAllowed action
Preparing orderTrusted backend is pricing and creatingCancel navigation if safe; no second create
Payment readyQR or Intent is availableStart payment
Opening UPI appExternal navigation requestedOffer fallback after a bounded wait
Checking paymentCustomer may have acted; backend is verifyingRefresh known order, do not create another
Result unknownNetwork outcome is uncertainReconcile original reference
VerifiedAuthenticated evidence matchedContinue to fulfilment or receipt
Review requiredEvidence is missing or mismatchedShow support reference

Render the state as readable text, not only animation. A spinner needs a label and must not be announced continuously to assistive technology.

Protect Order Creation

Disable the create action synchronously on the first activation. Generate a stable backend client_txn_id, save the local order under a uniqueness constraint, and reuse that identity if the network request is retried.

Do not rely on disabled UI alone. Two tabs, two devices, a slow double tap, or a browser retry can still reach the backend concurrently. The server must make duplicate submissions return the existing attempt or a controlled conflict.

Keep the amount server-owned. If the cart changes while the request is in progress, finish or cancel the original attempt through the state machine before creating another priced order.

Separate Launch From Verification

After the payment surface is ready, “Pay with UPI” may open an external app. That action is navigation. Page blur, visibility change, a redirect, or the customer returning does not prove success.

Use copy such as:

  • “Opening your UPI app…” during the launch request.
  • “Could not open an app—try another way” when navigation fails.
  • “Checking payment—do not pay again” after return.
  • “Payment verified” only after backend confirmation.

The mobile Intent fallback guide covers the recovery ladder.

Recover From Slow and Unknown Results

Set a UI timeout for communication, not for declaring the financial result. When create-order or verification takes longer than expected, preserve the order reference and ask the backend for its current state.

Provide an exact next action: continue waiting, retry the same request, switch payment surface on the same attempt, open a support case, or create a replacement after the first attempt is formally closed. Avoid “Something went wrong” when the result is actually unknown.

If the customer closes the page, a signed event or scheduled reconciliation should still resolve the backend order. The browser cannot be the only component responsible for completion.

Test Repeated Actions

Test rapid double click, Enter plus click, two tabs, back/forward navigation, mobile app return, slow API response, response lost after backend commit, and page reload during each state.

Assert one local order, one active client_txn_id, one final payment transition, and one fulfilment effect. Verify focus moves to a useful status only when necessary and that disabled controls explain why they are unavailable.

Measure repeated-action attempts, create-order duplicates prevented, time in each state, unresolved results, and verified conversion. Faster animation is not the goal; a clear, duplicate-safe payment journey is.

Use a copy matrix so the same state has the same meaning across mobile, desktop, support, and analytics:

Backend statePrimary copyUnsafe copy to avoid
Creating“Preparing your payment”“Payment processing”
Pending“Checking payment”“Almost successful”
Unknown“Confirming this order”“Try paying again”
Review“Payment needs review”“Payment lost”
Paid“Payment verified”“App returned successfully”

Review translations against these meanings. A shorter translated label must not turn uncertainty into failure or verification into a browser-only success signal.

Direct answers

Frequently asked questions

Should the pay button remain disabled after a request timeout?
Do not leave it disabled forever or immediately create another order. Show an unknown-result state while the backend reconciles the original reference, then provide the correct next action.
Does returning from a UPI app end the loading state?
It ends app navigation, not payment verification. Show a distinct checking-payment state until authenticated backend evidence resolves the order.
How can repeated taps be made safe?
Disable the action immediately, create the order with a stable client transaction ID, enforce database uniqueness, and make retries reuse that same identity.

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.