Webhook

Terima permintaan HTTPS bertanda tangan saat penjualan, pesanan, stok, produk, atau pelanggan berubah, dan verifikasi di Node atau PHP.

Webhook adalah permintaan yang dikirim Bakasir ke server Anda saat ada perubahan, sehingga Anda tidak perlu terus memeriksa API. Tambahkan endpoint di Pengaturan → Pengembang → Webhook. Satu bisnis dapat memiliki hingga 20 endpoint, dan tiap endpoint memilih sendiri event yang diinginkan.

Syarat endpoint

  • Alamat harus diawali https:// dan menuju server publik. Alamat jaringan privat, localhost, dan alamat metadata cloud ditolak.
  • Bakasir tidak mengikuti pengalihan. Jawab tepat di alamat yang Anda simpan.
  • Balas dengan status 2xx apa pun dalam 10 detik. Selain itu dianggap gagal.

Event

EventDikirim saat
sale.createdPenjualan tercatat di kanal mana pun
sale.updatedPenjualan berubah, misalnya selesai, dibatalkan, atau direfund
order.createdPesanan online dibuat
order.updatedStatus atau pembayaran pesanan online berubah
stock.updatedStok sebuah produk di outlet berubah
product.created, product.updated, product.deletedProduk dibuat, diubah, atau dihapus
customer.created, customer.updated, customer.deletedPelanggan dibuat, diubah, atau dihapus

Anda juga bisa berlangganan satu grup seperti product.*, atau semuanya dengan *. Perubahan di outlet Demo tidak pernah mengirim event. Beberapa perubahan pada data yang sama dalam satu operasi dikirim sebagai satu event.

Permintaannya

Bakasir mengirim POST dengan isi JSON:

{
  "id": "evt_5f2b0c7e91d34a0c8d1e6b27",
  "type": "product.updated",
  "created_at": "2026-11-04T08:30:00Z",
  "data": {
    "id": "01K9ZT6Q2X3M8N4V7B5C1D0EFG",
    "name": "Es Kopi Susu",
    "price": 24000
  }
}

data berbentuk sama dengan objek API yang sesuai (produk, penjualan beserta itemnya, pesanan online beserta barisnya, pelanggan). Untuk stock.updated isinya baris stok dengan store_id, product_id, variant_id, qty, dan avg_cost. Untuk data yang dihapus hanya berisi id. Data dibaca saat pengiriman pertama dicoba lalu disimpan, jadi percobaan ulang mengirim isi yang sama.

Header:

HeaderNilai
Bakasir-EventTipe event
Bakasir-Event-IdId event, sama dengan id di isi. Pakai untuk mengabaikan duplikat
Bakasir-DeliveryId pengiriman
Bakasir-Attempt1 untuk percobaan pertama, lalu 2, 3, dan seterusnya
Bakasir-Signaturet=<waktu unix>,v1=<tanda tangan hex>

Verifikasi tanda tangan

Setiap permintaan ditandatangani dengan secret endpoint, yang ditampilkan sekali saat dibuat atau diganti. Hitung HMAC SHA-256 dari teks <t>.<isi mentah> dengan secret itu, lalu bandingkan dengan v1 secara waktu konstan. Pakai isi mentah persis seperti diterima, sebelum diurai sebagai JSON, dan tolak permintaan yang t-nya lebih dari lima menit lalu.

Node.js:

import crypto from "node:crypto";

export function verifyBakasir(rawBody, header, secret, toleranceSeconds = 300) {
    const parts = Object.fromEntries(header.split(",").map((pair) => pair.split("=")));
    if (!parts.t || !parts.v1) return false;
    if (Math.abs(Date.now() / 1000 - Number(parts.t)) > toleranceSeconds) return false;
    const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest();
    const given = Buffer.from(parts.v1, "hex");
    return given.length === expected.length && crypto.timingSafeEqual(given, expected);
}

Dengan Express, baca isi mentah memakai express.raw({ type: "application/json" }) lalu berikan req.body.toString("utf8").

PHP:

<?php

function verifyBakasir(string $rawBody, string $header, string $secret, int $tolerance = 300): bool
{
    $parts = [];
    foreach (explode(",", $header) as $pair) {
        [$key, $value] = array_pad(explode("=", $pair, 2), 2, "");
        $parts[$key] = $value;
    }
    if (!isset($parts["t"], $parts["v1"]) || abs(time() - (int) $parts["t"]) > $tolerance) {
        return false;
    }
    $expected = hash_hmac("sha256", $parts["t"] . "." . $rawBody, $secret);
    return hash_equals($expected, $parts["v1"]);
}

$raw = file_get_contents("php://input");
$signature = $_SERVER["HTTP_BAKASIR_SIGNATURE"] ?? "";
if (!verifyBakasir($raw, $signature, getenv("BAKASIR_WEBHOOK_SECRET"))) {
    http_response_code(401);
    exit;
}
http_response_code(204);

Percobaan ulang dan log pengiriman

Jika endpoint Anda gagal atau kehabisan waktu, Bakasir mencoba lagi 6 kali, jadi total hingga 7 percobaan dalam sekitar 21 jam: setelah 1 menit, 5 menit, 30 menit, 2 jam, 6 jam, dan 12 jam. Urutan tidak dijamin dan sebuah event bisa tiba lebih dari sekali, jadi pakai Bakasir-Event-Id untuk mengabaikan pengulangan dan created_at untuk mengabaikan kondisi yang lebih lama.

Setelah percobaan terakhir gagal, pengiriman ditandai gagal dan pemilik menerima notifikasi. Anda dapat mengulang pengiriman yang gagal secara manual.

Pengaturan → Pengembang → Pengiriman mencantumkan setiap pengiriman selama 30 hari beserta status, jumlah percobaan, jawaban server Anda, dan error terakhir. Pakai Kirim tes pada endpoint untuk mengirim event webhook.test bertanda tangan saat itu juga.

Ganti atau hentikan

  • Ganti secret langsung menerbitkan secret baru. Perbarui server Anda lebih dulu atau terima keduanya sebentar.
  • Matikan endpoint untuk menjeda, atau hapus untuk menghentikan semua event dan menghapus lognya.
  • Jika Anda mereset bisnis ke pengaturan pabrik, semua kunci dan endpoint dihapus.
Bagaimana panduan ini?

Terakhir diperbarui