Webhooks
Add endpoints under Webhooks in the merchant panel. Fastaar POSTs a JSON payload when a payment reaches a terminal state, or when a customer record changes.
Events
| Event | Fires when |
|---|---|
payment.completed | A payment is verified and marked completed. |
payment.failed | A payment fails verification (e.g. duplicate TrxID, rejected by admin). |
payment.expired | A payment's checkout window closes without a submitted TrxID. |
payment.refunded | A payment is fully refunded (in one call, or across several partial refunds) via the API or merchant panel. |
payment.partially_refunded | A payment is refunded for less than its full remaining balance. |
customer.created | A new customer record is created. |
customer.updated | An existing customer record is updated. |
Payment event payload
{
"event": "payment.completed",
"created_at": "2026-06-12T14:31:05+06:00",
"data": {
"id": "01jxyz...",
"amount": "500.00",
"amount_due": "505.00", // includes cash-out charge if configured
"refunded_amount": "0.00", // cumulative amount refunded so far
"gateway_charge": "5.00", // actual charge applied (0.00 if none)
"gateway_charge_type": "fixed", // "percentage", "fixed", or null
"gateway_charge_value": "5.00", // configured rate; null if no charge
"currency": "BDT",
"livemode": true,
"source": "api",
"status": "completed",
"invoice_number": "ORDER-42",
"customer_id": null, // the customer_id passed at creation, or set automatically via a payment link
"provider": "bkash",
"payment_method": "bKash (01512345678)",
"customer_trx_id": "8AB2C3D4EF",
"customer_sender_number": "01512345678",
"metadata": {"order_id": "ORDER-42"},
"failure_reason": null,
"verified_at": "2026-06-12T14:31:05+06:00",
"created_at": "2026-06-12T14:30:00+06:00"
}
}
Customer event payload
{
"event": "customer.created",
"created_at": "2026-06-12T14:31:05+06:00",
"data": {
"id": "01jxyz...",
"name": "Jane Doe",
"email": "jane@example.com",
"phone": "01512345678",
"address": "House 12, Road 3, Dhaka",
"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');
if (! preg_match('/^t=(?P<t>\d+),v1=(?P<v1>[a-f0-9]{64})$/', $header, $m)) {
http_response_code(400); exit;
}
$valid = abs(time() - (int) $m['t']) <= 300
&& hash_equals(hash_hmac('sha256', "{$m['t']}.{$body}", $secret), $m['v1']);
Node.js:
const crypto = require('crypto');
function verify(header, rawBody, secret) {
const m = Object.fromEntries(header.split(',').map(p => p.split('=')));
if (Math.abs(Date.now() / 1000 - Number(m.t)) > 300) return false;
const expected = crypto.createHmac('sha256', secret)
.update(`${m.t}.${rawBody}`)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(m.v1));
}
Testing
Send a test delivery without making a real payment. In the merchant panel, open a webhook endpoint and click Send Test. Pick the event you want to simulate — the panel dispatches a realistic payload signed with your endpoint's secret, so you can confirm your server receives and verifies it correctly. The delivery is logged under Webhook Logs like any live event.
Retries
Respond with a 2xx status within 10 seconds. Failed deliveries are retried up to 5 times
with exponential backoff: 1 min, 5 min, 30 min, 2 h, 12 h. After all attempts are
exhausted the log is marked exhausted — you can inspect and manually
redeliver from the merchant panel. Handle duplicates idempotently using data.id
as the idempotency key.