Webhook keluar¶
Berlanggan bisa push peristiwa siklus hidup lisensi ke produkmu secara
real-time. Ini opsional dan melengkapi — bukan menggantikan — heartbeat
pull POST /v1/validate, yang tetap jadi sumber
kebenaran status lisensi.
Mengaktifkan¶
Di Dashboard seller → Produk → (produk) → Developer webhook, isi:
| Kolom | Fungsi |
|---|---|
| Webhook URL | Endpoint HTTPS yang menerima request POST. |
| Signing secret | Secret bersama untuk menandatangani tiap request (HMAC-SHA256). Jangan simpan di source control. |
Kosongkan URL untuk menonaktifkan. Peristiwa hanya dikirim untuk produk yang
punya deliverable license_key.
Peristiwa¶
| Peristiwa | Dikirim saat |
|---|---|
license.issued |
Order berbayar memprovisi lisensi (order.paid). |
license.renewed |
Langganan diperpanjang satu periode (subscription.renewed). |
license.suspended |
Langganan ditangguhkan dan akses dicabut (subscription.suspended). |
Ketiganya membawa bentuk payload yang sama.
Request¶
POST https://app-kamu.example.com/berlanggan/webhook
Content-Type: application/json
User-Agent: Berlanggan-Webhook/1.0
X-Berlanggan-Signature: sha256=1f8ac10f23c5b8...
{
"event": "license.issued",
"order_public_id": "ord_9F3K2M7QP1",
"customer_email": "buyer@example.com",
"license_key": "XXXX-XXXX-XXXX",
"plan": "InTourney Pro",
"license_expires_at": "2027-06-01T00:00:00Z",
"entitlements": { "SEATS": "5", "API_ACCESS": "true" },
"seat_limit": 1
}
| Kolom | Catatan |
|---|---|
event |
Salah satu dari tiga nama peristiwa di atas. |
order_public_id |
Order yang membuat atau terakhir memperpanjang lisensi. Bisa "" untuk langganan lama tanpa order terkait. |
customer_email |
E-mail akun pembeli. |
license_key |
XXXX-XXXX-XXXX (Crockford Base32). |
plan |
Nama plan saat pengiriman. |
license_expires_at |
ISO-8601 UTC, atau null untuk lisensi permanen (sekali beli). Pada license.renewed ini adalah akhir periode yang baru. |
entitlements |
Map datar {key: value} — nilai yang sama seperti dari API aktivasi. Pakai ini untuk feature gating. |
seat_limit |
Jumlah seat lisensi (integer). |
Memverifikasi tanda tangan¶
X-Berlanggan-Signature adalah sha256= diikuti hex HMAC-SHA256 dari raw
body request, dengan kunci signing secret-mu. Skema ini sama dengan yang
dipakai Berlanggan untuk memverifikasi webhook masuk dari Duitku, Sumopod, dan
kirim.chat.
Verifikasi terhadap byte persis yang kamu terima — jangan serialisasi ulang JSON-nya dulu.
const crypto = require("crypto");
function verify(rawBody, header, secret) {
const provided = (header || "").replace(/^sha256=/, "");
const expected = crypto.createHmac("sha256", secret)
.update(rawBody).digest("hex");
return provided.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(provided), Buffer.from(expected));
}
Merespons¶
- Balas 2xx segera setelah peristiwa tersimpan. Kerjakan proses lambat secara asinkron.
- Respons non-2xx, timeout (10 dtk), atau error koneksi memicu retry dengan exponential backoff (hingga ~6 percobaan selama kira-kira satu jam).
- Pengiriman bersifat at-least-once dan bisa datang tidak berurutan.
Deduplikasi berdasarkan
(event, order_public_id, license_key)dan selalu percayailicense_expires_at/entitlementsyang paling baru.
Checklist keamanan¶
- [ ] Tolak request yang tanda tangannya tidak valid.
- [ ] Tolak request tanpa HTTPS.
- [ ] Rotasi signing secret bila diduga bocor (ubah di dashboard, lalu deploy nilai baru).
- [ ] Anggap webhook sebagai petunjuk — konfirmasi perubahan status kritis
lewat
POST /v1/validatesebelum mengunci akses pelanggan secara permanen.