1
Create a pay-in
POST /v1/payin with your API key, an order id, the INR amount, and a return URL.
UPI payment API
Your server calls eKart.money with an order id and an amount. The response has a payment link.
The customer pays there. return_url only brings the browser back. An order is paid
when the status API or the webhook says success.
1
POST /v1/payin with your API key, an order id, the INR amount, and a return URL.
2
Send the customer to payment_url, or open upi.apps on their phone. Both are eKart.money. Neither is a provider checkout page.
3
Poll /v1/orders/status, or accept the signed webhook. Do not mark an order paid from the browser redirect.
Every /v1 call is POST with Content-Type: application/json and Authorization: Bearer <API key>. The examples below use this server’s address.
POST /v1/payin
The same order_id returns the stored order and does not create a second payment.
curl -s -X POST https://your-domain/v1/payin \
-H "Authorization: Bearer $GATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"order_id":"ORDER_123","amount":"1000.00","return_url":"https://shop.example/thanks"}'
{
"order_id": "ORDER_123",
"gateway_order_no": "PI...",
"payment_url": "https://your-domain/pay/ORDER_123",
"status": "pending",
"upi": {
"upi_id": "shop@okbank",
"uri": "upi://pay?pa=shop@okbank&am=1000.00&cu=INR&tn=ORDER_123",
"apps": {
"gpay": "gpay://upi/pay?pa=shop@okbank&am=1000.00&cu=INR&tn=ORDER_123",
"phonepe": "phonepe://pay?pa=shop@okbank&am=1000.00&cu=INR&tn=ORDER_123",
"paytm": "paytmmp://pay?pa=shop@okbank&am=1000.00&cu=INR&tn=ORDER_123",
"bhim": "bhim://upi/pay?pa=shop@okbank&am=1000.00&cu=INR&tn=ORDER_123"
}
}
}
| Field | Rule |
|---|---|
order_id | 1–64 letters, digits, _ or -. You choose it. |
amount | Positive INR, up to 2 decimals. 1000 and 1000.00 are both accepted. |
return_url | Absolute http or https URL. Browser return only. Not payment proof. |
upi | Present while the order can still be paid. uri opens any UPI app. apps opens one app. android uses Android intents. null after a final status. |
POST /v1/orders/status
Pending and processing orders are refreshed before the response. success and failed stay final.
curl -s -X POST https://your-domain/v1/orders/status \
-H "Authorization: Bearer $GATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"order_id":"ORDER_123"}'
{
"order_id": "ORDER_123",
"type": "payin",
"amount": "1000.00",
"status": "success",
"gateway_order_no": "PI...",
"payment_url": "https://your-domain/pay/ORDER_123",
"upi": null
}
pending processing success failed
| Status | When |
|---|---|
| 401 | Missing or wrong API key. Body is {"error":"Unauthorized"}. |
| 400 | Body is not a JSON object, or a field fails the rules above. |
| 404 | Status call for an order_id this server has never created. |
| 409 | order_id was already used for a different order type. |
| 502 | The UPI provider rejected the pay-in. The order is not stored, so you can retry the same id. |
When an order first becomes success or failed, eKart.money POSTs JSON to the callback URL configured for this server. The same status is not sent twice.
{
"order_id": "ORDER_123",
"type": "payin",
"amount": "1000.00",
"status": "success",
"gateway_order_no": "PI..."
}
Header x-signature is hex HMAC-SHA256 of the raw body, keyed with your webhook secret. Verify the raw bytes before you parse JSON.
import { createHmac, timingSafeEqual } from "node:crypto";
const expected = createHmac("sha256", process.env.GATEWAY_WEBHOOK_SECRET)
.update(rawBody)
.digest("hex");
const ok = expected.length === signature.length
&& timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
Sign in with a Tron wallet through WalletConnect, on this site. eKart.money checks a signature from that address, then opens the account. A successful panel pay-in records a 1% reward against that Tron address. The reward is stored by eKart.money. It is not an on-chain token.