QR — UPI QR Generation
Generates a scannable UPI QR code — static (payer types their own amount) or dynamic (fixed amount baked into the code) from a single endpoint, switching on whether you send amount.
This page was rewritten against the live backend
The previous version of this page documented POST /api/gateway with operation: "generateQr" / "checkQrStatus" — an endpoint and payload shape that don't exist anywhere in this codebase. What follows is read directly from paymentSystem/src/modules/interface/isg/routes/isg.txt.js and isg.txn.controller.js#ISGgenerateQR.
Flow
merchantId is resolved from the JWT before this flow starts — it's never a body field for this route.
Endpoint
POST /upi/txn/v1/GenerateQR
Content-Type: application/json
Authorization: Bearer <merchant JWT>
| Field | Required | Effect |
|---|---|---|
amount | No | Present/truthy → Dynamic QR (fixed amount, signed into the payload sent to ISG). Omitted or 0 → Static QR (payer enters their own amount at pay time). |
order_id | No | Your own reference to correlate with your order records. Omitted → the backend auto-generates a retailer reference number for you. |
merchantId is resolved from the JWT — it is not a body field for this route.
Dynamic QR — amount supplied
{ "amount": 250, "order_id": "ORD-12345" }
{
"statusCode": "00",
"message": "success",
"data": {
"qrString": "upi://pay?pa=merchant@bucksbox&am=250...",
"qr_image": "data:image/png;base64,...",
"txn_ref_no": "...",
"order_id": "ORD-12345"
}
}
Static QR — amount omitted
{ "order_id": "ORD-12346" }
{
"statusCode": "00",
"message": "success",
"data": {
"qrString": "upi://pay?pa=merchant@bucksbox...",
"qr_image": "data:image/png;base64,...",
"txn_ref_no": "...",
"order_id": "ORD-12346"
}
}
Same response shape either way — the only functional difference is whether qrString's UPI deep-link carries an am= (amount) parameter, which is what makes the scanning app prompt for an amount or not.
Failure response
A non-"00" statusCode ("03"/"04") comes back with an empty data object — most commonly "no active ISG terminal found for this merchant" if the merchant's UPI onboarding isn't complete.
{ "statusCode": "04", "message": "No active ISG UPI terminal found for this merchant", "data": {} }
Access control
checkRole("merchant") only — there is no "vendor" entry the way AEPS and DMT have. A vendor's apiKey call gets rejected before it reaches this handler at all, regardless of how the request is transported. See Vendor UPI for what that means in practice.
A separate, older QR pair also exists
paymentSystem's merchant-payments module (src/modules/merchant/routes/payments.routes.js, mounted at /merchant/payment) has its own GET /merchant/payment/GenerateQR (static only, returns the terminal's fixed qrCodeString) and POST /merchant/payment/DynamicQR (creates a payment order first, then generates the code). These predate and duplicate part of what /upi/txn/v1/GenerateQR does, and are unrelated to it — don't mix the two up. This page documents /upi/txn/v1/GenerateQR specifically, per the current integration guidance; if you find code still calling the /merchant/payment/* pair, treat it as the legacy path.
Related — Collection, SMS Pay, status & refunds
For pushing a collect request straight to a customer's VPA, an SMS pay link, status polling, or refunds (as opposed to generating a QR code), see UPI — Collection, SMS Pay, Status & Refunds.