Cấu hình cửa hàng

Xác thực webhook

Kiểm chữ ký X-Hubport-Signature của webhook Hubport gửi tới hệ thống của bạn

Khi bạn khai báo Webhook trong Cài đặt > Webhook (ví dụ sự kiện order.add), Hubport gửi POST tới URL bạn khai mỗi khi sự kiện xảy ra. Mỗi request đều được ký HMAC-SHA256 bằng khoá ký (signing secret) riêng của webhook đó, để hệ thống của bạn chắc chắn request đến từ Hubport và nội dung không bị sửa.

Đơn hàng từ sàn (Shopee, Lazada, TikTok Shop, Tiki)

Theo chính sách dữ liệu của các sàn, thông tin cá nhân người mua của đơn có source là sàn (11 Shopee, 13 Lazada, 15 TikTok Shop, 17 Tiki) được che trong nội dung webhook (kể cả khi bạn bấm "Gửi lại webhook"): họ tên dạng N*** V*** A***, số điện thoại dạng 09xx***678, email dạng a***@..., địa chỉ chỉ còn quận/huyện và tỉnh/thành, shipping_address_line_2, shipping_instruction, note, tax_name, tax_code, tax_address thành ***. Nội dung có thêm trường buyer_pii_masked: 1. Mã đơn, sản phẩm, số tiền, trạng thái giữ nguyên. Đơn từ các nguồn khác không đổi.

Khoá ký (signing secret)

  • Hệ thống tự sinh khi tạo webhook, dạng whsec_.... Xem ở form sửa webhook (ô "Khoá ký"), hoặc trường meta_data.webhook.secret khi đọc chi tiết webhook.
  • Khoá giữ nguyên khi bạn sửa webhook. Bạn không tự đặt được khoá.
  • Lộ khoá: tạo khoá mới bằng POST /v1/hooks/{id}/rotatesecret (quyền setting.advanced); khoá cũ hết hiệu lực ngay.

Header gửi kèm

HeaderMô tả
X-Hubport-Signaturet=<unix timestamp>,v1=<chữ ký hex>
CP-Webhook-EventMã sự kiện, ví dụ order.add
CP-Webhook-TimeThời điểm gửi (unix timestamp), bằng giá trị t ở trên
CP-Webhook-RetrySố lần đã gửi trước đó của sự kiện này (0 là lần đầu)
Content-Typeapplication/json

Header tuỳ chỉnh bạn khai trong webhook cũng được gửi kèm (trừ các header hệ thống ở trên, Host, Content-Length).

Cách kiểm chữ ký

  1. Đọc nguyên văn body request (chuỗi byte, chưa parse JSON).
  2. Tách t và v1 từ header X-Hubport-Signature.
  3. Tính HMAC-SHA256(secret, "<t>.<body>"), mã hoá hex chữ thường.
  4. So sánh với v1 bằng hàm so sánh hằng thời gian. Khác nhau thì trả 401 và bỏ qua request.
  5. Nên từ chối request có t lệch quá 5 phút so với giờ máy chủ của bạn (chống gửi lại).
$body = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_X_HUBPORT_SIGNATURE'] ?? ''), $sig);
$expected = hash_hmac('sha256', ($sig['t'] ?? '') . '.' . $body, $secret);

if (!hash_equals($expected, $sig['v1'] ?? '') || abs(time() - (int)($sig['t'] ?? 0)) > 300) {
    http_response_code(401);
    exit;
}
// Node.js (Express): dùng express.raw() để lấy body nguyên văn
const crypto = require('crypto');
const sig = Object.fromEntries(req.get('X-Hubport-Signature').split(',').map((p) => p.split('=')));
const expected = crypto.createHmac('sha256', secret).update(`${sig.t}.${req.body}`).digest('hex');
const ok = sig.v1 && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig.v1))
  && Math.abs(Date.now() / 1000 - Number(sig.t)) <= 300;

Gửi lại khi lỗi

  • Hệ thống coi là thành công khi bạn trả HTTP 2xx. Mã khác, hết thời gian (30 giây) hoặc lỗi kết nối thì gửi lại theo lịch lùi dần (30 giây, 1 phút, 2 phút...), tối đa 12 lần, sau đó bỏ sự kiện.
  • Hubport không theo redirect (3xx tính là lỗi). URL phải là http/https; trên Hubport SaaS URL phải là https tới máy chủ công khai (URL trỏ vào địa chỉ nội bộ bị từ chối khi lưu với lỗi error_webhook_url_not_allowed).
  • Mỗi sự kiện có thể tới nhiều lần (ví dụ lần trước bạn xử lý xong nhưng trả lỗi): xử lý theo kiểu idempotent.

On this page