Verify Callbacks

Best practices for handling Payzava webhook callbacks securely

Always verify callback data by calling the retrieve endpoint. Never trust callback data alone.

Why verify?

  • Callback query parameters could theoretically be spoofed
  • Callbacks may fire multiple times
  • The retrieve endpoint gives you the authoritative current status

Correct callback handling pattern

const express = require("express");
const axios = require("axios");

const app = express();

app.post("/webhooks/payzava", async (req, res) => {
  // Step 1: Respond immediately with 200 OK
  res.status(200).send("OK");

  // Step 2: Extract transaction ID from query params
  const { transactionId, status } = req.query;

  if (!transactionId) {
    console.error("Missing transactionId in callback");
    return;
  }

  // Step 3: Verify status via the retrieve endpoint
  try {
    const response = await axios.get(
      `https://sandbox.payzava.com/cp/api/v1/payment/retrieve/${transactionId}`,
      {
        headers: {
          "x-api-key": process.env.PAYZAVA_API_KEY,
        },
      }
    );

    const { status: verifiedStatus } = response.data.data;

    // Step 4: Process based on verified status
    switch (verifiedStatus) {
      case "Completed":
        // Fulfill the order, credit the user, etc.
        await fulfillOrder(transactionId);
        break;

      case "Cancelled":
        // Mark payment as failed
        await markPaymentFailed(transactionId);
        break;

      case "Processing":
        // Still processing, do nothing yet
        break;
    }
  } catch (error) {
    console.error("Failed to verify callback:", error.message);
  }
});

app.listen(3000);

Best practices

  1. Respond quickly — Always return 200 OK before doing any processing. This prevents callback timeouts.

  2. Verify via retrieve — Never take action based solely on callback query parameters. Always verify with the retrieve endpoint.

  3. Handle duplicates — The same callback may be sent multiple times. Use the transactionId to check if you have already processed this status change.

  4. Log everything — Keep a log of all callbacks received for debugging purposes.

What happens if a callback fails?

If your server does not respond within the timeout window (10 seconds), the callback is considered failed. However:

  • The actual transaction is not affected. The payment, payout, or refund will still proceed normally.
  • Payzava may retry the callback, but this is not guaranteed.
  • You should always poll the retrieve endpoint if you suspect you missed a callback.