Webhook Guide
Cara menerima notifikasi otomatis dari Bukasir saat status transaksi berubah.
Cara Kerja Webhook
Ketika status transaksi berubah, Midtrans mengirimkan notifikasi ke Bukasir. Bukasir memverifikasi signature (SHA512) dari Midtrans, lalu memperbarui status transaksi di database. Setelah itu, Bukasir me-relay notifikasi asli dari Midtrans ke webhook URL yang sudah Anda daftarkan.
Alur lengkap:
- Midtrans mengirim POST notification ke Bukasir (
/api/webhook) - Bukasir memverifikasi signature Midtrans (SHA512)
- Bukasir memperbarui status transaksi di database (mapping status Midtrans ke status platform)
- Bukasir me-relay notifikasi asli Midtrans ke
webhook_urlmerchant Anda - Field
signature_keydihapus dari payload, selain itu dikirim apa adanya
signature_key yang dihapus).
Setup Webhook
- Login ke dashboard Bukasir
- Buka Pengaturan > Profil
- Isi field Webhook URL (contoh:
https://yourdomain.com/webhook) - Klik Simpan
Verifikasi Sumber Webhook
Setiap webhook request dari Bukasir disertai header X-Bukasir-Webhook: 1 dan User-Agent: Bukasir-WebhookRelay/1.0. Gunakan header ini untuk memverifikasi bahwa request benar-benar berasal dari Bukasir.
X-Bukasir-Webhook: 1.
PHP
<?php
$bukasirHeader = $_SERVER['HTTP_X_BUKASIR_WEBHOOK'] ?? '';
// Verifikasi sumber request
if ($bukasirHeader !== '1') {
http_response_code(403);
die('Unauthorized');
}
$data = json_decode(file_get_contents('php://input'), true);
$orderId = $data['order_id'];
$status = $data['transaction_status']; // bukan 'status'
$grossAmount = $data['gross_amount']; // string, misal "50350.00"
echo json_encode(['success' => true]);
?>
Node.js / Express
app.post('/webhook', express.json(), (req, res) => {
const bukasirHeader = req.headers['x-bukasir-webhook'];
if (bukasirHeader !== '1') {
return res.status(403).json({ error: 'Unauthorized' });
}
const { order_id, transaction_status, gross_amount } = req.body;
console.log(`Payment ${order_id}: ${transaction_status}`);
// Update database, kirim email, dll.
res.json({ success: true });
});
Payload Format
Payload yang dikirimkan adalah notifikasi asli dari Midtrans (tanpa field signature_key). Berikut contoh payload yang akan Anda terima:
{
"transaction_time": "2026-07-23 14:30:00",
"transaction_status": "settlement",
"transaction_id": "abc123-def456-ghi789",
"status_code": "200",
"order_id": "ORDER-001",
"gross_amount": "50350.00",
"payment_type": "qris",
"fraud_status": "accept",
"settlement_time": "2026-07-23 14:30:15"
}
Field Deskripsi
| Field | Type | Deskripsi |
|---|---|---|
order_id |
string | Unique identifier transaksi |
transaction_status |
string | Status transaksi dari Midtrans (bukan status platform Bukasir) |
transaction_id |
string | ID transaksi dari Midtrans |
status_code |
string | HTTP-like status code dari Midtrans (misal "200") |
gross_amount |
string | Total yang dibayarkan, format string desimal (misal "50350.00") |
payment_type |
string | Metode pembayaran: qris, bank_transfer, gopay, dll. |
fraud_status |
string | Status fraud: accept, challenge, deny |
transaction_time |
string | Waktu transaksi dibuat (format: YYYY-MM-DD HH:MM:SS) |
settlement_time |
string | Waktu pembayaran diselesaikan (format: YYYY-MM-DD HH:MM:SS) |
Status Pembayaran
Webhook meneruskan status dari Midtrans (transaction_status). Perhatikan bahwa ini berbeda dari status platform Bukasir (completed, expired, cancelled, failed).
| Status Midtrans | Deskripsi | Aksi yang Disarankan |
|---|---|---|
pending |
Menunggu pembayaran dari customer | Tunggu — jangan proses pesanan |
capture |
Transaksi berhasil di-capture (kartu kredit) | Cek fraud_status sebelum proses |
settlement |
Pembayaran berhasil diterima | Proses pesanan / kirim barang |
expire |
Pembayaran melewati batas waktu | Batalkan pesanan / informasikan ke customer |
cancel |
Transaksi dibatalkan | Tidak perlu aksi tambahan |
deny |
Pembayaran ditolak oleh provider | Hubungi customer untuk pembayaran ulang |
refund |
Transaksi di-refund | Update status pesanan dan informasikan customer |
Retries & Reliability
Bukasir akan melakukan retry pengiriman webhook hingga 5 kali jika server Anda tidak merespons dengan status 200. Interval retry: 1 menit, 5 menit, 30 menit, 2 jam, 24 jam.
- Selalu kirim response 200 secepat mungkin. Proses berat bisa dilakukan secara async.
- Implementasikan idempotency — webhook yang sama mungkin dikirim lebih dari sekali.
- Gunakan database untuk mencatat order_id dan status yang sudah diproses.
Contoh Implementasi Lengkap
<?php
// webhook.php — Implementasi lengkap dengan logging
// 1. Verifikasi sumber
$bukasirHeader = $_SERVER['HTTP_X_BUKASIR_WEBHOOK'] ?? '';
if ($bukasirHeader !== '1') {
http_response_code(403);
error_log("[Bukasir Webhook] Unauthorized from " . $_SERVER['REMOTE_ADDR']);
die('Forbidden');
}
$data = json_decode(file_get_contents('php://input'), true);
// Log untuk debugging
error_log("[Bukasir Webhook] Order: " . $data['order_id']
. " Status: " . $data['transaction_status']);
// 2. Cek idempotency — jangan proses dua kali
$orderId = $data['order_id'];
$status = $data['transaction_status'];
// 3. Proses berdasarkan status Midtrans
switch ($status) {
case 'capture':
// Kartu kredit — cek fraud_status
if ($data['fraud_status'] === 'accept') {
updateOrderStatus($orderId, 'paid');
}
break;
case 'settlement':
// Pembayaran berhasil — update order, kirim email
updateOrderStatus($orderId, 'paid');
sendConfirmationEmail($data);
break;
case 'expire':
// Pembayaran expired — update order
updateOrderStatus($orderId, 'expired');
break;
case 'cancel':
case 'deny':
// Dibatalkan atau ditolak
updateOrderStatus($orderId, $status);
break;
case 'refund':
// Transaksi di-refund
updateOrderStatus($orderId, 'refunded');
break;
}
http_response_code(200);
echo json_encode(['success' => true]);
?>