UPIQRPay API v1.0
Gateway Online
Official Developer Manual

UPIQRPay Integration Guide

UPIQRPay provides enterprise Zero-MDR direct settlement payment gateway infrastructure. Using our APIs, you can generate dynamic UPI QR codes, sanitized 1-tap mobile deep links that eliminate bank "Risk Policy" warnings, and automatically reconcile customer settlements with cryptographic webhooks.

Base Gateway URL:
http://localhost:5001

Authentication & Security

All order creation and management endpoints authenticate requests using a Merchant API Key passed in the Authorization HTTP header.

HTTP Header Requirement Bearer Authentication
Authorization: Bearer upiqr_live_your_merchant_api_key_here

You can inspect or regenerate your production API key anytime under Dashboard ➔ API & Webhooks.

Standard Response Structure

All responses from the UPIQRPay API are formatted as standard JSON with an envelope containing success, data or message, and error codes when applicable.

Success Response (HTTP 200/201)
{
  "success": true,
  "data": { ... },
  "message": "Operation successful"
}
Error Response (HTTP 4xx/5xx)
{
  "success": false,
  "message": "Invalid API Key or expired",
  "error_code": "UNAUTHORIZED"
}
POST

/api/v1/order/create

Creates a dynamic UPI payment session. Generates a unique 12-character payment ID, calculates collision-free payable amount, generates Base64 QR code image, and provides sanitized Risk-Policy bypass deep links.

Request Headers & Body

Field Type Required Description
amount Number Yes Order amount in INR (min 1.00).
order_id String Yes Your custom merchant order/invoice reference.
customer_name String Optional Customer's full name.
customer_email String Optional Customer's email address.
redirect_url String Optional URL to return customer to upon successful payment.
curl -X POST http://localhost:5001/api/v1/order/create \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_MERCHANT_API_KEY" \
  -d '{
    "amount": 299.00,
    "order_id": "INV_881923",
    "customer_name": "Vikram Singh",
    "customer_email": "vikram@example.com",
    "redirect_url": "https://yourstore.com/thankyou"
  }'
Sample JSON Response (201 Created)
{
  "success": true,
  "message": "Order session created successfully",
  "data": {
    "payment_id": "UPG4A8C91E5B20F",
    "order_id": "INV_881923",
    "amount": 299.00,
    "payable_amount": 299.04,
    "status": "pending",
    "qr_code": "data:image/png;base64,iVBORw0KGgoAAA...",
    "upi_link": "upi://pay?pa=paytmqr...@paytm&pn=Store&am=299.04&tr=UPG4A8C91E5B20F&tn=UPG4A8C91E5B20F&cu=INR",
    "pay_url": "http://localhost:5001/pay/UPG4A8C91E5B20F",
    "intent_links": {
      "paytm": "paytmmp://cash_wallet?pa=paytmqr...@paytm&pn=Store&am=299.04&cu=INR&tn=UPG4A8C91E5B20F&featuretype=money_transfer",
      "phonepe": "phonepe://pay?pa=paytmqr...@paytm&pn=Store&am=299.04&cu=INR&tn=UPG4A8C91E5B20F",
      "gpay": "tez://upi/pay?pa=paytmqr...@paytm&pn=Store&am=299.04&cu=INR&tn=UPG4A8C91E5B20F"
    },
    "expires_at": "2026-10-27T12:05:00.000Z"
  }
}
GET

/api/v1/order/status/:payment_id

Polls or checks the live settlement state of a transaction. Automatically triggers real-time reconciliation against the connected provider APIs if the order is still pending.

Sample Response (Settled Order)
{
  "success": true,
  "data": {
    "payment_id": "UPG4A8C91E5B20F",
    "order_id": "INV_881923",
    "payable_amount": 299.04,
    "status": "success",
    "utr_number": "428190019283",
    "paid_at": "2026-10-27 11:58:34"
  }
}

Webhooks & Signature Verification

Whenever a customer completes a payment, UPIQRPay sends an instantaneous HTTP POST request to your configured webhook URL. Every webhook includes an X-UPIQRPay-Signature header containing an HMAC-SHA256 signature calculated using your secret key.

Webhook Event Body (`payment.success`)
{
  "event": "payment.success",
  "timestamp": "2026-10-27T11:58:34.000Z",
  "data": {
    "payment_id": "UPG4A8C91E5B20F",
    "order_id": "INV_881923",
    "amount": 299.00,
    "payable_amount": 299.04,
    "status": "success",
    "utr_number": "428190019283",
    "paid_at": "2026-10-27 11:58:34"
  }
}

How to Verify Signatures on Your Server

Node.js / Express
const crypto = require('crypto');

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-upiqrpay-signature']; // "sha256=..."
  const secret = process.env.UPIQRPAY_WEBHOOK_SECRET;

  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(req.body).digest('hex');

  if (signature !== expected) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.body);
  if (event.event === 'payment.success') {
    console.log(`Payment confirmed for Order: ${event.data.order_id}, UTR: ${event.data.utr_number}`);
  }
  res.status(200).send('OK');
});
PHP (Laravel / Native)
$rawPayload = file_get_contents('php://input');
$signatureHeader = $_SERVER['HTTP_X_UPIQRPAY_SIGNATURE'] ?? '';
$secret = getenv('UPIQRPAY_WEBHOOK_SECRET');

$expectedSignature = 'sha256=' . hash_hmac('sha256', $rawPayload, $secret);

if (!hash_equals($expectedSignature, $signatureHeader)) {
    http_response_code(401);
    exit('Invalid signature');
}

$event = json_decode($rawPayload, true);
// Fulfill customer order in database...
http_response_code(200);
echo 'OK';

Android Soundbox & Notification Reader API

POST /api/v1/webhook/soundbox-reader

The Soundbox Reader endpoint enables Android companion devices, MacroDroid, Tasker, or Soundbox APKs to push incoming payment notifications directly to your UPIQRPay gateway. The engine automatically parses amounts and 12-digit bank UTR numbers, matches pending merchant orders, updates payment status to SUCCESS, and fires signed HMAC webhooks.

POST /api/v1/webhook/soundbox-reader Authenticated
Authentication: Pass your Merchant API Key via:
  • Header: Authorization: Bearer YOUR_API_KEY
  • Header: X-API-KEY: YOUR_API_KEY
  • Query parameter: ?api_key=YOUR_API_KEY
  • Body property: "api_key": "YOUR_API_KEY"
Request Body:
{
  "package_name": "net.one97.paytm",
  "title": "Payment of Rs. 150.01 received",
  "text": "Received Rs. 150.01 successfully on Paytm for Business. UPI Ref no 429811998877"
}
Success Response (200 OK):
{
  "success": true,
  "matched": true,
  "message": "Payment notification verified successfully and order completed!",
  "data": {
    "matched": true,
    "payment_id": "UPG68EE2C57C74B",
    "order_id": "ORD_1002",
    "amount": 150.01,
    "utr": "429811998877",
    "provider": "paytm",
    "status": "success"
  }
}

Android Kotlin Risk-Policy Bypass Kit

Zero Risk-Policy Warnings • Seamless Auto-Return to App

The Problem: When launching generic upi://pay?tr=... links, Paytm, PhonePe, and Google Pay show a red banner stating "Risk Policy Error: Unverified Transaction" because the merchant is not a multi-crore enterprise signed banking aggregator.

Our Solution: By transforming links into paytmmp://cash_wallet and placing the payment ID in tn (Transaction Note) while omitting tr, the UPI apps recognize it as a verified peer-to-merchant payment without raising security alarms!

1. UpiPaymentHelper.kt Deep Link Sanitizer
package com.yourcompany.app.payment

import android.content.Context
import android.content.Intent
import android.net.Uri

object UpiPaymentHelper {
    /**
     * Launches Paytm with Sanitized Cash Wallet Scheme to bypass banking risk policies.
     */
    fun openPaytmBypass(
        context: Context,
        upiVpa: String,
        merchantName: String,
        amount: Double,
        paymentId: String
    ): Boolean {
        val sanitizedUri = Uri.parse(
            "paytmmp://cash_wallet?pa=$upiVpa&pn=${Uri.encode(merchantName)}&am=$amount&cu=INR&tn=$paymentId&featuretype=money_transfer"
        )
        val intent = Intent(Intent.ACTION_VIEW, sanitizedUri).apply {
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
        }
        return try {
            context.startActivity(intent)
            true
        } catch (e: Exception) {
            false
        }
    }

    /**
     * Launches PhonePe or Google Pay with sanitized intent note.
     */
    fun openPhonePeOrGPay(context: Context, deepLink: String): Boolean {
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse(deepLink)).apply {
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
        }
        return try {
            context.startActivity(intent)
            true
        } catch (e: Exception) {
            false
        }
    }
}
2. PaymentActivity.kt WebView & Auto-Return
package com.yourcompany.app.payment

import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity

class PaymentActivity : AppCompatActivity() {

    private lateinit var webView: WebView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        webView = WebView(this).apply {
            settings.javaScriptEnabled = true
            settings.domStorageEnabled = true
            
            webViewClient = object : WebViewClient() {
                override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
                    val url = request?.url?.toString() ?: return false

                    // 1. Intercept Paytm, PhonePe, GPay, and BHIM UPI schemes
                    if (url.startsWith("paytmmp://") || url.startsWith("phonepe://") ||
                        url.startsWith("tez://") || url.startsWith("upi://")) {
                        try {
                            val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
                                flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
                            }
                            startActivity(intent)
                            return true
                        } catch (e: Exception) {
                            return false // Fall back to rendering hosted QR in WebView
                        }
                    }

                    // 2. Intercept Success Callback to bring app back to foreground automatically
                    if (url.contains("status=success")) {
                        val returnIntent = Intent().apply {
                            putExtra("EXTRA_PAYMENT_STATUS", "SUCCESS")
                            putExtra("EXTRA_UTR", request.url?.getQueryParameter("utr"))
                        }
                        setResult(Activity.RESULT_OK, returnIntent)
                        finish() // Auto-close checkout & resume merchant app
                        return true
                    }

                    return false
                }
            }
        }

        setContentView(webView)

        // Load the hosted pay_url returned by /api/v1/order/create
        val payUrl = intent.getStringExtra("EXTRA_PAY_URL") ?: return finish()
        webView.loadUrl(payUrl)
    }
}

PHP & WooCommerce Gateway Snippet

Drop-in WordPress / WooCommerce Payment Gateway

wc-gateway-upiqrpay.php
<?php
/**
 * Plugin Name: UPIQRPay Zero-MDR UPI Gateway for WooCommerce
 * Description: Direct Bank Settlements via Paytm & BharatPe with 0% Fee.
 * Version: 1.0.0
 */

if (!defined('ABSPATH')) exit;

add_action('plugins_loaded', 'init_upiqrpay_gateway');

function init_upiqrpay_gateway() {
    if (!class_exists('WC_Payment_Gateway')) return;

    class WC_Gateway_UPIQRPay extends WC_Payment_Gateway {
        public function __construct() {
            $this->id = 'upiqrpay';
            $this->method_title = '0% Fee UPI Gateway (Paytm/BharatPe)';
            $this->method_description = 'Accept direct bank settlements via scannable QR and 1-tap mobile apps.';
            $this->has_fields = false;

            $this->init_form_fields();
            $this->init_settings();

            $this->title = $this->get_option('title', 'UPI / QR Code (0% Extra Fee)');
            $this->api_key = $this->get_option('api_key');
            $this->webhook_secret = $this->get_option('webhook_secret');

            add_action('woocommerce_update_options_payment_gateways_' . $this->id, array($this, 'process_admin_options'));
            add_action('woocommerce_api_upiqrpay_webhook', array($this, 'handle_webhook'));
        }

        public function init_form_fields() {
            $this->form_fields = array(
                'enabled' => array('title' => 'Enable/Disable', 'type' => 'checkbox', 'label' => 'Enable UPIQRPay', 'default' => 'yes'),
                'title' => array('title' => 'Title', 'type' => 'text', 'default' => 'Pay with UPI (GPay, PhonePe, Paytm)'),
                'api_key' => array('title' => 'Merchant API Key', 'type' => 'text', 'description' => 'From your UPIQRPay Dashboard'),
                'webhook_secret' => array('title' => 'Webhook HMAC Secret', 'type' => 'text')
            );
        }

        public function process_payment($order_id) {
            $order = wc_get_order($order_id);
            $apiUrl = 'http://localhost:5001/api/v1/order/create';

            $response = wp_remote_post($apiUrl, array(
                'headers' => array(
                    'Authorization' => 'Bearer ' . $this->api_key,
                    'Content-Type' => 'application/json'
                ),
                'body' => json_encode(array(
                    'order_id' => (string)$order->get_id(),
                    'amount' => (float)$order->get_total(),
                    'customer_name' => $order->get_billing_first_name(),
                    'customer_email' => $order->get_billing_email(),
                    'redirect_url' => $this->get_return_url($order)
                )),
                'timeout' => 15
            ));

            if (is_wp_error($response)) {
                wc_add_notice('Gateway connection error.', 'error');
                return;
            }

            $body = json_decode(wp_remote_retrieve_body($response), true);
            if (!empty($body['success'])) {
                return array('result' => 'success', 'redirect' => $body['data']['pay_url']);
            }

            wc_add_notice('Error: ' . ($body['message'] ?? 'Could not create order'), 'error');
            return;
        }

        public function handle_webhook() {
            $raw = file_get_contents('php://input');
            $signature = $_SERVER['HTTP_X_UPIQRPAY_SIGNATURE'] ?? '';
            $expected = 'sha256=' . hash_hmac('sha256', $raw, $this->webhook_secret);

            if (!hash_equals($expected, $signature)) {
                status_header(401);
                exit('Invalid Signature');
            }

            $data = json_decode($raw, true);
            if ($data['event'] === 'payment.success') {
                $order = wc_get_order($data['data']['order_id']);
                if ($order && !$order->is_paid()) {
                    $order->payment_complete($data['data']['utr_number']);
                    $order->add_order_note('Payment verified via UPIQRPay. Bank UTR: ' . $data['data']['utr_number']);
                }
            }

            status_header(200);
            exit('OK');
        }
    }
}

add_filter('woocommerce_payment_gateways', function($gateways) {
    $gateways[] = 'WC_Gateway_UPIQRPay';
    return $gateways;
});