API reference

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" }
}
amountAmount in INR. Required.
item_nameShown on the checkout. Required, up to 160 characters.
idempotency_keySend the same key again and you get the same order back instead of a duplicate.
expiry_minutesOptional. Between 1 and 120. Defaults to the value set in Admin → Settings.
customer_name / customer_contactOptional. Shown to you in the dashboard and used for WhatsApp sharing.
device_infoOptional browser/device string if you collect it yourself (the checkout page also records it).
success_url / cancel_url / webhook_urlOptional. 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

TaxIf 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 linksWith “Short links” enabled, GET /pay/:orderCode redirects to the checkout page and the response includes short_url.
InvoicesWith “PDF invoices” enabled, GET /api/v1/orders/:orderCode/invoice returns a PDF once the order is verified.
Rate limitEach website has a requests-per-minute limit (default 60); exceeding it returns 429.

Common errors

401Invalid or inactive site key.
400Invalid order payload (amount, item name or URL).
403Your IP is blocked (for example after repeated failed UTR attempts).
404Order not found.
409Order is not payable (expired or already paid).
429Website rate limit exceeded.
503Maintenance mode is on — new checkouts are paused.
500Server configuration or database error.