Callback Mechanism
Callbacks are sent to merchants when transaction status changes (e.g., payment received, expired, failed). The callback includes transaction details and payment method-specific information.
HTTP Headers
The following headers are sent with each callback:
Content-Type: application/json
X-Signature: <HMAC-SHA256 signature>
X-Timestamp: <ISO8601 timestamp>
X-Callback-Id: <UUID of callback log entry>The signature is generated using HMAC-SHA256 with the business unit's secret API key.
Base Callback Structure
All callbacks follow this structure:
{
"timestamp": "2026-01-04T10:30:00Z",
"data": {
"transactionId": "uuid-of-transaction",
"partnerReferenceNo": "merchant-reference-number",
"status": "PAID|EXPIRED|FAILED|PENDING",
"amount": "100000.00",
"currency": "IDR",
"paymentMethod": "VA|QRIS|CC|WECHATPAY|ALIPAY",
"bank": "BNI|BRI|BCA|CIMB|WECHATPAY|ALIPAY",
"paidAt": "2026-01-04T10:30:00Z",
"expiredAt": "2026-01-04T11:30:00Z"
}
}1. Virtual Account (VA)
Payment Method: VA
{
"timestamp": "2026-01-04T10:30:00Z",
"data": {
"transactionId": "550e8400-e29b-41d4-a716-446655440000",
"partnerReferenceNo": "ORDER-123456",
"status": "PAID",
"amount": "150000.00",
"currency": "IDR",
"paymentMethod": "VA",
"bank": "BNI",
"paidAt": "2026-01-04T10:30:00Z",
"expiredAt": "2026-01-04T11:30:00Z"
}
}2. QRIS (QR Indonesian Standard)
Payment Method: QRIS
{
"timestamp": "2026-01-04T10:30:00Z",
"data": {
"transactionId": "550e8400-e29b-41d4-a716-446655440001",
"partnerReferenceNo": "ORDER-123457",
"status": "PAID",
"amount": "250000.00",
"currency": "IDR",
"paymentMethod": "QRIS",
"bank": "CIMB",
"paidAt": "2026-01-04T10:30:00Z",
"expiredAt": "2026-01-04T10:35:00Z"
}
}3. Credit Card (CC)
Payment Method: CC
For credit card payments, the callback includes provider-specific metadata received from the bank.
{
"timestamp": "2026-01-04T10:30:00Z",
"data": {
"transactionId": "550e8400-e29b-41d4-a716-446655440002",
"partnerReferenceNo": "ORDER-123458",
"status": "PAID",
"amount": "500000.00",
"currency": "IDR",
"paymentMethod": "CC",
"bank": "CIMB",
"paidAt": "2026-01-04T10:30:00Z",
"expiredAt": "2026-01-04T10:40:00Z"
}
}4. WeChat Pay
Payment Method: WECHATPAY
WeChat Pay is processed as a QR payment method. The callback structure is similar to QRIS but with payment method set to WECHATPAY.
{
"timestamp": "2026-01-04T10:30:00Z",
"data": {
"transactionId": "550e8400-e29b-41d4-a716-446655440003",
"partnerReferenceNo": "ORDER-123459",
"status": "PAID",
"amount": "300000.00",
"currency": "IDR",
"paymentMethod": "WECHATPAY",
"bank": "WECHATPAY",
"paidAt": "2026-01-04T10:30:00Z",
"expiredAt": "2026-01-04T10:35:00Z"
}
}5. Alipay
Payment Method: ALIPAY
Alipay is also processed as a QR payment method, similar to WeChat Pay.
{
"timestamp": "2026-01-04T10:30:00Z",
"data": {
"transactionId": "550e8400-e29b-41d4-a716-446655440004",
"partnerReferenceNo": "ORDER-123460",
"status": "PAID",
"amount": "400000.00",
"currency": "IDR",
"paymentMethod": "ALIPAY",
"bank": "ALIPAY",
"paidAt": "2026-01-04T10:30:00Z",
"expiredAt": "2026-01-04T10:35:00Z"
}
}Status Values
Retry Mechanism
If the callback fails (non-2xx response), the system will retry up to 10 times with exponential backoff. Failed callbacks can be queried via the callback log API.
Security
- All callbacks are signed with HMAC-SHA256 using the business unit's secret API key
- Merchants should verify the
X-Signatureheader to ensure callback authenticity - Callbacks are sent over HTTPS only
Signature Verification Example
The secret used for signature verification is the Private Key from your business unit's API credentials. The Private Key has the format pk_xxxxxxxxxxxxxxxxx (starts with pk_ prefix).
Go
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
func verifyCallbackSignature(payload []byte, signature string, secret string) bool {
h := hmac.New(sha256.New, []byte(secret))
h.Write(payload)
expectedSignature := hex.EncodeToString(h.Sum(nil))
return hmac.Equal([]byte(signature), []byte(expectedSignature))
}PHP
<?php
/**
* Verify callback signature using HMAC-SHA256
*
* @param string $payload The raw request body
* @param string $signature The X-Signature header value
* @param string $secret The business unit's Private Key (format: pk_xxxxxxxxxxxxxxxxx)
* @return bool True if signature is valid
*/
function verifyCallbackSignature(string $payload, string $signature, string $secret): bool
{
$expectedSignature = hash_hmac('sha256', $payload, $secret);
return hash_equals($expectedSignature, $signature);
}
// Example usage:
// $payload = file_get_contents('php://input');
// $signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
// $secret = 'pk_xxxxxxxxxxxxxxxxx'; // Your Private Key
// $isValid = verifyCallbackSignature($payload, $signature, $secret);
?>