Developer Toolkit
Integrate Free UPI Payments On Your Website
Generate interactive client-side UPI QR widgets, payment buttons, and deep links instantly. For a real checkout flow, your app sends customers to a checkout screen or QR, then your backend records the verified payment result after the gateway confirms it.
Widget Configurator
Customize your payment widget parameters and get instant embed code below.
<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
Scanning this QR code from any standard UPI app (GPay, PhonePe, Paytm, BHIM) will request ₹500 to Merchant Store.
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.
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 }Redirection API & Query Parameters
If you want to generate payment QR codes dynamically inside your custom CRM, billing software, or ecommerce site, you can directly invoke our rendering endpoints by passing standard URL query parameters.
| Parameter | Type | Description | Example |
|---|---|---|---|
| pa | String (Required) | Your registered merchant/personal UPI Virtual Payment Address (VPA) | payee@bank |
| pn | String (Required) | The display name of the payee/business | Merchant Store |
| am | Number (Optional) | Requested transaction amount. (Locks the amount field on scanning apps) | 250.50 |
| tn | String (Optional) | Transaction note or reference ID (shows up on the customer's payment statement) | Order-8390 |
| theme | String (Optional) | Custom hex color (without # prefix) to theme the payment widget button | ef4444 |
| logo | String (Optional) | Brand overlay logo. Options: phonepe, gpay, paytm, bhim | phonepe |
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. Your app creates a payment request or shows a checkout screen.
- 2. The customer pays in their UPI app.
- 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": "…" } }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=INREncode parameter values and verify the payee and amount in the UPI app before authorizing payment.
Zero commission explained
Unlike payment gateways that charge 2% to 3% transaction fees, our widget uses native UPI link protocols. Payments flow directly from the customer's bank app to your bank account over the NPCI network, entirely bypassing any third-party gateway processor.
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 // truebuildUpiUri(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.
/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 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-full 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 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=250Android (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
}
}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.