Apps·5 min read

UPI Payment Links: How Apps Hand Off a Payment to You

Tap Pay in a bill-splitting app, on a shop's checkout page or on a printed counter QR, and a UPI app opens with the payee and amount already filled in. The app you started in does not run the payment. It builds a link that begins with upi://pay and passes it to the UPI app on your phone, which does the rest.

This post walks through that link using NPCI's UPI linking specification (version 1.6, a November 2017 draft, read through a copy hosted on labnol.org) and Android's developer documentation. Where they say nothing, for example how iOS picks an app, this post stays silent too. Real apps may support more than the 2017 draft, so treat it as a baseline.

What a UPI payment link looks like

The specification defines one format: upi://pay followed by name=value pairs joined with &. It also says every PSP app (the bank or payment apps that run UPI for users) must listen for these links, whether they arrive by QR, intent, NFC or another route.

Example (the payee is made up): upi://pay?pa=demo.cafe@examplebank&pn=Demo%20Cafe&am=250.00&cu=INR&tn=Lunch%20split

Read it left to right. pa is the payee's UPI address and pn is the payee's name; the specification marks both as mandatory. am is the amount in decimal format. cu is the currency, and the draft says INR is the only supported value. tn is a short note describing the payment. The %20 is a space, because the specification asks for spaces to be percent-encoded rather than left bare.

The other parameters you may meet

Merchants add more. tr is a transaction reference such as an order number, bill ID or booking ID, and it is mandatory for merchant transactions and for dynamically generated links. mam sets a minimum amount; the specification says the amount field should not be editable when mam is absent or null. url points to bill or order details and must begin with http or https. mode records how the request began (00 default, 01 QR code, 04 intent), and sign carries a Base64 digital signature.

The specification also separates static from dynamic. A small shop can print a static QR holding only the payee address and name, and the customer types the amount after scanning. A dynamic QR, generated per bill, carries the amount and reference too.

The signature matters for safety. The draft says an unsigned request should trigger a warning that the source of the intent could not be verified, and a tampered one should be declined.

How the hand-off works

The specification lists four ways to deliver a link: a deep link on the same phone, a QR code the customer scans, a local transfer such as NFC, or a shared link sent by chat or email. Its first example has a video app create the link and launch an Android intent. The UPI app opens at its pay screen with the values filled in, and the user checks them and presses pay.

On Android, that launch is an implicit intent. Android's documentation says the system compares the intent with the intent filters declared by other apps. If one app matches, it opens immediately. If several match, the system shows a dialog so you can pick one. If none match, the calling app can catch an ActivityNotFoundException. So the list of apps you see is simply the UPI apps installed on your phone that registered for these links. On Android 13 and later, the receiving app must declare a matching intent filter to receive another app's intent at all.

The calling app assembled text and launched an intent. The pay screen, the credentials and the payment all sit in your UPI app, so an app that only hands off never holds your money. It sends a request; your own app makes the payment.

Launching a payment is not confirming it

Starting the UPI app tells the calling app nothing about the outcome. Android does let one app start another and receive a result back, with codes such as RESULT_OK or RESULT_CANCELED, but its documentation describes the mechanism, not what a UPI app returns.

The specification is cautious here. It says that as standard practice a merchant app must check the final status with its own server or PSP server. It also recommends data the bank or PSP app can return: a transaction ID, a response code, a status of SUBMITTED, SUCCESS or FAILURE, and the merchant's reference. That is a recommendation, and the bank app may need to whitelist the merchant app's URL first.

Hence apps that ask you to confirm. Tabful, a bill-splitting app from ARTH, is free on Android and says it opens your UPI app with the amount filled in, but you confirm whether the payment went through. Without a server to query, the person who saw the result in their UPI app is the best source.

A short safety checklist

Before you press pay, check the payee name on the UPI app's own screen against the person or shop you meant to pay. The specification's examples all rely on the payer verifying the details on that screen first. Check the amount and note too, and on a static QR check the amount you typed.

A link in a chat or email is only as trustworthy as its sender, since the specification lists sharing links that way. If your UPI app warns that the source could not be verified, stop and ask the sender through another channel.

Never share your UPI PIN or an OTP with anyone, including someone claiming to be from a bank, a shop or a support team. Enter your PIN only inside your own UPI app. If anyone says you must enter it, or scan a code, in order to receive money, stop and check with your bank before doing anything.

Sources and further reading

Frequently asked questions

What does upi://pay mean?

It is the link format defined in NPCI's UPI linking specification. The part after the question mark carries details such as the payee address (pa), payee name (pn), amount (am), currency (cu) and a short note (tn). A UPI app that is registered for these links opens a pay screen with the values filled in.

Can the app that opened my UPI app see whether I paid?

Not automatically. The specification recommends that the bank or PSP app return a status and transaction details, but it is only a recommendation, and it says a merchant app should check the final status with its own server or PSP server. An app without a server often asks you to confirm instead.

Does a UPI QR code hold my bank details?

The specification describes a QR code as the same upi://pay link turned into an image. It carries the payee's details, such as address, name and sometimes an amount and reference. A static QR carries no amount, so you enter it after scanning.

Why does Android show me a list of apps when I pay?

Android's documentation says that when several apps can handle an intent, the system shows a dialog so you can choose. In this case the apps listed are the ones on your phone that registered to handle UPI links.

Can a UPI link use a currency other than rupees?

The 2017 draft specification says INR is currently the only supported value for the currency parameter, cu.

Have something worth building?

Tell us what you're trying to make happen. We'll reply with a plan, not a brochure.

Start a project