Pro UPI QR

Developer Toolkit

Embed a UPI QR, or run a checkout session

Three layers. The widget and upi://pay link are free and need no key. Hosted checkout needs a dashboard API key. This is not NPCI’s switch API and not a Razorpay replacement.

Using Claude or another AI assistant? Add our free MCP server and ask it for UPI QR codes, payment links, error-code help and GST in plain words.

Widget configurator

No API key. The iframe loads /embed/ with your VPA. Copy the snippet or the URL.

Overlay Logo Badge
Theme Accent Color
#287a57
<iframe 
  src="https://www.proupiqr.in/embed/?pa=merchant%40upi&pn=Merchant+Store&am=500&tn=Invoice+%231024&theme=287a57" 
  width="340" 
  height="480" 
  style="border:none; background:transparent; overflow:hidden;" 
  title="UPI Payment QR Widget"
></iframe>

Live Sandbox Preview

Scan from PhonePe, GPay, Paytm, or BHIM. Amount ₹500 to Merchant Store. This preview does not confirm a bank credit.

Merchant API key required

Hosted Checkout Sessions

Create a 24-hour checkout with an exact INR amount and public HTTPS return URL. Reusing the same order_id is idempotent. A customer-submitted UTR remains verification_pending until your server checks its bank records and confirms the session.

Get an API key

  1. Create an account.
  2. Open the merchant dashboard and copy puqi_live_….
  3. Set the shop VPA on that profile. Checkout uses the saved VPA, not a VPA in the POST body.
  4. Call the API only from your server: Authorization: Bearer puqi_live_….

curl · create session

curl -sS -X POST https://www.proupiqr.in/api/v1/checkout-sessions/ \
-H "Authorization: Bearer puqi_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"order_id":"ord_1001","amount":250.50,"currency":"INR","redirect_url":"https://example.com/thanks"}'

1. Create

POST /api/v1/checkout-sessions/
Authorization: Bearer puqi_live_...

2. Inspect

GET /api/v1/checkout-sessions/:id/
Authorization: Bearer puqi_live_...

3. Confirm

PATCH /api/v1/checkout-sessions/:id/
{ "status": "paid", "confirmed": true }

Embed widget query parameters

Point an iframe at /embed/?pa=…&pn=…. This is a widget, not an open redirect. There is no public endpoint that 302s to an arbitrary URL.

https://www.proupiqr.in/embed/?pa=shop@oksbi&pn=My%20Shop&am=250&tn=INV-1&theme=287a57

ParameterTypeDescriptionExample
paString (Required)Your registered merchant/personal UPI Virtual Payment Address (VPA)payee@bank
pnString (Required)The display name of the payee/businessMerchant Store
amNumber (Optional)Requested transaction amount. (Locks the amount field on scanning apps)250.50
tnString (Optional)Transaction note or reference ID (shows up on the customer's payment statement)Order-8390
themeString (Optional)Custom hex color (without # prefix) to theme the payment widget buttonef4444
logoString (Optional)Brand overlay logo. Options: phonepe, gpay, paytm, bhimphonepe

Backend-only verification

Payment Response API

This API is for your server, not for the customer's browser. The browser can open a checkout screen or UPI link, but only your backend can receive and trust the final payment result from a verified gateway webhook or secure relay.

Simple client flow

  1. 1. Your app creates a payment request or shows a checkout screen.
  2. 2. The customer pays in their UPI app.
  3. 3. Your backend stores the verified result and your UI polls for status.

1. Receive the verified gateway result

Configure your gateway webhook to reach your server first. Your backend verifies the provider signature, uses a globally unique order ID, normalizes the result, and then calls this endpoint server-to-server.

POST /api/payment-response/
Content-Type: application/json
x-pro-upi-timestamp: 1787832000000
x-pro-upi-signature: sha256=...

{ "orderId": "order_8390", "status": "SUCCESS", "transactionId": "UTR…", "amountPaise": 25000, "provider": "your-gateway" }

2. Read the saved response

Your frontend can show a payment pending screen while your backend polls this endpoint or updates your order database. The result is retained for 90 days in Vercel KV.

GET /api/payment-response/?orderId=order_8390
x-pro-upi-timestamp: 1787832000000
x-pro-upi-signature: sha256=...

{ "payment": { "status": "SUCCESS", "transactionId": "UTR…", "receivedAt": "…" } }
Backend requirement: set PAYMENT_WEBHOOK_SECRET, KV_REST_API_URL, and KV_REST_API_TOKEN in Vercel. Sign POST as POST.timestamp.rawBody and GET as GET.timestamp.sortedQuery. The secret is only for server-to-server calls. Keep it out of browser JavaScript and embedded widgets. This response store is separate from hosted checkout sessions and does not change their status automatically.

Native UPI Intent Link

Open the standard UPI URI directly from your mobile checkout. Pro UPI QR does not provide an unrestricted redirect endpoint for arbitrary destinations.

upi://pay?pa=merchant@upi&pn=Pro%20UPI%20QR&am=250.00&cu=INR

Encode parameter values and verify the payee and amount in the UPI app before authorizing payment.

No gateway cut on the widget

The iframe and upi://pay link are not a payment gateway. Money goes bank-to-bank. That is not the same as “always 0% MDR”: from 15 Oct 2026 some P2M UPI above ₹2,000 may take 0.4% (cap ₹300). P2P, tickets of ₹2,000 or less, and small QR (P2PM) stay free.

✓ No API key for the widget✓ No SDK to install

Open-source utility

UPI URI Parser & Builder

A single 4 kB TypeScript module that both constructs valid upi://pay?... links from typed fields and parses them back into structured data. Copy it into any JavaScript, React, Next.js, or Node project. Zero dependencies. Based on NPCI UPI Linking Specification v1.6.

Build a UPI URI

import { buildUpiUri } from './lib/upi-uri';

const uri = buildUpiUri({
pa: 'shop@upi',
pn: 'My Shop',
am: '250.50'
});
// "upi://pay?pa=shop%40upi&pn=My%20Shop&am=250.50&cu=INR"

Parse a UPI URI

import { parseUpiUri } from './lib/upi-uri';

const r = parseUpiUri('upi://pay?...');
r.pa // "shop@upi"
r.pn // "My Shop"
r.amountPaise // 25050
r.valid // true

buildUpiUri(fields)

Accepts { pa, pn, am?, tn?, cu?, mc?, tr?, ref? }. Returns an encoded UPI URI string. Omits optional fields when empty.

parseUpiUri(uri)

Returns { pa, pn, am, tn, cu, amountPaise, amountNumeric, valid }. amountPaise is integer paise or null.

isValidUpiUri(uri)

Returns boolean. Validates VPA format (regex), payee name presence, amount decimal places, and char safety.

Source: Copy /src/lib/upi-uri.ts from the site source. No npm package yet. The parser uses URLSearchParams, which is available in browsers, Node 18+, React Native, and Android/iOS webviews. Full source is ~120 lines, MIT-licensed.

Platform Integration Examples

Copy-paste ready code for four platforms. All examples use the free UPI intent — no API key, no SDK, no server.

React 18+TypeScript

React Component

import React from 'react'; function UpiPayButton({ vpa, name, amount }: { vpa: string; name: string; amount?: number }) { const pay = () => { const params = new URLSearchParams({ pa: vpa, pn: name, cu: "INR" }); if (amount) params.set("am", amount.toFixed(2)); window.location.href = `upi://pay?${params}`; }; return ( <button onClick={pay} className="rounded-[10px] bg-emerald-800 px-6 py-3 text-white font-bold hover:bg-emerald-700"> Pay ₹{amount ? amount.toFixed(2) : "..."} </button> ); } export default UpiPayButton;
Next.js 14+App Router

Next.js Server + Client

// app/api/upi-link/route.ts — Server Component import { NextResponse } from 'next/server'; export async function GET(req: Request) { const { searchParams } = new URL(req.url); const vpa = searchParams.get('vpa') || 'shop@upi'; const name = searchParams.get('name') || 'My Shop'; const amt = searchParams.get('amount'); const params = new URLSearchParams({ pa: vpa, pn: name, cu: 'INR' }); if (amt) params.set('am', amt); return NextResponse.redirect(`upi://pay?${params}`); } // Usage: /api/upi-link?vpa=shop@upi&name=Store&amount=250
AndroidKotlin

Android (Kotlin)

// Open a UPI payment intent in any installed UPI app fun payViaUpi(context: Context, vpa: String, name: String, amount: String?) { val uri = Uri.Builder() .scheme("upi") .authority("pay") .appendQueryParameter("pa", vpa) .appendQueryParameter("pn", name) .appendQueryParameter("cu", "INR") .apply { amount?.let { appendQueryParameter("am", it) } } .build() val intent = Intent(Intent.ACTION_VIEW, uri).apply { setPackage("com.google.android.apps.nbu.paisa.user") // GPay fallback } try { context.startActivity(intent) } catch (e: ActivityNotFoundException) { // No UPI app installed — show install prompt } }
iOSSwift

iOS (Swift)

// Open UPI payment via URL scheme (Swift) func payViaUpi(vpa: String, name: String, amount: String?) { var components = URLComponents() components.scheme = "upi" components.host = "pay" components.queryItems = [ URLQueryItem(name: "pa", value: vpa), URLQueryItem(name: "pn", value: name), URLQueryItem(name: "cu", value: "INR") ] if let amt = amount { components.queryItems?.append(URLQueryItem(name: "am", value: amt)) } guard let url = components.url else { return } if UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url) } else { // Fallback: open App Store UPI app } }

Attribution: Embedding the Pro UPI QR widget is free for any commercial or non-commercial use. Widget attribution ("Powered by Pro UPI QR") may be disabled by setting ?attribution=none on the embed URL. We ask that you do not use the widget in link schemes, keyword-stuffed footer links, or hidden embeds — this violates Google's link-spam policy and can hurt your site's search rankings.

Developer FAQ

Do I need an API key to put a UPI QR on my website?

No. The iframe embed and the upi://pay link are free and run in the browser. An API key is only for hosted checkout sessions after you sign in.

Is this the official NPCI or UPI API?

No. NPCI switch APIs are for banks and PSPs. This site exposes the public upi://pay intent, an embeddable QR widget, and optional merchant checkout sessions. Payments settle bank-to-bank; we do not hold funds.

How do I get a merchant API key?

Create an account, open the dashboard, and copy the puqi_live_ key. Send it only as Authorization: Bearer on server-side calls. Never put it in frontend JavaScript or the embed URL.

How do I know the customer actually paid?

A green tile in PhonePe is not proof. Match the 12-digit UTR in your bank SMS, or confirm a hosted checkout session from your server after you have verified the credit. The widget does not fire a payment webhook by itself.

Longer write-up: free UPI API without a payment gateway· Test a payload with the UPI QR decoder· Print a standee with the UPI QR generator.