Build a checkout in one request.
Everything your server needs to create orders, read their status and trust the webhook that confirms them.
Authentication
Keep your site API key on your server and send it in the x-prime-pay-key header. Never put it in browser JavaScript.
x-prime-pay-key: pk_primepay_...
Create checkout
POST /api/v1/checkout
{
"amount": 499,
"item_name": "520 Diamonds",
"external_order_id": "ORDER-12345",
"idempotency_key": "ORDER-12345",
"success_url": "https://example.com/payment/success",
"cancel_url": "https://example.com/payment/cancel",
"webhook_url": "https://example.com/api/payment/webhook",
"metadata": { "product_id": "diamond_520", "uid": "123456789" }
}| amount | Amount in INR. Required. |
| item_name | Shown on the checkout. Required, up to 160 characters. |
| idempotency_key | Send the same key again and you get the same order back instead of a duplicate. |
| expiry_minutes | Optional. Between 1 and 120. Defaults to the value set in Admin → Settings. |
| customer_name / customer_contact | Optional. Shown to you in the dashboard and used for WhatsApp sharing. |
| device_info | Optional browser/device string if you collect it yourself (the checkout page also records it). |
| success_url / cancel_url / webhook_url | Optional. Fall back to the values saved for your site. |
The response contains order, checkout_url, upi_uri and qr_data_url. Redirect the customer to checkout_url.
Order status
GET /api/v1/orders/:orderCode
Returns the order. Status is one of pending_payment, verified, late_paid, expired or refunded. Deliver only on verified (and, at your discretion, late_paid).
QR code
GET /api/v1/orders/:orderCode/qr
Returns an interoperable UPI URI and a QR image for a payable order. Orders that are expired or already paid return 409.
Verify by UTR
POST /api/v1/orders/:orderCode/verify
Used by the checkout page when a customer enters their UTR. Body: { "utr": "123456789012" }. Returns { "success": true } once the payment is found; otherwise an error explaining why.
Sandbox mode
Use the pk_test_… key of a website instead of its live key. The checkout call returns instantly with an order that is already verified, upi_uri and qr_data_url are null, sandbox: true is set, and your webhook fires with is_test: true. Sandbox orders never touch Gmail matching and are excluded from revenue and analytics.
Webhooks
Prime Pay signs the exact raw request body with your site’s webhook secret using HMAC-SHA256 (hex) and sends it in x-prime-pay-signature.
import crypto from "node:crypto";
app.post("/api/payment/webhook", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.get("x-prime-pay-signature") || "";
const expected = crypto.createHmac("sha256", process.env.PRIME_PAY_WEBHOOK_SECRET)
.update(req.body).digest("hex");
const ok = sig.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
if (!ok) return res.sendStatus(401);
const event = JSON.parse(req.body); // event.event === "payment.verified"
// mark event.external_order_id as paid, then deliver
res.sendStatus(200);
});The body contains event plus the fields selected in the website’s Webhook payload builder (default: order_code, amount, status, utr; optional: metadata, external_order_id, device_info, base_amount, tax_amount, paid_at). Failed deliveries retry with exponential backoff (about 30s, 1m, 2m, 4m… up to 8 attempts).
Handle duplicate deliveries idempotently using your external order ID or the order code.
Tax, short links & invoices
| Tax | If a website has a tax rate, it is added on top of amount. The order exposes base_amount, tax_amount, tax_percent and the customer pays the total (amount). |
| Short links | With “Short links” enabled, GET /pay/:orderCode redirects to the checkout page and the response includes short_url. |
| Invoices | With “PDF invoices” enabled, GET /api/v1/orders/:orderCode/invoice returns a PDF once the order is verified. |
| Rate limit | Each website has a requests-per-minute limit (default 60); exceeding it returns 429. |
Common errors
| 401 | Invalid or inactive site key. |
| 400 | Invalid order payload (amount, item name or URL). |
| 403 | Your IP is blocked (for example after repeated failed UTR attempts). |
| 404 | Order not found. |
| 409 | Order is not payable (expired or already paid). |
| 429 | Website rate limit exceeded. |
| 503 | Maintenance mode is on — new checkouts are paused. |
| 500 | Server configuration or database error. |
