UPI — Collection, SMS Pay, Status & Refunds
Broader UPI collection features beyond QR code generation: pushing a collect request straight to a customer's VPA, an SMS-based pay link, status lookups, refunds, and VPA verification. For generating a scannable Dynamic QR code specifically, see the QR page — the two are closely related (both ride the UPI rail) but are separate route groups.
Direct REST only — not in the fintech-gateway's operation map
Unlike AEPS and DMT, these endpoints have no operation entry in fintech-gateway's qr-aeps-dmt-operations.md reference — they're reachable only as direct paymentSystem REST calls with a merchant Bearer JWT, read from src/modules/merchant/routes/payments.routes.js. If you need these through the encrypted gateway envelope for a third-party integration, that's a gateway-side change to request, not something documented here as available today.
Base path & auth
POST https://<paymentSystem-host>/Collection
Authorization: Bearer <merchant JWT>
Every route below requires the merchant role specifically — see Access control.
Collection — push a collect request to a VPA
POST /Collection
Content-Type: application/json
Authorization: Bearer <token>
{
"amount": 250.00,
"order_id": "ORD-12345",
"vpa": "customer@oksbi",
"customer_mobile": "9876543210",
"customer_name": "Priya Sharma",
"customer_email": "priya@example.com",
"remarks": "Table 4 order"
}
Creates the order/customer record, then sends an ISG collect request to the given VPA (the customer approves it in their own UPI app — there's no QR involved). On success, paymentSystem records the returned ref_id, payee_virtual_address, and payer_virtual_address against the transaction. A gateway-level failure marks the underlying order FAILED immediately rather than leaving it pending.
Response:
{
"statusCode": "00",
"message": "success",
"data": {
"ref_id": "ISG-REF-...",
"payee_virtual_address": "customer@oksbi",
"payer_virtual_address": "merchant@bucksbox"
}
}
SMS Pay — send a payment link by SMS
POST /SMSpay
Content-Type: application/json
Authorization: Bearer <token>
{
"amount": 250.00,
"order_id": "ORD-12345",
"customer_number": "9876543210",
"customer_name": "Priya Sharma",
"remarks": "Table 4 order"
}
Same idea as Collection, but the customer receives an SMS with a payment link instead of an in-app collect request — no VPA needed upfront.
Response:
{ "statusCode": "00", "message": "success", "data": { "trans_id": "SMS-TXN-..." } }
Status checks
POST /UpiStatus
{ "trans_id": "..." }
{ "statusCode": "00", "message": "success", "data": { "trans_id": "...", "status": "SUCCESS", "...": "remaining fields are ISG's own status response, passed through — not independently confirmed here" } }
POST /SMSStatus
{ "trans_id": "..." }
{ "statusCode": "00", "message": "success", "data": { "trans_id": "...", "status": "SUCCESS", "...": "remaining fields are ISG's own status response, passed through — not independently confirmed here" } }
Poll these for a Collection/SMS Pay transaction the same way you'd poll QR status — trans_id is the reference you received back when you created the transaction.
Refunds
POST /RefundUPI
{ "tr": "<the original order's rrn>", "amount": 250.00 }
{ "statusCode": "00", "message": "Refund initiated successfully", "data": { "...": "ISG's own refund response, passed through — not independently confirmed here" } }
POST /RefundSMS
{ "trans_id": "..." }
{ "statusCode": "00", "message": "Refund initiated successfully", "data": { "...": "ISG's own refund response, passed through — not independently confirmed here" } }
RefundUPI refunds by your own reference (tr, the RRN from the original order); RefundSMS refunds by the gateway's trans_id instead — don't mix the two up, they key off different identifiers.
Verify a VPA before collecting
POST /verifyVPA
{ "payer_virtual_address": "customer@oksbi" }
Confirms a VPA resolves to a real account (and typically returns the account holder's name) before you push a Collection request to it — useful to catch typos before bothering the customer.
{ "statusCode": "00", "message": "success", "data": { "payer_virtual_address": "customer@oksbi", "account_holder_name": "...", "...": "remaining fields are ISG's own VPA-verification response, passed through — not independently confirmed here" } }
Response shape
All of the above return the standard envelope:
{ "statusCode": "00", "message": "...", "data": { ... } }
The data fields shown above for UpiStatus/SMSStatus/RefundUPI/ RefundSMS/verifyVPA mark exactly what's confirmed vs. ISG passthrough — log a real response the first time you call each in a new environment rather than assuming the shown shape is complete.
Access control
Every route here is checkRole("merchant") only — there is no vendor fallback the way AEPS and DMT have. A vendor cannot drive Collection, SMS Pay, status checks, or refunds on behalf of a managed merchant today — see Vendor UPI for what that means in practice and why.