Payments in mobile apps in India: UPI, cards and wallets
What should you know about mobile payments integration in India across UPI, cards and wallets?
Integrate UPI intent and collect flows, cards and wallets through a gateway, with webhooks and reconciliation on the backend. The app collects the payment attempt and shows status; the backend creates orders, verifies webhooks, decides success and handles refunds. Getting that split right is most of the work.
Mobile payments integration in India is a backend problem with a mobile front end. The app's job is small: create a payment attempt through the backend, hand the user to UPI, a card form or a wallet via the gateway SDK, and show whatever status the backend confirms. The backend's job is large: create the order, verify the signed webhook, decide whether the payment succeeded, reconcile against the gateway's statements, and process refunds. Teams that put the logic in the app end up trusting a client-side callback that can be spoofed, lost or wrong. This article walks through the rails, the React Native integration, the backend flow and the failure cases you must handle before launch.
Why the split between app and backend matters
A payment can succeed after the app shows a failure (the UPI app returned before the bank confirmed), fail after the app shows a success (a callback with a stale status), or complete while the app is closed. Only the gateway's verified webhook or status API knows the truth. So the app never marks an order paid; the backend does, and the app subscribes to the order's state. This single rule removes a whole category of disputes and makes the integration testable, because the backend can be exercised without a phone in hand.
The rails and how they behave in a mobile app
| Rail | Mobile flow | Where it works best | Failure case to design for |
|---|---|---|---|
| UPI intent | App lists installed UPI apps; user picks one; approves; returns to your app | Android, most consumer purchases | User does not return to the app; result arrives by webhook only |
| UPI collect | User enters a UPI ID; a request appears in their UPI app; they approve within the expiry | iOS, web, recurring collections | Request expires or is ignored; show a countdown and a retry |
| UPI QR | App shows a QR code the customer scans with their own phone | Field collections, counters, delivery | Screenshot confusion; confirm only via backend webhook |
| UPI Autopay | One-time mandate setup; later debits happen without the user | Subscriptions, EMIs, recurring services | Mandate declined or revoked; needs its own notification and retry flow |
| Cards | Gateway's hosted or native card form, tokenisation, bank authentication step | Larger amounts, international customers | Authentication redirect fails mid-way; treat as pending and poll |
| Wallets and pay-later | Redirect to provider, return with status | Users who prefer them; promotions | Provider outage; offer another rail immediately |
Razorpay React Native and the gateway SDK
Most Indian gateways provide a React Native SDK or a native module for Android and iOS that opens their checkout, handles UPI intent, cards, net banking and wallets, and returns a result to the app. Razorpay's developer documentation is representative: the app receives an order identifier created by your backend, opens checkout, and gets back a payment identifier and a signature. The app forwards that result to the backend for verification, but the backend still waits for the webhook before marking the order paid. Keep the SDK behind a thin wrapper in the app so a second gateway can be added without touching screens, and pin the SDK version, because store review and OS upgrades interact with payment SDKs more than with most libraries.
What the app must handle
- A pending state with a clear message and a way back to the order, for every rail
- Return-to-app after UPI intent, including when the OS killed the app in the background
- Deep links from payment status notifications into the order screen
- Retry with a different rail without creating a duplicate order
- A receipt screen fed by the backend's confirmed state, not the SDK's callback
- Accessibility and the small-screen layout of the checkout, tested on low-end Android
The backend flow, step by step
Create an order in your database with a client-generated idempotency key, then create the gateway order and store its identifier. Return both to the app. When the app reports a result, verify the signature and record it as a claim, not a fact. When the webhook arrives, verify its signature, deduplicate by event identifier, and apply the state change: paid, failed, or pending. For payments pending beyond a few minutes, a scheduled job polls the status API. Only a verified paid state triggers fulfilment, notifications and the ledger entry. The ledger and reconciliation design behind this is the same one described in wallets, UPI and payments in super apps.
Webhooks: verify, deduplicate, and never trust order
Webhooks arrive out of order, more than once, and occasionally not at all. The handler verifies the signature with the gateway's secret, checks the event identifier against a store of processed events, and applies the change through the same state machine the polling job uses, so both paths produce identical results. Respond quickly and process asynchronously; a slow handler causes the gateway to retry and the duplicates multiply. Log every event with its outcome, because the reconciliation job will need to explain gaps. The NPCI documents the UPI transaction states that your state machine must map to.
Refunds and partial refunds
Refunds go through the gateway's refund API, are recorded as their own ledger entries linked to the original payment, and have their own webhook events that must be handled the same way. Partial refunds are common in commerce and services; represent them as multiple refund records against one payment rather than editing the payment. Show the customer the refund status and the expected timeline in the app, because "where is my refund" is the most common payment support ticket, and a status screen answers most of them.
Reconciliation on the backend
Every day, a job downloads the gateway's settlement and transaction reports and matches them against your orders and ledger. Mismatches fall into three groups: payments the gateway recorded that you did not (a missed webhook and an unhappy customer), payments you recorded that the gateway did not (a spoofed or mistaken callback), and fee or tax differences. Each becomes a task for finance with evidence attached. Build this in the first release, not after the first month's accounts fail to balance.
Compliance and security basics
Never handle raw card numbers in your app or backend; use the gateway's tokenisation so that card data stays within their PCI scope. Store only the identifiers and the last four digits the gateway returns. Keep gateway secrets on the backend, never in the app bundle. Card-on-file requires network tokenisation, which the gateway handles if you use its flows. The Reserve Bank of India directions on payment aggregators, tokenisation and recurring payments set the rules your gateway and your flows must follow, and they change; someone reads the updates.
A worked example
A D2C brand's React Native app had checkout built directly on the gateway callback: the app marked orders paid when the SDK returned success, and support handled the cases where the bank later disagreed. We moved the decision to the backend: verified webhooks, an idempotent state machine, a status poll for pending payments, and a daily reconciliation report. In the app, a pending screen replaced the false success, UPI intent got a proper return-to-app path, and refunds gained a status screen. Payment links for WhatsApp orders used the same backend flow. Support tickets about payment status and refunds dropped to the genuinely unusual, and finance stopped finding unexplained differences at month end. The personalisation and WhatsApp agent case study covers the wider build.
Team and timeline
Payments in a mobile app is typically a mobile engineer for the SDK integration and states, a backend engineer for orders, webhooks, refunds and reconciliation, and a QA engineer who spends most of the time on failure cases across rails, over four to six weeks. It is included in our React Native mobile development service from $17,500 / ₹11.2L, or delivered as a standalone API development and integrations project from $7,000 / ₹4.4L when the app exists. Gateway changes and regulatory updates are handled under an Essential Care Plan at $1,000 / ₹68,000 a month. See the pricing page.
Before you start: a checklist
- Choose the rails per use case: intent, collect, QR, Autopay, cards, wallets
- Select a gateway with a maintained React Native SDK, webhooks and refund APIs
- Design the order state machine with paid, failed and pending, and fulfil only on paid
- Implement webhook signature verification and event deduplication
- Add a status poll for pending payments and a return-to-app path for UPI intent
- Build refunds and partial refunds with their own records and a customer-facing status
- Schedule daily reconciliation against gateway reports from the first release
- Keep secrets on the backend and card data inside the gateway's tokenised flows
Glossary
- UPI intent: launching the user's UPI app with a prefilled payment request
- UPI collect: a payment request pushed to a UPI ID for approval within an expiry
- Autopay: a UPI mandate allowing recurring debits within agreed limits
- Webhook: a signed server-to-server message from the gateway about a payment event
- Idempotency key: a unique value that makes a repeated request a safe no-op
- Tokenisation: replacing card details with a gateway-issued token so your systems never hold them
Related reading
Read wallets, UPI and payments in super apps for the ledger and multi-gateway design, field service apps: what technicians actually need for QR and cash collection on site, and fraud detection in digital payments for what to build once volumes grow.
Let the app collect and display, let the backend decide and reconcile, and mobile payments in India become a dependable subsystem rather than a source of tickets.
Frequently asked questions
How do we integrate UPI in a React Native app?
▾
Through the gateway's React Native SDK for intent, collect and QR flows, with the backend creating orders and confirming success only from verified webhooks or status polls. See our React Native service.
Should the app or the backend decide if a payment succeeded?
▾
Always the backend, from a verified webhook or a status API call. The app shows the backend's confirmed state; SDK callbacks are treated as claims to be verified.
How long does mobile payments integration take?
▾
Four to six weeks for rails, webhooks, refunds and reconciliation, most of it on failure cases. Included in React Native builds or from $7,000 / ₹4.4L standalone; see the pricing page.