Base URL https://pay.mawanta.com/v1 · HTTP Basic auth with key_id:key_secret · amounts in paise (₹1 = 100) · JSON.
curl -u KEY_ID:KEY_SECRET https://pay.mawanta.com/v1/orders \
-H 'Content-Type: application/json' \
-d '{"amount": 49900, "currency": "INR", "receipt": "rcpt_1001", "notes": {"cart": "42"}}'
<script src="https://pay.mawanta.com/checkout.js"></script>
<script>
new PayGate({
order_id: "order_xxxxxxxx", // from step 1
handler: function (r) { // success: send r to YOUR server
// r.paygate_payment_id, r.paygate_order_id, r.paygate_signature
fetch('/verify', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(r)});
},
onfailed: function (r) { alert('Payment failed'); }
}).open();
</script>
Or redirect the customer to https://pay.mawanta.com/checkout/ORDER_ID. Pass callback_url when creating the order to get the result POSTed back to your server (fields: paygate_payment_id, paygate_order_id, paygate_signature, paygate_status).
// Node.js
const expected = crypto.createHmac('sha256', KEY_SECRET).update(order_id + '|' + payment_id).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(paygate_signature))) reject();
// PHP
$expected = hash_hmac('sha256', $order_id.'|'.$payment_id, $KEY_SECRET);
if (!hash_equals($expected, $signature)) reject();
# Python
expected = hmac.new(KEY_SECRET.encode(), f"{order_id}|{payment_id}".encode(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, signature): reject()
Set the endpoint in Dashboard → Webhooks. Each POST has headers X-PayGate-Signature = HMAC-SHA256(raw body, webhook secret), X-PayGate-Event-Id (use for idempotency). Respond with any 2xx. Events: payment.captured, payment.failed, order.paid, refund.processed. Retries: 1m, 5m, 30m, 2h, 6h, 12h.
{"entity":"event","event":"payment.captured","mode":"live","payload":{"payment":{"id":"pay_...","amount":49900,"status":"captured","order_id":"order_...","method":"upi", ...}}}
| POST | /v1/orders | amount*, currency, receipt, notes, callback_url, email, contact |
| GET | /v1/orders · /v1/orders/:id · /v1/orders/:id/payments | fetch / list (count, skip) |
| GET | /v1/payments · /v1/payments/:id | fetch / list |
| POST | /v1/payments/:id/refund | amount (optional, paise; default full), notes |
| GET | /v1/refunds · /v1/refunds/:id | |
| POST | /v1/payment_links | amount*, description, reusable |
| GET | /v1/payment_links | |
| POST | /v1/payment_links/:id/cancel | |
| GET | /v1/settlements |
Test keys (pg_test_…) use a simulated bank: card 4111 1111 1111 1111 succeeds, 4000 0000 0000 0002 is declined, 4000 0000 0000 9995 insufficient funds; UPI success@paygate / failure@paygate; netbanking & wallets open a page where you choose success or failure. No real money moves.
HTTP 400 {"error":{"code":"BAD_REQUEST_ERROR","description":"amount must be an integer in paise, minimum 100 (₹1)","field":"amount"}}
HTTP 401 bad credentials · 403 live not activated / suspended · 404 not found · 429 rate limited