A return URL improves a checkout experience: after using a UPI app, a customer can return to your site and see the state of their purchase. It is not a signed payment receipt. A secure payment flow treats the redirect as navigation only, then checks the original saved order from the merchant server before it unlocks anything.
- Create and save a business purchase before checkout.
- Allow only an approved return destination.
- Send the customer to the hosted checkout.
- On return, load the saved purchase by a server-controlled reference.
- Verify its exact Trust Gateway order status before fulfilment.
What a return URL does—and does not do
A redirect destination tells the browser where it may go after checkout. It can display a “checking payment” state, show an order summary, or let the buyer return to a download page once your server has confirmed payment. It does not prove that the UPI app approved a request, that funds reached the receiver, or that the returned browser belongs to the original payer.
Keep that distinction visible in your code. A browser-side parameter such as ?success=true, a checkout close event, or a customer screenshot can be helpful for user experience but must not set your local purchase to paid. The status API reference requires an authenticated server check for the payment decision.
Allowlist destinations on your server
If your app lets a merchant configure a return URL, validate it before it is saved or passed into order creation. Prefer a small allowlist of HTTPS domains owned by that merchant. Reject malformed URLs, credentials embedded in a URL, and arbitrary third-party destinations. In a multi-tenant platform, bind the allowed destination to the merchant account instead of trusting a browser-supplied value.
A public checkout should not let a visitor replace the merchant's return destination with a query parameter. That creates an open-redirect risk and can be used to send customers from a trusted checkout to an unrelated site. Trust Gateway keeps the order's return destination server-side when loading a public payment-link checkout; your integration should apply the same principle in its own application.
Save an order mapping before redirecting
When your server creates an order, store your local purchase ID, the returned Trust Gateway order_id, requested amount, exact payable amount, product and approved return path together. Use the local purchase ID in your own session or signed state, not a raw user-selectable amount. When the customer comes back, look up the saved mapping rather than constructing a new order from the URL.
This record also handles an interrupted browser journey. A customer can pay in one app, return in a different tab, or reopen the link later. Your return page can show the current server-side purchase state and continue checking the same known order. Creating a new payment request on every return increases the chance of duplicate payment attempts.
Verify before fulfilment
From your backend, call GET /api/v1/orders/status/:order_id with the merchant API key. Only when the response is authenticated, reports paid: true, and matches the stored order ID and exact payable amount should you run fulfilment. Make the fulfilment transition idempotent so a refreshed return page cannot issue a second license, refund, subscription extension, or shipment.
If the order is pending, show a clear waiting state with the business order reference. If it is expired or unresolved, keep the original record available for reconciliation rather than declaring a payment failure from the redirect alone. The payment API error-handling guide covers timeouts, 4xx responses, and safe retry choices.
Test the return flow like an attacker and a customer
- Change a return URL parameter in the browser and confirm the server ignores it.
- Return to your site without paying and confirm no product is delivered.
- Refresh the success page several times and confirm fulfilment happens once.
- Open the checkout in two tabs and confirm both map to the same purchase record.
- Use a deliberately invalid or foreign URL in settings and confirm validation rejects it.
A good return page reduces customer anxiety, but the secure outcome comes from the server-held order record. Combine it with a signed webhook if your app needs faster notification, then retain authenticated status lookup as a recovery path. Read the webhook security guide before accepting a callback as a trigger.
