Bỏ qua để đến nội dung

Webhook: nhận sự kiện đặt phòng

API cho đối tácAdd-on
Tính năng này thuộc gói Add-on

Bạn vẫn đọc được đầy đủ hướng dẫn bên dưới. Khi cần bật, nhắn Zalo OA GoHost — CS sẽ giúp trong 1 ngày làm việc.

Webhook gửi POST tới URL HTTPS của bạn khi một đặt phòng của cơ sở được tạo, sửa, huỷ, nhận hoặc trả phòng — kể cả đơn do lễ tân hay kênh OTA tạo, không chỉ đơn tạo qua API. Bạn quản lý webhook bằng chính API, theo từng cơ sở, với scope webhooks:read / webhooks:write (key cũ chưa có hai scope này — xin GoHost thêm).

Sự kiệnKhi nào
booking.createdĐặt phòng mới
booking.updatedĐặt phòng bị sửa (không gửi khi cùng thao tác đã có một sự kiện bên dưới)
booking.confirmedĐơn được xác nhận qua API (Confirm Booking); xác nhận trên GoHost đến dưới dạng booking.updated
booking.cancelledĐơn bị huỷ
booking.checked_inKhách nhận phòng
booking.checked_outKhách trả phòng
webhook.pingGửi thử bằng endpoint Ping — không đăng ký được
Terminal window
curl -s -X POST "$GOHOST_API/properties/<tenant_id>/webhooks" \
-H "Authorization: Bearer $GOHOST_KEY" -H "Content-Type: application/json" \
-d '{ "url": "https://partner.example.com/gohost/webhook",
"events": ["booking.created", "booking.cancelled"] }'

Phản hồi có secret dạng whsec_… — chỉ hiện một lần, lưu lại để kiểm chữ ký. Body đầy đủ: Store Webhook Endpoint.

Ràng buộc: URL phải là https (cổng 443), không chứa user:pass, không trỏ vào địa chỉ mạng nội bộ; GoHost không theo redirect (3xx tính là thất bại). Mỗi key, trên mỗi cơ sở: tối đa 5 endpoint đang bật, mỗi URL đăng ký một lần.

Gửi thử: Ping → nhận sự kiện webhook.ping.

{
"id": "evt_01j…",
"type": "booking.created",
"created_at": "2026-11-20T14:05:09+07:00",
"tenant_id": "<tenant_id>",
"data": { "id": "…", "…": "…" }
}
  • data có cùng cấu trúc với Get Booking Detail.
  • created_at theo múi giờ của cơ sở.
  • id khác nhau cho từng endpoint. Dùng nó để bỏ qua sự kiện trùng — cùng một sự kiện có thể tới nhiều lần khi GoHost gửi lại.

Header đi kèm: X-Gohost-Signature, X-Gohost-Event-Id, X-Gohost-Event, X-Gohost-Timestamp, User-Agent: GoHost-Webhook/1.0.

X-Gohost-Signature có dạng t=<unix timestamp>,v1=<chữ ký>, trong đó chữ ký là HMAC-SHA256 (hex) của chuỗi "<t>.<raw body>" với khoá là secret của endpoint.

  1. Tách t và v1 từ header.
  2. Tính HMAC-SHA256 của t + "." + body gốc (đúng byte nhận được, chưa parse JSON).
  3. So sánh với v1 bằng hàm so sánh an toàn thời gian.
  4. Từ chối nếu t lệch giờ máy bạn quá 5 phút (chống gửi lại).
// Node.js — Express: dùng express.raw({ type: 'application/json' }) để giữ nguyên body
import crypto from 'node:crypto';
function verifyGohostSignature(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map((kv) => kv.split('=')));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(parts.v1 ?? '');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
// PHP
function verifyGohostSignature(string $rawBody, string $header, string $secret): bool {
parse_str(str_replace(',', '&', $header), $p);
$t = (int) ($p['t'] ?? 0);
if ($t === 0 || abs(time() - $t) > 300) return false;
$expected = hash_hmac('sha256', $t . '.' . $rawBody, $secret);
return hash_equals($expected, $p['v1'] ?? '');
}

Đổi secret: Rotate Secret — secret cũ mất hiệu lực ngay, cập nhật bên bạn trước khi gọi.

  • Trả mã 2xx trong 5 giây là thành công. Xử lý nặng thì đưa vào hàng đợi rồi trả 200 ngay.
  • Không thành công: ngoài lần gửi đầu, GoHost gửi lại tối đa 8 lần, cách nhau 5 giây, 30 giây, 2 phút, 10 phút, 30 phút, 2 giờ, 6 giờ, 24 giờ (tổng 9 lần thử).
  • Trả 410 Gone: endpoint bị tắt ngay. 8 sự kiện liên tiếp đều thất bại hết 9 lần thử: endpoint tự tắt — bật lại bằng Update Webhook Endpoint (is_active: true).
  • Xem lịch sử gửi (giữ 14 ngày): Deliveries.
  • API key bị tắt thì mọi webhook của key đó cũng tắt theo và không bật lại được.
Bài này có giúp được bạn không?