Callbacks
Handling callbacks
Callbacks are the source of truth for order state. They are delivered at least once, over plain HTTP POST, and you have five seconds to acknowledge — anything slower counts as a failure and triggers a retry.
Last updated 2026-10-24
Collection callback payload
status 2 means paid, 3 means failed. utr is only present on success and may be empty for some channels.
| Field | Type | Notes |
|---|---|---|
| platform_order_no | string | Platform order number |
| merchant_order_no | string | Your order number |
| amount | string | Order amount as string, e.g. "1000.00" |
| actual_amount | string | Amount actually paid |
| fee | string | Fee, e.g. "30.00" |
| status | int | 2 paid, 3 failed |
| utr | string | Bank reference, success only |
| sign | string | MD5 signature |
Verify before you trust
Use the raw received values — do not reformat amount or fee. Formatting "1000" into "1000.00" changes the hash and every callback will fail verification.
$data = json_decode(file_get_contents('php://input'), true);
$calc = md5(
$data['platform_order_no'] .
$data['merchant_order_no'] .
$data['amount'] .
$data['fee'] .
$data['status'] .
$key
);
if (!hash_equals($calc, $data['sign'] ?? '')) {
http_response_code(403);
exit('SIGN ERROR');
}
// idempotency: platform_order_no is the key
if (alreadyProcessed($data['platform_order_no'])) {
echo 'SUCCESS';
exit;
}
if ((int)$data['status'] === 2) {
markOrderPaid($data['merchant_order_no'], $data['actual_amount'], $data['utr'] ?? '');
}
echo 'SUCCESS';Rules that prevent most integration bugs
Reply within five seconds. Do the heavy work asynchronously — persist the payload, reply SUCCESS, then process.
Be idempotent. The same order may be delivered more than once; key your writes on platform_order_no.
Never trust the body without the signature. A forged callback that passes your handler is a free order.
Do not rely on callback order. A payout reversal can arrive after a success if it was re-delivered late — always reconcile against the latest state query.
FAQ
What if my endpoint is down?
Delivery is retried. Reconciling with collectStatus or payoutStatus closes any gap without waiting for a retry.
Do you sign with HMAC?
No — plain MD5 over concatenated values plus your secret key. Bind the callback URL to HTTPS and verify the signature; that is the security model.