Developers

API reference

Base URL: https://zappay.growth-more.in

Overview

Your server creates an order with the amount. Zap Pay returns a payment_url. Send the buyer there. The buyer pays your own UPI ID with any UPI app. When your forwarded payment email arrives, Zap Pay marks the order Success, calls your webhook and redirects the buyer to your redirect_url.

To make matching exact, each open order gets a unique paise amount between 0.01 and 0.99. The buyer is asked to pay that exact amount (pay_amount), for example ₹499.07 for a ₹499 order. The extra paise go to you along with the payment.

All requests are POST with a JSON body (Content-Type: application/json) or form fields. All responses are JSON with a top level status of "success" or "error".

API keys

Find your keys in Dashboard, then API and webhooks. A live key starts with zp_live_, a test key with zp_test_. Send the key as the key field. Keys are shown once when generated; regenerate any time. Keep keys on your server only, never in browser or app code.

Create order

POST https://zappay.growth-more.in/api/create-order

FieldRequiredDescription
keyYesYour live or test API key.
order_idYesYour own unique order reference. 1 to 40 characters: letters, digits, - and _. Must be unique per merchant.
amountYesOrder amount in rupees, 1.00 to 100000.00, up to 2 decimals.
customer_nameNoBuyer name, up to 100 characters.
customer_emailNoBuyer email.
customer_mobileNo10 digit Indian mobile number.
redirect_urlNohttps URL to send the buyer back to after success, failure or expiry.
remarkNoShort note shown on the payment page, up to 60 characters.
curl -X POST https://zappay.growth-more.in/api/create-order \
  -H "Content-Type: application/json" \
  -d '{
    "key": "zp_live_xxxxxxxx",
    "order_id": "ORD1001",
    "amount": "499.00",
    "customer_name": "Asha",
    "customer_mobile": "9876543210",
    "redirect_url": "https://yourshop.com/thank-you",
    "remark": "Ebook purchase"
  }'

Success response:

{
  "status": "success",
  "order_id": "ORD1001",
  "txn_id": "ZP7K2M9QX4TB",
  "payment_url": "https://zappay.growth-more.in/pay/ZP7K2M9QX4TB",
  "amount": "499.00",
  "pay_amount": "499.07",
  "mode": "live",
  "expires_at": "2026-11-03T20:15:00+05:30"
}

Order status

POST https://zappay.growth-more.in/api/order-status with key and order_id (or txn_id).

curl -X POST https://zappay.growth-more.in/api/order-status \
  -H "Content-Type: application/json" \
  -d '{"key":"zp_live_xxxxxxxx","order_id":"ORD1001"}'
{
  "status": "success",
  "data": {
    "order_id": "ORD1001",
    "txn_id": "ZP7K2M9QX4TB",
    "status": "Success",
    "amount": "499.00",
    "pay_amount": "499.07",
    "utr": "430712345678",
    "mode": "live",
    "confirmed_by": "email",
    "created_at": "2026-11-03T20:00:00+05:30",
    "paid_at": "2026-11-03T20:02:41+05:30"
  }
}

Order status values: Pending, Success, Failed, Expired. An order expires 15 minutes after creation if no payment is found. If a matching payment email arrives up to 60 minutes after creation, an expired order still becomes Success, so always handle that change. confirmed_by is email, email_utr, manual or test.

Webhooks

Set a webhook URL in the dashboard. On every status change Zap Pay sends a POST with a JSON body and these headers:

  • X-ZapPay-Signature: sha256=<hex>, the HMAC-SHA256 of the raw request body using your webhook secret.
  • X-ZapPay-Timestamp, Unix time of the attempt.
{
  "event": "order.updated",
  "order_id": "ORD1001",
  "txn_id": "ZP7K2M9QX4TB",
  "status": "Success",
  "amount": "499.00",
  "pay_amount": "499.07",
  "utr": "430712345678",
  "mode": "live",
  "confirmed_by": "email",
  "created_at": "2026-11-03T20:00:00+05:30",
  "paid_at": "2026-11-03T20:02:41+05:30",
  "timestamp": 1793718761
}

Reply with any 2xx status within 8 seconds. Failed deliveries are retried after 1, 5, 15, 60 and 180 minutes. Treat webhooks as a signal: verify the signature, then confirm with /api/order-status and check the amount before delivering.

<?php
$secret = getenv('ZAPPAY_WEBHOOK_SECRET');
$body = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_ZAPPAY_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $body, $secret);
if (!hash_equals($expected, $sig)) {
    http_response_code(401);
    exit('bad signature');
}
$event = json_decode($body, true);
if ($event['status'] === 'Success') {
    // mark your order $event['order_id'] as paid (after checking amount)
}
http_response_code(200);
echo 'ok';

The dashboard has a built-in test receiver. Use it to see exactly what your server will receive before you write any code.

Redirect

When an order succeeds, fails or expires, the payment page sends the buyer to your redirect_url with order_id, txn_id, status and utr added as query parameters. These can be edited by the buyer, so never trust them alone: check the order with the status API.

Test mode

Orders created with a zp_test_ key are test orders. Their payment page shows a test banner and two buttons, "Simulate success" and "Simulate failure", that change the status and send the webhook with "mode": "test". No money moves and no UPI app opens. Test and live orders are kept separate, so you can reuse order IDs.

Auto-confirm setup

Every merchant gets a private forwarding address, shown in Dashboard, then Auto-confirm. Forward your bank or UPI app's payment-received emails to it:

  1. In Gmail on a computer, open Settings, then "See all settings", then "Forwarding and POP/IMAP". Click "Add a forwarding address" and paste your Zap Pay address.
  2. Gmail sends a confirmation code to that address. Within a minute it appears in your Zap Pay dashboard. Enter it in Gmail and click Verify.
  3. Create a filter: in the Gmail search box open "Show search options". In "From" put your bank's or UPI app's sender address (for example the address on a recent "money received" email). Click "Create filter", tick "Forward it to", pick your Zap Pay address, and save. Do not turn on forwarding of all mail.
  4. Ask someone to send you a small payment through a test order, and watch the Email log in the dashboard.

Supported wording includes "received", "credited", "paid you" and "sent you" with amounts written as ₹, Rs or INR. Emails about money you sent, debits and balances are ignored.

Errors and limits

Errors return a non-2xx HTTP status and {"status":"error","message":"..."}. Common messages: Invalid API key, Merchant suspended, Set your UPI ID in the dashboard first, Duplicate order_id, Invalid amount, Too many open orders for this amount, Rate limit exceeded.

Rate limits per API key: 60 create-order and 120 order-status requests per minute. Up to 99 open orders can share the same rupee amount at one time.

Security notes

  • Auto-confirm only accepts emails sent to your own forwarding address, from a known bank or UPI app domain, with a valid DKIM signature for that domain. Anything else goes to Unmatched payments for you to review.
  • Email-based confirmation can still be wrong in rare cases (for example a forged email that passes checks, or a bank changing its email format). For high-value orders, check your UPI app before delivering.
  • Keep your forwarding address private. Never forward OTP or password emails.