Skip to main content

Webhooks (Callbacks)

When an order's status changes, the system sends an HTTP POST to the notifyUrl specified on the order (or your merchant profile default).

Payload Format​

Legacy API callbacks use the compact UTpay format:

{
"clientId": "TEST_CLIENT_001",
"refCode": "ORD-260601103045-A1B2",
"transactionTime": "2026-06-01 10:30:45",
"orderId": "INV-2026-00123",
"totalAmount": 50000,
"customerName": "Budi Santoso",
"customerEmail": "budi@example.com",
"customerPhone": "6281234567890",
"status": "PAID",
"paidAt": "2026-06-01 10:38:12",
"signature": "lowercase-sha512-hex"
}

Fields​

FieldTypeDescription
refCodestringFlyPay internal reference code
clientIdstringYour unchanged UTpay client ID
transactionTimestringOrder creation time in WIB
orderIdstringYour original order ID (the orderId you sent in the create request)
totalAmountintegerFull IDR integer — same value you sent in totalAmount
statusstringPAID, PENDING, or FAILED
paidAtstringWIB timestamp ("YYYY-MM-DD HH:MM:SS"); null if not paid
signaturestringSHA-512 verification signature

Status Mapping​

FlyPay StatusCallback Status
PAIDPAID
PENDINGPENDING
PROCESSINGPENDING
EXPIREDFAILED
FAILEDFAILED
CANCELLEDFAILED
REFUNDEDFAILED
note

PENDING callbacks may fire while an order is still awaiting payment. Always wait for PAID before fulfilling orders.

Timezone​

All timestamps in Legacy API callbacks are in WIB (Asia/Jakarta, UTC+7). Format: "YYYY-MM-DD HH:MM:SS".

Responding​

Return HTTP 200 to acknowledge. Any other response triggers a retry on the same schedule as V1 webhooks:

AttemptDelay
1Immediate
2+30s
3+2m
4+10m
5+30m
6 (final)+2h

Callback Signature​

The signature is in the JSON body, as in UTpay:

signature = lowercase_hex(SHA512(clientId + "|" + orderId + "|" + refCode + "|" + status + "|" + clientSecret))

Compare it in constant time and use the query endpoint when reconciliation is required.

Minimal Handler​

PHP — minimal callback handler
<?php
$payload = json_decode(file_get_contents('php://input'), true);

http_response_code(200);
echo json_encode(['received' => true]);

if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
}

if ($payload['status'] === 'PAID') {
// Lookup your order by $payload['orderId']
// Mark as paid in your database
}