Skip to content

Quickstart

Your first test payment. No real money moves.

Seven steps, all in test mode through the built-in test gateway. You need a ZopPay account and a server that can make HTTPS requests.

Creates a test payment with cURL and prints its checkout link.
  1. Step 01

    Create a test key

    Sign up, open API keys in the dashboard and create a key: give it a name and choose the mode Test. Copy the secret straight away: it is shown once, and ZopPay keeps only a hash of it.

    Keep the key on your server. Never put it in a web page or a mobile app.

    Sign up

    Shell
    # Keep the key on your server. Every request sends it as
    #   Authorization: Bearer $ZOPPAY_SECRET_KEY
    export ZOPPAY_SECRET_KEY="sk_test_…"   # paste the whole key from Dashboard → API keys
  2. Step 02

    Create a payment

    Send the amount in the currency’s smallest unit (paise for rupees: 49900 is ₹499.00), the currency, your own order reference and the return_url the shopper should come back to. It must start with https://.

    Add an Idempotency-Key so that a retried request returns the first answer instead of creating a second payment.

    The answer is 201 Created with the payment under data, including its checkout_url.

    POST /v1/payments
    # Create a payment. Needs: export ZOPPAY_SECRET_KEY=sk_test_…
    curl https://api.zoppay.in/v1/payments \
      -H "Authorization: Bearer $ZOPPAY_SECRET_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: order_1042" \
      -d '{
        "amount": 49900,
        "currency": "INR",
        "reference": "order_1042",
        "return_url": "https://shop.example.com/orders/1042/complete",
        "customer": {
          "name": "Asha Rao",
          "email": "asha@example.com",
          "phone": "+919876543210"
        }
      }'
    
    # The answer is 201 Created, with the payment under "data".
    # Send the shopper's browser to data.checkout_url.
    Response
    HTTP/1.1 201 Created
    
    {
      "data": {
        "id": "pay_01JD3V8Q4M6Y2C9T7K5W1R0BNA",
        "status": "pending",
        "amount": 49900,
        "currency": "INR",
        "reference": "order_1042",
        "checkout_url": "https://checkout.zoppay.in/q3l7vz2kbx6ndw4rj5tmhs2pyc7ga4fe6uon3wia"
      }
    }
  3. Step 03

    Send the shopper to the checkout

    Redirect the shopper’s browser to checkout_url. The link works for 30 minutes.

    With a test key the checkout is a test page that says no real money moves. Press Pay (test mode). With a live key the shopper goes to your gateway’s own secure page instead.

    Afterwards the browser comes back to your return_url with payment_id and status added.

    Back on your site
    # After the checkout, the shopper's browser comes back to your return_url
    # with the payment id and status added to the query string:
    https://shop.example.com/orders/1042/complete?payment_id=pay_01JD3V8Q4M6Y2C9T7K5W1R0BNA&pp_outcome=payment-success&status=captured
    
    # status is captured, failed or pending. Anyone can type this URL,
    # so confirm the payment from your server before you ship the order.
  4. Step 04

    Confirm the payment on your server

    Anyone can type a return URL, so don’t ship an order on the query string alone. Fetch the payment with your secret key and check that status is captured.

    A key only sees payments of its own mode: a test key gets 404 for a live payment.

    GET /v1/payments/{id}
    # Get a payment. Use the data.id that create returned.
    curl https://api.zoppay.in/v1/payments/pay_01JD3V8Q4M6Y2C9T7K5W1R0BNA \
      -H "Authorization: Bearer $ZOPPAY_SECRET_KEY"
    
    # The answer is 200 OK, with the payment under "data".
    # Fulfil the order when data.status is "captured".
  5. Step 05

    Receive webhooks

    In the dashboard, open Webhooks, add your endpoint’s address and pick the events. The address must be https:// on a public host. Copy the signing secret shown when you save it.

    Check X-PP-Signature-V2 on every delivery before you trust it. To try your receiver on your own machine first, the cURL tab sends it a correctly signed test event.

    Verify a webhook
    # Send a correctly signed test event to your own receiver, the way ZopPay signs it.
    SECRET='whsec_…'   # your endpoint's signing secret: the whole value, whsec_ included
    URL='http://localhost:3000/webhooks/zoppay'
    BODY='{"id":"whe_01JD3VC4R7T9W2Y5A8C1E3G6J9","type":"payment.captured","created_at":"2026-10-19T10:21:03.512Z","organisation_id":"org_01JD2Q0X8M4N6P1R3S5T7V9W2Y","data":{"amount":49900,"currency":"INR","gateway_transaction_id":"test_txn_pay_01JD3V8Q4M6Y2C9T7K5W1R0BNA_1760869263512345678","mode":"test","order_id":"ord_01JD3V8Q2K5N7Q9S1V3W5Y7A0C","payment_id":"pay_01JD3V8Q4M6Y2C9T7K5W1R0BNA","reference":"order_1042"}}'
    TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)
    
    # X-PP-Signature-V2 = "sha256=" + hex(HMAC-SHA256(secret, timestamp + "." + raw body))
    SIG="sha256=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)"
    
    curl -i "$URL" \
      -H "Content-Type: application/json" \
      -H "X-PP-Event: payment.captured" \
      -H "X-PP-Timestamp: $TS" \
      -H "X-PP-Signature-V2: $SIG" \
      -d "$BODY"
  6. Step 06

    Try a refund

    Refund part of the test payment, or leave out amount to refund what is left. Test refunds finish at once with the status succeeded, and refund.succeeded is sent to your webhook.

    With a live gateway a refund can stay pending for a while; you hear the result by webhook.

    POST /v1/refunds
    # Refund ₹100.00 of a payment. Leave out "amount" to refund everything that is left.
    curl https://api.zoppay.in/v1/refunds \
      -H "Authorization: Bearer $ZOPPAY_SECRET_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: refund_order_1042_1" \
      -d '{
        "payment_id": "pay_01JD3V8Q4M6Y2C9T7K5W1R0BNA",
        "amount": 10000,
        "reason": "Item returned"
      }'
    
    # The answer is 201 Created, with the refund under "data".
    # data.status is "succeeded", or "pending" until the gateway confirms.
  7. Step 07

    Go live

    Your account goes live by itself once you have done three things in the dashboard: accept the terms, add billing details (email, address and a tax ID such as a GSTIN), and connect a live gateway. There is no review by ZopPay. Your gateway may have its own approval.

    Then create a live key (sk_live_…) and use it in place of the test key. The requests stay the same. Until you are live, a live payment answers 403 and the message says what is missing.

    A live key before going live
    HTTP/1.1 403 Forbidden
    
    {"error":{"message":"live payments are unavailable until this organisation goes live: accept the terms, add billing details (email, address and tax id), connect a gateway"}}

Stuck on a step?

Send us the request you made and the response you got, and we will help you find the problem.