Skip to content

Webhook Integration

Receive real-time transaction status updates (SUCCESS | FAILED | EXPIRED | PENDING) via HTTP POST.

Payload (Example)

json
{
  "transactionId": "abcf4dd4-b7b1-460d-a309-250981498458",
  "md5": "0335ae49...",
  "status": "SUCCESS",
  "amount": "1",
  "currency": "840",
  "requestLog": {
    "metaData": {
      "meta": { "orderId": "ORD-123" },
      "amount": 1,
      "currency": 840,
      "expiredAfter": 30000
    }
  },
  "createdAt": "2025-08-03T15:11:13.483Z"
}

Key fields: transactionId | status | amount | currency | requestLog.metaData.meta | createdAt

Security Headers

x-timestamp: <ISO8601>
x-signature: sha256=<hmac>
content-type: application/json

Signature = HMAC_SHA256( ${timestamp}.${rawBody} , WEBHOOK_SECRET )

Minimal Node.js Handler

js
import crypto from "crypto";
import express from "express";
const app = express();
app.use(express.json());

function verify(req) {
  const ts = req.headers["x-timestamp"];
  const sig = req.headers["x-signature"];
  const body = JSON.stringify(req.body);
  const exp =
    "sha256=" +
    crypto
      .createHmac("sha256", process.env.WEBHOOK_SECRET)
      .update(`${ts}.${body}`)
      .digest("hex");
  return sig === exp;
}

app.post("/webhook", (req, res) => {
  if (!verify(req)) return res.status(401).send("bad sig");
  res.send("OK"); // ack fast
  queueMicrotask(() => handle(req.body));
});

Status Meanings

SUCCESS paid | FAILED declined/error | EXPIRED QR timed out | PENDING awaiting payment

Idempotency

Store processed transactionIds; skip duplicates (retries may occur on 5xx/timeouts).

Quick Checklist

[x] HTTPS [x] Signature verify [x] 200 fast [x] Idempotent [x] Log + monitor

Testing Tip

Use ngrok (ngrok http 3000) and send a manual POST with computed signature.

Last updated: August 10, 2025