Skip to content

Postbacks

If you send a postbackUrl, the gateway calls it with POST when the payment reaches a final status.

{
"transactionUuid": "caa8c25a-e7c1-490d-8d6",
"paymentUuid": "pay_8f2c1d0e5a7b",
"type": "DEPOSIT",
"status": "APPROVED",
"amount": "10.0",
"currencyIso3": "USD",
"message": "Approved",
"referenceCode": "ORDER-2026-000123"
}

type is DEPOSIT or REFUND. status is APPROVED, DECLINED, ERROR or PENDING. Note that amount is formatted as a number (10.0), not padded to two decimals.

  • Headers: Content-Type: application/json and X-Idempotency-Key: <transactionUuid>.
  • Your server must answer 2xx. Anything else, or a timeout, is a failed attempt.
  • Failed attempts are retried with increasing delays (1, 1, 2, 3, 5, 8, 13, 21 and 34 minutes), up to ten attempts.
  • Redirects are not followed.

It must be a public http(s) address on port 80 or 443, without credentials. Private IPs, localhost and other ports are refused with 400 postbackUrl is not allowed: ... on a production gateway.

  1. Answer 2xx fast, then do the work. Slow handlers cause retries.
  2. Be idempotent. The same postback can arrive more than once. Dedupe on X-Idempotency-Key or on referenceCode plus status.
  3. Find the order by referenceCode.
  4. Do not trust the body alone. Postbacks are not signed. Before you ship goods, confirm the status with a lookup using your API key.
app.post('/payments/postback', express.json(), async (req, res) => {
res.sendStatus(200); // answer first
const { referenceCode } = req.body;
const confirmed = await lookup(referenceCode); // POST /payment-Lookup with your key
await markOrder(referenceCode, confirmed.status);
});