API reference
Create UPI payments for your own Paytm Business account and get told when they're paid. Your API key and secret are on Paytm & API keys after you sign in.
Nanofox SMM panels
No code needed. In your panel open Admin → Payment gateways → + Nanofox Pay, paste your API key and secret, and press Save. The panel checks the keys, then shows customers a QR on Add funds and adds the money automatically.
Authentication
Base URL: https://gateway.nanofox.in/api/v1/. Every request carries three headers. The signature proves the request came from you and can't be replayed later (requests more than 5 minutes off are refused).
| Header | Value |
|---|---|
X-Api-Key | Your API key (nfk_…) |
X-Timestamp | Current Unix time in seconds |
X-Signature | Hex HMAC-SHA256 with your API secret over timestamp.METHOD.path.body |
path is the path with its query string, e.g. /api/v1/payments.php?id=NFP…. For GET the body is empty.
// PHP $ts = (string) time(); $path = '/api/v1/payments.php'; $body = json_encode(['order_id' => 'INV-1042', 'amount' => 499]); $sig = hash_hmac('sha256', "$ts.POST.$path.$body", $apiSecret); // send headers X-Api-Key, X-Timestamp: $ts, X-Signature: $sig
// Node.js
const ts = String(Math.floor(Date.now() / 1000));
const sig = crypto.createHmac('sha256', apiSecret)
.update(`${ts}.POST.${path}.${body}`).digest('hex');POST Create a payment
/api/v1/payments.php with a JSON body. Sending the same order_id again returns the same payment, so retries are safe.
| Field | Type | Notes |
|---|---|---|
order_id | string | Your reference, 3–64 letters, numbers, _ or - |
amount | number | Rupees, 1 to 2,00,000 |
webhook_url | string | Optional. Public http(s) address; defaults to the one saved on your account |
// 201 Created (200 with "existing": true for a repeated order_id) { "ok": true, "existing": false, "payment_id": "NFP4F2A…", "order_id": "INV-1042", "amount": 499, "status": "PENDING", "paid_amount": null, "utr": null, "paytm_txn_id": null, "upi_link": "upi://pay?pa=…&am=499.00&tr=NFP4F2A…&cu=INR", "created_at": "…", "expires_at": "…", "paid_at": null }
Show upi_link as a QR code (and as a button on phones). Customers can pay with any UPI app.
GET Get a payment
/api/v1/payments.php?id=NFP… or ?order_id=…. A payment that is still waiting is checked with Paytm right away (at most once every 2 seconds). Returns the same fields as above.
GET Test your keys
/api/v1/ping.php checks your keys and plan and tells you whether your Paytm details are saved.
{ "ok": true, "merchant": 12, "plan_until": "2026-12-01 10:00:00", "paytm_ready": true }Webhooks
When a payment is confirmed we POST JSON to its webhook address: the payment fields plus "event": "payment.success" (or payment.underpaid). Headers:
X-Nanofox-Timestamp | Unix time in seconds |
X-Nanofox-Signature | Hex HMAC-SHA256 with your API secret over timestamp.body |
Answer with any 2xx code. Otherwise we retry after 1, 2, 5, 15 and 30 minutes, then 1, 3 and 6 hours. Always confirm with GET before giving value, and ignore webhooks with an old timestamp.
// PHP: check a webhook
$raw = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_NANOFOX_TIMESTAMP'] ?? '';
$ok = abs(time() - (int) $ts) < 600 && hash_equals(
hash_hmac('sha256', "$ts.$raw", $apiSecret),
$_SERVER['HTTP_X_NANOFOX_SIGNATURE'] ?? '');Statuses
Pending PENDING | Waiting for the customer to pay |
Paid SUCCESS | Paid in full (or more) — give value |
Underpaid UNDERPAID | Paid, but less than the amount; paid_amount says how much |
Expired EXPIRED | The QR time ran out. Still re-checked for 48 hours, so it can become SUCCESS later |
Errors
Errors return {"ok": false, "error": "…", "message": "…"}.
| HTTP | error | Meaning |
|---|---|---|
| 400 | json | The body is not JSON |
| 401 | auth · clock | Wrong key or signature · your server clock is more than 5 minutes off |
| 402 | plan | No active plan — renew to continue |
| 404 | not_found | No such payment on your account |
| 409 | order_id | This order ID was already used with a different amount |
| 422 | paytm · order_id · amount · webhook_url | Paytm details missing, or a field is not valid |
| 429 | rate | Too many requests this minute (120 new payments, 600 status checks) |