Create a pay-in
POST /v1/payin
The same order_id returns the stored order and does not create a second payment. return_url must use a domain you added in the panel.
Copy
curl -s -X POST https://your-domain/v1/payin \
-H "Authorization: Bearer $EKART_API_KEY" \
-H "Content-Type: application/json" \
-d '{"order_id":"ORDER_123","amount":"1000.00","return_url":"https://shop.example/thanks"}'
Copy
{
"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?...",
"phonepe": "phonepe://pay?...",
"paytm": "paytmmp://pay?...",
"bhim": "bhim://upi/pay?..."
}
}
}
Field Rule
order_id1–64 letters, digits, _ or -. You choose it.
amountPositive INR, up to 2 decimals. 1000 and 1000.00 are both accepted.
return_urlAbsolute http or https URL on a domain from the panel. Browser return only. Not payment proof.
upiPresent 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.
Check an order
POST /v1/orders/status
Pending and processing orders are refreshed before the response. success and failed stay final. A key only sees orders created with that same key.
Copy
curl -s -X POST https://your-domain/v1/orders/status \
-H "Authorization: Bearer $EKART_API_KEY" \
-H "Content-Type: application/json" \
-d '{"order_id":"ORDER_123"}'
Copy
{
"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
Errors
Status When
401 Missing or wrong API key. Body is {"error":"Unauthorized"}.
400 The body is not JSON, a field is invalid, or return_url is not on one of your domains.
404 Status call for an order this key cannot see.
409 order_id was already used.
502 The UPI provider rejected the pay-in. The order is not stored, so you can retry the same id.
Webhook
When an order first becomes success or failed, eKart.money POSTs JSON to the webhook URL saved in your merchant panel. The same status is not sent twice.
Copy
{
"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.
Copy
import { createHmac, timingSafeEqual } from "node:crypto";
const expected = createHmac("sha256", process.env.EKART_WEBHOOK_SECRET)
.update(rawBody)
.digest("hex");
const ok = expected.length === signature.length
&& timingSafeEqual(Buffer.from(expected), Buffer.from(signature));