Zoorya Pay Developers

Handling webhooks

Receive the webhook, confirm it with Get a payment, and handle repeats.

A webhook is a message Zoorya Pay sends to your server when a payment finishes, so you don't have to keep asking. This page shows how to receive it safely, confirm it, and avoid handling the same payment twice.

The rule: a webhook is a signal, not proof#

When a webhook arrives, call Get a payment for its paymentId and act only when status is SUCCESSFUL and amount matches your order. This keeps you safe even if someone sends a fake request to your callback URL.

Steps in your handler#

  1. Read externalUniqueId (your ID) and paymentId from the body. See Webhook shape.
  2. Reply 200 straight away. Do the rest in the background.
  3. If you already processed this paymentId, stop.
  4. Call Get a payment for the paymentId.
  5. If status is SUCCESSFUL and the amount matches your order, mark it paid and fulfil.
app.post("/webhooks/zoorya", express.json(), (req, res) => {
  res.sendStatus(200); // answer fast

  const { externalUniqueId, paymentId } = req.body || {};
  if (!externalUniqueId || !paymentId) return;

  setImmediate(async () => {
    if (await alreadyProcessed(paymentId)) return;
    const order = await findOrder(externalUniqueId);
    if (order && (await isPaid(auth, paymentId, order.total))) { // see Recommended pattern
      await markOrderPaid(order);
    }
    await rememberProcessed(paymentId);
  });
});

Repeats: use paymentId#

The same webhook can arrive more than once. Each payment has its own paymentId — use it to detect repeats, and never deliver the same order twice.

Delivery#

  • Your callback URL must be reachable from the public internet. localhost does not work — use a tunnel when testing (see below).
  • Reply with a 2xx quickly, and do slow work in the background.
  • Use https:// in production.

If you miss a webhook, nothing is lost: call Get a payment at any time.

Do not rely on the webhook alone. For orders still unpaid after a few minutes, call Get a payment yourself — for example from a scheduled job every few minutes.

Test webhooks locally#

Your laptop is not on the public internet, so use a tunnel such as ngrok:

bash
ngrok http 3000

Use the https://…ngrok… address it prints as your callback URL in sandbox.