Mobile Website Browser/Mobile App
Accept UPI payments on mobile web and in your mobile app, where the shopper is redirected to their UPI app to authorize the payment. UPI has different requirements depending on the goods you sell: for both physical and digital goods you must pass an invoice for every transaction, and for physical goods you must also pass an hs_code for each line item in order.products. For details on submitting and uploading invoices, see Invoices for UPI transactions.
Step 0. Create a customer (optional)
If the shopper is new, create a customer with the Create a Customer APIAPI and reuse the returned id (a cus_... value) as customer_id when you create the Payment Intent. This step is optional. If you already have a customer, or you prefer to pass the customer details inline, skip to Step 1.
1curl -X POST https://api.sandbox.airwallex.com/api/v1/pa/customers/create \2 -H 'Content-Type: application/json' \3 -H 'Authorization: Bearer {{ACCESS_TOKEN}}' \4 -d '{5 "request_id": "2ed2d823-f8b3-416e-86db-ced8770ce217",6 "merchant_customer_id": "9fafc2fb-4262-4ff8-a205-d60fb4781cb0",7 "first_name": "Aarav",8 "last_name": "Sharma",9 "email": "[email protected]",10 "phone_number": "+919876501234"11 }'
Response:
1{2 // ... other fields omitted.3 "id": "cus_hkdmr9ye6cxbmv8b8vg",4 "request_id": "2ed2d823-f8b3-416e-86db-ced8770ce217",5 "merchant_customer_id": "9fafc2fb-4262-4ff8-a205-d60fb4781cb0",6 "first_name": "Aarav",7 "last_name": "Sharma",8 "email": "[email protected]",9 "phone_number": "+919876501234"10}
Step 1. Initialize a Payment Intent
Create a Payment Intent with the Create a Payment Intent APIAPI. The invoice number (order.invoice.number) is mandatory: UPI payments cannot be completed without it. If the invoice has already been uploaded with the file service, also include its file_id.
Note the following UPI-specific requirements:
- Customer: pass the
customer_idreturned in Step 0, or pass the customer details inline as acustomerobject. If you do not provide the shopper's name, email, and phone number here, you must pass them at confirm asshopper_name,shopper_email, andshopper_phone. See Step 2. - Invoice:
order.invoice.numberis required for both physical and digital goods. - HS code: for merchants dealing in physical goods, you must pass an
hs_codefor each line item inorder.products, and eachhs_codemust reflect the corresponding product.hs_codeis not required for digital goods. - Shipping: for merchants dealing in physical goods, the shipping
addressis required, includingstreet,city,state,postcode, andcountry_code. - Return URL: for a native app, pass a
return_urlthat uses your app scheme, so the shopper is returned to your app after payment.
1curl -X POST https://api.sandbox.airwallex.com/api/v1/pa/payment_intents/create \2 -H 'Content-Type: application/json' \3 -H 'Authorization: Bearer {{ACCESS_TOKEN}}' \4 -d '{5 "request_id": "d917ad44-6ae4-447e-a534-cce4ceebfd2e",6 "amount": 100,7 "currency": "INR",8 "customer_id": "cus_hkdmr9ye6cxbmv8b8vg",9 "merchant_order_id": "85d7b0e0-7235-11ea-862e-9f6aa1adfca6",10 "return_url": "https://www.airwallex.com",11 "order": {12 "invoice": {13 "number": "INV-123456",14 "file_id": "{{FILE_ID}}"15 },16 "products": [17 {18 "name": "trousers",19 "hs_code": "123456"20 },21 {22 "name": "t-shirt",23 "hs_code": "456789"24 }25 ],26 "shipping": {27 "address": {28 "street": "Mantri Apartment, Koramangala",29 "city": "Bengaluru",30 "state": "Karnataka",31 "postcode": "560032",32 "country_code": "IN"33 }34 }35 }36 }'
Alternatively, if you did not create a customer in Step 0, replace customer_id with an inline customer object:
1{2 // ... other fields omitted.3 "customer": {4 "first_name": "Aarav",5 "last_name": "Sharma",6 "email": "[email protected]",7 "phone_number": "+919876501234"8 }9 // ... other fields omitted.10}
Step 2. Confirm the Payment Intent
When a shopper selects to pay with UPI on mobile, call the Confirm a Payment Intent APIAPI with the flow that matches the shopper's surface:
mobile_web(mobile browser) ormobile_app(native app): Airwallex returns a redirect URL that opens the shopper's UPI app. For a native app, pass areturn_urlthat uses your app scheme when you create the Payment Intent, so the shopper is returned to your app after payment.
If you did not provide the shopper's details when you created the Payment Intent, either through customer_id or an inline customer object, pass shopper_name, shopper_email, and shopper_phone in payment_method.upi at confirm. These fields are required when the equivalent customer details are not already on the Payment Intent, and can be omitted when they are.
Request:
1curl -X POST https://api.sandbox.airwallex.com/api/v1/pa/payment_intents/{{PAYMENT_INTENT_ID}}/confirm \2 -H 'Content-Type: application/json' \3 -H 'Authorization: Bearer {{ACCESS_TOKEN}}' \4 -d '{5 "request_id": "d917ad44-6ae4-447e-a534-cce4ceebfd2e",6 "payment_method": {7 "type": "upi",8 "upi": {9 "flow": "mobile_web",10 "shopper_name": "Aarav Sharma",11 "shopper_email": "[email protected]",12 "shopper_phone": "+919876501234"13 }14 }15 }'
Response:
1{2 // ... other fields omitted.3 "next_action": {4 "type": "redirect",5 "method": "GET",6 "url": "upi://pay?am=10.00&cu=INR&mc=5817&mode=04&pa=airwallex167065.rzp@rxaxis&pn=Airwallex&tr=Tf2qi0Z9GKvqHJ"7 }8}
Redirect the shopper on Android
On an Android phone, opening the upi://pay link natively pulls up a selection of the shopper's installed UPI apps, so the shopper can choose which app to redirect to and complete payment. You can redirect the shopper to next_action.url directly.
Redirect the shopper on iOS
On an iOS phone, the upi://pay scheme does not present an app selection, so the deep link must target a specific app. You can either have Airwallex return the app-specific link, or build it yourself from the generic link.
Have Airwallex return the app-specific link
When you confirm the Payment Intent with flow as mobile_web or mobile_app and os_type as ios, you can also pass app_name. Airwallex then returns a next_action.url that already uses that app's URI scheme, so you can open it without rewriting the prefix.
Request:
1curl -X POST https://api.sandbox.airwallex.com/api/v1/pa/payment_intents/{{PAYMENT_INTENT_ID}}/confirm \2 -H 'Content-Type: application/json' \3 -H 'Authorization: Bearer {{ACCESS_TOKEN}}' \4 -d '{5 "request_id": "d917ad44-6ae4-447e-a534-cce4ceebfd2e",6 "payment_method": {7 "type": "upi",8 "upi": {9 "flow": "mobile_web",10 "os_type": "ios",11 "app_name": "phonepe"12 }13 }14 }'
Response:
1{2 // ... other fields omitted.3 "next_action": {4 "type": "redirect",5 "method": "GET",6 "url": "phonepe://pay?am=210.00&cu=INR&mc=5817&mode=04&pa=airwallex167065.rzp@rxaxis&pn=Airwallex&tr=TrDSyfz7TQrX7m",7 "fallback_url": "upi://pay?am=210.00&cu=INR&mc=5817&mode=04&pa=airwallex167065.rzp@rxaxis&pn=Airwallex&tr=TrDSyfz7TQrX7m"8 }9}
The response also returns a fallback_url, which is the same payment request as the generic upi://pay link. Open fallback_url when the app named in app_name is not installed, so the shopper can still select another UPI app instead of reaching a link that cannot open.
If you do not pass app_name, Airwallex returns only the generic upi://pay link in next_action.url, which you must rewrite yourself before opening. See Build the app-specific link yourself.
To retrieve the UPI apps most commonly used in India, call the Get available bank namesAPI API with payment_method_type as upi and country_code as IN. Use it to populate the app list you present to the shopper, so you do not maintain the list yourself.
Request:
GET /api/v1/pa/config/banks?country_code=IN&payment_method_type=upi
Build the app-specific link yourself
If you do not pass app_name, change the upi://pay prefix in next_action.url to the app's URI scheme, keeping the query parameters unchanged. The suggested handling is to display a list of these app-specific links to the shopper, and then open the link for the app they select.
The most popular UPI-compatible apps in India are as follows:
On iOS, if you open the original upi://pay link from next_action.url without changing the prefix to an app-specific URI scheme, the link redirects the shopper to WhatsApp instead of a UPI payment app. Always replace the upi://pay prefix with the target app's URI scheme before opening the link.
| UPI app | URI scheme | Example deep link |
|---|---|---|
| PhonePe | phonepe://pay | phonepe://pay?am=10.00&cu=INR&… |
| Google Pay | tez://upi/pay | tez://upi/pay?am=10.00&cu=INR&… |
| Paytm | paytmmp://upi/pay | paytmmp://upi/pay?am=10.00&cu=INR&… |
| Navi | navi://upi/pay | navi://upi/pay?am=10.00&cu=INR&… |
| super.money | super://pay | super://pay?am=10.00&cu=INR&… |
Additional guidance for iOS:
- Declare the URI schemes you intend to open under
LSApplicationQueriesSchemesin your app'sInfo.plist. Without this,canOpenURL(_:)cannot detect whether an app is installed. - Before opening a deep link, check whether the target app is installed with
UIApplication.shared.canOpenURL(_:). Open the link withUIApplication.shared.open(_:)only for apps that are installed, and hide or disable the links for apps that are not, so the shopper is not shown an app they cannot use. - Provide a fallback for shoppers who do not have a supported UPI app installed. If you passed
app_name, open thefallback_urlreturned alongside the app-specific link. Otherwise, prompt the shopper to complete the payment on a desktop by scanning a QR code. - If you present the checkout in a
WKWebView, implementwebView(_:decidePolicyFor:decisionHandler:)to intercept non-http(s)schemes and hand the URL toUIApplication.shared.open(_:), then cancel the navigation in the web view.
Step 3. Query the payment result status
To get the payment result, we suggest you poll the status of the Payment Intent via the Retrieve a Payment Intent APIAPI. You may start polling the Payment Intent status after the shopper is redirected back to your website or mobile app, that is, the return_url passed when creating the Payment Intent.
In addition, Airwallex will notify you of the payment result asynchronously via the webhooks. Please refer to the webhook documentation to set up your webhook accordingly. Although subscribing to webhook events is optional, it is recommended to subscribe to the payment_intent.succeeded webhook which indicates that the shopper has paid the order.