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.

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"}'
{
  "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?..."
    }
  }
}
FieldRule
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.

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"}'
{
  "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

StatusWhen
401Missing or wrong API key. Body is {"error":"Unauthorized"}.
400The body is not JSON, a field is invalid, or return_url is not on one of your domains.
404Status call for an order this key cannot see.
409order_id was already used.
502The 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.

{
  "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.EKART_WEBHOOK_SECRET)
  .update(rawBody)
  .digest("hex");
const ok = expected.length === signature.length
  && timingSafeEqual(Buffer.from(expected), Buffer.from(signature));