Lewati ke isi

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.

import hashlib, hmac

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    provided = (header or "").removeprefix("sha256=")
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, provided)
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));
}
function verify(string $rawBody, string $header, string $secret): bool {
    $provided = preg_replace('/^sha256=/', '', $header);
    $expected = hash_hmac('sha256', $rawBody, $secret);
    return hash_equals($expected, $provided);
}

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 percayai license_expires_at / entitlements yang 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/validate sebelum mengunci akses pelanggan secara permanen.