Developer Docs

Accept UPI payments from your app

Subscriptions, credit top-ups, and product checkouts — one API, signed webhooks, and SDKs in every language. Get from zero to first payment in 5 minutes.

Quickstart

Three steps from zero to a working checkout.

  1. Grab your API token from Dashboard → API. Treat it like a password — never embed it in client-side code.
  2. POST an order to /api/create-order with the buyer's mobile, an amount, and a unique order_id.
  3. Redirect the buyer to result.payment_url. When they pay, we POST a signed callback to your webhook URL.
Idempotent by design. Resubmitting the same order_id returns DUPLICATE_ORDER_ID instead of creating a second charge. Use your own internal order/cart id and you get safe retries for free.

Authentication

Every API call carries your merchant token in the body.

Send user_token in the JSON or form body of every request. There are no separate API keys or OAuth flows. Tokens never expire — but you can rotate them at any time from the dashboard.

Security: the token must only be used from your backend. Never expose it in browser JavaScript, mobile app source, or public repos.

Create Order

POST /api/create-order — returns a hosted payment_url to redirect the buyer to.

POST https://upi4dev.lovable.app/api/create-order

Request body

FieldTypeRequiredDescription
user_tokenstring
required
Your merchant API token
order_idstring ≤64
required
Your unique reference. Idempotency key.
amountnumber ≥1
required
INR amount.
customer_mobilestringoptionalBuyer mobile (10-digit)
customer_namestringoptionalBuyer display name
customer_emailstringoptionalBuyer email — echoed in webhook remark2
descriptionstringoptionalFree-text description
remark1stringoptionalFree-text — used for fulfilment routing (plan, sku, …)
remark2stringoptionalAdditional metadata — echoed back on webhook
redirect_urlstringoptionalWhere to send buyer after payment

Example request

curl -X POST https://upi4dev.lovable.app/api/create-order \
  -H "Content-Type: application/json" \
  -d '{
    "user_token": "YOUR_API_TOKEN",
    "order_id": "ORD_1777054751095",
    "amount": 499,
    "customer_mobile": "9999999999",
    "customer_name": "John Doe",
    "customer_email": "john@example.com",
    "remark1": "plan:pro_monthly",
    "redirect_url": "https://yoursite.com/thanks"
  }'

Response

{
  "status": true,
  "message": "Order created",
  "result": {
    "orderId": "ORD_1700000000",
    "payment_url": "https://upi4dev.lovable.app/pay/abcd1234efgh5678",
    "upi_intent": "upi://pay?pa=...&am=499&tr=...",
    "byte_transaction_id": "x9k2m4n8p3q7"
  }
}

Check Order Status

POST /api/check-order-status — poll if you missed the webhook.

POST https://upi4dev.lovable.app/api/check-order-status

Returns COMPLETED, PENDING, or FAILURE. Webhooks are the source of truth — only poll as a fallback.

curl -X POST https://upi4dev.lovable.app/api/check-order-status \
  -H "Content-Type: application/json" \
  -d '{ "user_token": "YOUR_API_TOKEN", "order_id": "ORD_1700000000" }'

Webhooks

We POST signed form-encoded callbacks to your URL on every status change.

Each webhook carries an x-pay0clone-signature header — an HMAC-SHA256 hex digest of the raw request body, keyed with your SMS Forwarder Secret from the dashboard. Reject any request whose signature doesn't match.

Verify the signature

import crypto from "node:crypto";

// Express handler
app.post("/api/payment-webhook",
  express.urlencoded({ extended: false }),
  (req, res) => {
    const raw = new URLSearchParams(req.body).toString();
    const expected = crypto
      .createHmac("sha256", process.env.PAY0CLONE_SECRET)
      .update(raw)
      .digest("hex");
    if (req.headers["x-pay0clone-signature"] !== expected) {
      return res.status(401).send("invalid signature");
    }
    // req.body.order_id, req.body.utr, req.body.amount, req.body.status
    fulfillOrder(req.body);
    res.send("ok");
  }
);

Retry policy

If your endpoint returns anything other than 2xx, we retry up to 5 times with exponential backoff (30s → 2m → 8m → 32m → 2h). Always respond quickly with 200 OK and process the payload async.

Use cases

Three patterns covering 99% of merchant integrations.

Subscriptions

Set remark1=plan:pro_monthly. On webhook success, look up the plan, extend the user's expiry by 30 days.

Credits top-up

Set remark1=credits:500. On webhook success, parse the number after credits: and add it to the user's wallet balance.

Products / cart

Set remark1=cart:abc123. On webhook success, look up the cart, mark it paid, and trigger fulfilment.

Error codes

Stable codes you can branch on — message text may change for clarity.

CodeHTTPMeaning
MISSING_TOKEN400user_token field absent from request body
MISSING_ORDER_ID400order_id field absent from request body
AMOUNT_TOO_LOW400amount missing or less than 1
ORDER_ID_TOO_LONG400order_id exceeds 64 characters
INVALID_TOKEN401user_token does not match any merchant
ACCOUNT_BANNED403Merchant account is banned
ACCOUNT_LOCKED403Account locked after repeated failures
PLAN_EXPIRED400Subscription expired — renew to continue
DUPLICATE_ORDER_ID409An order with this order_id already exists
NO_UPI_CONFIGURED400Merchant has no active UPI account
INSERT_FAILED500Database insert failed — contact support

Payment Button SDK

Drop a button into any HTML page — no backend code required to start.

<!-- Drop this anywhere on your page -->
<button data-pay0clone
        data-token="YOUR_API_TOKEN"
        data-amount="499"
        data-customer-mobile="9999999999"
        data-customer-email="user@example.com"
        data-remark1="plan:pro_monthly"
        data-redirect="https://yoursite.com/thanks">
  Buy Pro · ₹499
</button>

<script src="https://upi4dev.lovable.app/sdk/button.js"></script>

The script auto-binds to every [data-pay0clone] element. On click it POSTs to /api/create-order and redirects the buyer. For dynamic SPAs the script re-scans the DOM every 2s.

Because the token sits in HTML, the SDK is best for low-risk merchants and single-tenant sites. For multi-tenant SaaS, generate orders on your backend.

Test mode

There is no separate test environment — small live orders are how you test.

Submit an order with amount: 1 against your real token. Pay ₹1 from your own UPI app. The full webhook → fulfilment loop fires exactly as it would for a real customer. Use the Webhook deliveries page to inspect the signed payload, and the Send test webhook button to fire a synthetic delivery without making any real payment.

Ready to take payments?

Create a free merchant account, get your token, integrate in 5 minutes.

Get started