Webhooks
Add endpoints under Webhooks in the merchant panel. Fastaar POSTs a JSON
payload when a payment reaches a terminal state: payment.completed,
payment.failed, or payment.expired.
Payload
{
"event": "payment.completed",
"created_at": "2026-06-12T14:31:05+06:00",
"data": {
"id": "01jxyz...",
"object": "payment",
"amount": "500.00",
"currency": "BDT",
"status": "completed",
"provider": "bkash",
"customer_trx_id": "8AB2C3D4EF",
"customer_sender_number": "01712345678",
"metadata": {"order_id": "ORDER-42"},
"verified_at": "2026-06-12T14:31:05+06:00",
"created_at": "2026-06-12T14:30:00+06:00"
}
}
Verifying signatures
Each request carries an X-Fastaar-Event header and an
X-Fastaar-Signature header of the form t=<timestamp>,v1=<hmac>,
where v1 is an HMAC-SHA256 of "{t}.{raw_body}" using your webhook
secret (shown in the merchant panel). Reject requests older than 5 minutes.
PHP:
$header = $_SERVER['HTTP_X_FASTAAR_SIGNATURE'];
$body = file_get_contents('php://input');
[$t, $v1] = [null, null];
foreach (explode(',', $header) as $part) {
[$key, $value] = explode('=', $part, 2);
if ($key === 't') $t = $value;
if ($key === 'v1') $v1 = $value;
}
$valid = abs(time() - (int) $t) <= 300
&& hash_equals(hash_hmac('sha256', "$t.$body", $secret), $v1);
Node.js:
const crypto = require('crypto');
function verify(header, rawBody, secret) {
const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = crypto.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
Retries
Respond with a 2xx status within 10 seconds. Failed deliveries are retried 5 times with
increasing backoff (1m, 5m, 30m, 2h, 12h). You can inspect and redeliver from the merchant
panel. Handle duplicates idempotently — use data.id as the idempotency key.