Webhook
Byl нь шинэ event бүрийг webhook хүсэлтээр таны бакэнд системд цаг алдалгүй хүргэнэ.
Хүсэлт бүр криптограф гарын үсэгтэй байх тул үүнийг шалгах замаар яг манайхаас ирсэн эсэхийг хялбар шалган хамгаалалт хийх боломжтой юм.
Webhook-г POST хүсэлт илгээн дуудах бөгөөд payload нь тухайн event-ийн төрөл болон хамаарах объектын мэдээллийг багтаасан байна.
Payload-ийн бүтэц
Бүх event ижил бүтэцтэй JSON байна:
| Талбар | Тайлбар |
|---|---|
id | Event-ийн дахин давтагдашгүй дугаар. |
project_id | Event хамаарах төслийн ID. |
type | Event-ийн төрөл (жш: invoice.paid). |
object | data.object-т ямар объект байгааг заана (жш: invoice). |
data.object | Event хамаарах объект бүтнээрээ (нэхэмжлэх, checkout гэх мэт). |
created_at | Event үүссэн огноо. |
Давхардсан event-ээс сэргийлэх
Ховор тохиолдолд нэг event танд нэгээс олон удаа очиж болзошгүй (жишээ нь таны сервер хариу өгсөн ч сүлжээний алдаагаар бидэнд хүрээгүй үед). Боловсруулсан event-ийн id-г хадгалж, өмнө нь ирсэн id-тэй event-ийг алгасахыг зөвлөж байна (idempotency).
Төсөлд webhook тохируулах
Удирдлагын буланд төслийнхөө Webhook цэсээр орж шинээр бүртгэнэ. Нэг төсөлд өөрийн идэвхжүүлсэн багцын хязгаар хүртэлх хэдэн ч тооны webhook тохируулан ашиглах боломжтой.
Ямар ч хаяг тохируулан ашиглах боломжтой ч https-ээр хамгаалагдсан url хаягийг тохируулан ашиглахыг бид танд зөвлөж байна.
Хамгаалалт ба гарын үсэг
Бүх webhook хүсэлтүүд Byl-Signature header-тэй очих бөгөөд энэ нь тухайн event-ийн дахин давтагдашгүй гарын үсэг юм.
Бид гарын үсгийг хүсэлтийн түүхий (raw) body-гоор дараах байдлаар үүсгэдэг:
$payload = $request->getContent(); // түүхий body. Задлаад дахин encode хийхгүй.
$computedSignature = hash_hmac('sha256', $payload, $secret);Иймд таны бакэнд систем ирсэн хүсэлтийн түүхий body-гоор гарын үсгийг дахин үүсгээд Byl-Signature header-ээр ирсэн гарын үсэгтэй харьцуулах хэрэгтэй. JSON-ыг задлаад дахин json_encode хийвэл түлхүүрийн дараалал өөрчлөгдөж гарын үсэг таарахгүй болох магадлалтай.
Харьцуулахдаа === биш, тогтмол хугацааны (constant-time) харьцуулалт ашиглана уу — PHP-д hash_equals(), Node.js-д crypto.timingSafeEqual():
if (! hash_equals($computedSignature, $request->header('Byl-Signature'))) {
abort(401);
}Та гарын үсгийн secret мэдээллийг удирдлагын булан дахь webhook-ийн дэлгэрэнгүй хуудаснаас харах боломжтой. secret нь багийн бүх төсөлд нэг ижил байна.
Гуравдагч этгээдийг зүй бус үйлдлээс сэргийлэхийн тулд хүсэлт бүрийг заавал шалгаж байхыг бид танд зөвлөж байна.
Жишээ nodejs гарын үсэг үүсгэх код:
const crypto = require("crypto");
let secret = "your_secret_here";
let payload = rawRequestBody; // the raw body string, not JSON.stringify(req.body)
let computedSignature = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");Жишээ апп
Хэрэв холболт хийсэн жишээ код хэрэгтэй бол дараах repository-г харна уу:
- NodeJS - Express example
Webhook хүсэлтийн алдаа
Хэрэв webhook хүсэлт илгээх үед таны серверээс HTTP/200 хариу ирсэн бол бид амжилттайд тооцдог. Өөр хариу ирсэн эсвэл хүсэлт хугацаа хэтэрсэн бол алдаатай гэж үзэн 1 цагийн дараа дахин илгээдэг.
Нийт 3 удаа амжилтгүй болсон үед дахин илгээхээ зогсоох болно. Амжилтгүй хүсэлтийн талаарх мэдээллийг удирдлагын булангаас харах боломжтой. Хэрэв та алдаагаа зассан бол дахин илгээх товчлуур дараарай.
Удирдлагын буланд сүүлд илгээсэн хүсэлтүүд, тэдгээрийн payload болон таны серверээс ямар хариу ирснийг харах боломжтой.
Webhook event-үүд
Дараах төрлийн event-үүдийг бид одоогоор илгээж байна.
| Event | Тайлбар |
|---|---|
invoice.paid | Нэхэмжлэх амжилттай төлөгдсөн. |
invoice.void | Нэхэмжлэх хүчингүй болгогдсон. |
checkout.completed | Checkout амжилттай төлөгдсөн. |
subscription.created | Шинэ багц үүссэн. |
subscription.renewed | Багцын мөчлөг сунгагдсан. |
subscription.updated | Багц солигдсон, цуцлалт хүссэн эсвэл буцсан. |
subscription.renewal_due | Сунгалтын сануулга илгээгдсэн. |
subscription.past_due | Багцын хугацаа хэтэрсэн (grace period). |
subscription.canceled | Багц бүрэн цуцлагдсан. |
invoice.paid
Нэхэмжлэх амжилттай төлөгдсөн үед invoice.paid төрөлтэй event илгээх болно. data.object-т төлөгдсөн нэхэмжлэхийн объект очих болно.
{
"id": 3,
"project_id": 1,
"type": "invoice.paid",
"object": "invoice",
"data": {
"object": {
"id": 71,
"amount": 10,
"number": "TEST-0003",
"status": "paid",
"due_date": "2025-08-08T16:14:07.000000Z",
"created_at": "2025-08-07T16:14:07.000000Z",
"project_id": 1,
"updated_at": "2025-08-07T16:14:16.000000Z",
"description": "First invoice",
"url": "https://byl.mn/h/invoice/5708/XN3GbRBxTslkMCeDj10CJtqlHiPfcmZ8"
}
},
"created_at": "2025-08-07T16:14:16.000000Z",
"updated_at": "2025-08-07T16:14:16.000000Z"
}checkout.completed
Checkout амжилттай төлөгдсөн үед checkout.completed төрөлтэй event илгээх болно. data.object-т төлөгдсөн checkout объект очих болно.
{
"id": 59,
"project_id": 5,
"type": "checkout.completed",
"object": "checkout",
"data": {
"object": {
"id": 13338,
"url": "https://byl.mn/h/checkout/13338/Yi7smBuk",
"items": [
{
"id": 69,
"price": {
"id": 17,
"product": {
"id": 15,
"name": "Product 1",
"created_at": "2025-08-03T10:15:50.000000Z",
"project_id": 5,
"updated_at": "2025-08-03T10:15:50.000000Z",
"client_reference_id": null
},
"created_at": "2025-08-03T10:17:07.000000Z",
"product_id": 15,
"updated_at": "2025-08-03T10:17:07.000000Z",
"unit_amount": 50
},
"price_id": 17,
"quantity": 1,
"created_at": "2025-08-03T10:17:07.000000Z",
"updated_at": "2025-08-03T10:17:07.000000Z",
"amount_unit": 50,
"checkout_id": 64,
"amount_total": 50,
"amount_subtotal": 50
}
],
"status": "complete",
"is_guest": true,
"cancel_url": null,
"created_at": "2025-08-03T10:17:07.000000Z",
"expires_at": "2025-08-03T16:00:00.000000Z",
"project_id": 5,
"receipt_id": null,
"updated_at": "2025-08-03T10:18:43.000000Z",
"customer_id": null,
"success_url": "https://will.iam/success",
"amount_total": 50,
"phone_number": "99999999",
"customer_email": "[email protected]",
"amount_subtotal": 50,
"payment_method": "qpay",
"client_reference_id": null,
"phone_number_collection": true,
"email_collection": true,
"coupon_codes": [
{
"code": "SUMMER2025",
"coupon_name": "Summer Sale",
"discount_amount": "10.00",
"redeemed_at": "2025-08-03T10:18:43.000000Z"
}
]
}
},
"created_at": "2025-08-03T10:18:43.000000Z",
"updated_at": "2025-08-03T10:18:43.000000Z"
}coupon_codes талбар нь checkout-д хэрэглэгдсэн хөнгөлөлтийн кодуудын мэдээллийг агуулна. Хэрэв checkout-д хөнгөлөлтийн код хэрэглэгдээгүй бол энэ талбар хоосон массив байна.
coupon_codes массивын элемент бүр дараах талбаруудыг агуулна:
code: Хөнгөлөлтийн кодcoupon_name: Хөнгөлөлтийн нэрdiscount_amount: Хөнгөлөлтийн дүнredeemed_at: Хөнгөлөлт хэрэглэгдсэн огноо
subscription.*
Багцын төлөвт өөрчлөлт орох бүрд subscription. угтвартай event илгээгдэнэ (төрлүүдийг дээрх хүснэгтээс харна уу). data.object-т багцын объект харилцагч, бүтээгдэхүүн, үнийн мэдээллийн хамт очно.
{
"id": 87,
"project_id": 1,
"type": "subscription.renewed",
"object": "subscription",
"data": {
"object": {
"id": 4,
"project_id": 1,
"status": "active",
"customer": {
"id": 12,
"client_reference_id": "user_842",
"name": "Бат-Эрдэнэ",
"email": "[email protected]"
},
"product": {
"id": 7,
"name": "Starter багц",
"client_reference_id": null
},
"price": {
"id": 3,
"type": "recurring",
"unit_amount": 30000,
"recurring_interval": "month",
"recurring_interval_count": 1
},
"current_period_start": "2026-07-23T04:10:00.000000Z",
"current_period_end": "2026-08-23T15:59:59.000000Z",
"trial_ends_at": null,
"canceled_at": null,
"is_test": false,
"created_at": "2026-06-23T04:10:00.000000Z",
"updated_at": "2026-07-23T04:10:00.000000Z"
}
},
"created_at": "2026-07-23T04:10:00.000000Z",
"updated_at": "2026-07-23T04:10:00.000000Z"
}Хэрэглэгчийн эрхийг subscription.canceled ирэх үед хаана — цуцлалт хүссэн ч мөчлөг дуустал эрх хүчинтэй байдаг тул subscription.updated (canceled_at бөглөгдсөн) дээр эрх хаахгүй. Дэлгэрэнгүйг Subscriptions хуудаснаас уншина уу.