Quickstart
Three steps from zero to a working checkout.
- Grab your API token from Dashboard → API. Treat it like a password — never embed it in client-side code.
- POST an order to
/api/create-orderwith the buyer's mobile, an amount, and a uniqueorder_id. - Redirect the buyer to
result.payment_url. When they pay, we POST a signed callback to your webhook URL.
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.
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
| Field | Type | Required | Description |
|---|---|---|---|
| user_token | string | required | Your merchant API token |
| order_id | string ≤64 | required | Your unique reference. Idempotency key. |
| amount | number ≥1 | required | INR amount. |
| customer_mobile | string | optional | Buyer mobile (10-digit) |
| customer_name | string | optional | Buyer display name |
| customer_email | string | optional | Buyer email — echoed in webhook remark2 |
| description | string | optional | Free-text description |
| remark1 | string | optional | Free-text — used for fulfilment routing (plan, sku, …) |
| remark2 | string | optional | Additional metadata — echoed back on webhook |
| redirect_url | string | optional | Where 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.
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.
| Code | HTTP | Meaning |
|---|---|---|
| MISSING_TOKEN | 400 | user_token field absent from request body |
| MISSING_ORDER_ID | 400 | order_id field absent from request body |
| AMOUNT_TOO_LOW | 400 | amount missing or less than 1 |
| ORDER_ID_TOO_LONG | 400 | order_id exceeds 64 characters |
| INVALID_TOKEN | 401 | user_token does not match any merchant |
| ACCOUNT_BANNED | 403 | Merchant account is banned |
| ACCOUNT_LOCKED | 403 | Account locked after repeated failures |
| PLAN_EXPIRED | 400 | Subscription expired — renew to continue |
| DUPLICATE_ORDER_ID | 409 | An order with this order_id already exists |
| NO_UPI_CONFIGURED | 400 | Merchant has no active UPI account |
| INSERT_FAILED | 500 | Database insert failed — contact support |
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.
