Developer

Webhook sự kiện Hosted Link

✧ Mở bằng AI

Sao chép tài liệu, mở AI rồi dán vào cuộc trò chuyện.

ChatGPTClaudePerplexityGrok
≡ Xem Markdown

Webhook này thông báo tiến trình Hosted Link và trạng thái tài khoản. Webhook giao dịch tiền vào/ra có cấu hình và giao thức riêng.

Cấu hình nơi nhận

Trong Merchant → BankHub → Thiết lập → Webhook event — trạng thái liên kết, lưu URL HTTPS và token webhook. Token gồm 32–128 ký tự chữ, số, _, -; đây là token webhook, không phải API token. Endpoint phải công khai, HTTPS cổng 443; không chuyển hướng sang URL khác.

Một URL có thể nhận cả hai nhóm bên dưới. Phân loại theo event; payload hiện tại không có trường category và không có tùy chọn đăng ký từng nhóm. Return URL chỉ đưa trình duyệt quay lại, không nhận POST webhook.

Hai nhóm sự kiện

Tiến trình phiên

Event Ý nghĩa
LINK_TOKEN_CREATED Đã tạo phiên/link
LINK_SESSION_INITIALIZED Khách mở phiên lần đầu
LINK_SESSION_STARTED Khách đồng ý và bắt đầu
LINK_SESSION_OTP_REQUIRED Chờ OTP
LINK_SESSION_VERIFYING Đang xác minh OTP
LINK_SESSION_COMPLETED Hoàn tất phiên; đọc purpose và unlink_mode
LINK_SESSION_CANCELLED Khách hủy phiên
LINK_SESSION_FAILED Phiên thất bại
LINK_SESSION_EXPIRED Phiên hết hạn
LINK_SESSION_REVOKED Phiên bị thu hồi
LINK_SESSION_UNCERTAIN Chưa xác định kết quả, cần đối soát
LINK_SESSION_STATE_CHANGED Thông báo trạng thái tổng quát được giữ để tương thích

Kết quả và trạng thái tài khoản

Event Ý nghĩa
BANK_ACCOUNT_LINKED Liên kết thành công
BANK_ACCOUNT_UNLINKED Đã xác nhận hủy phía ngân hàng; UAT là giả lập
BANK_ACCOUNT_INACTIVED Vô hiệu hóa tại Pay2S, không xác nhận hủy phía ngân hàng
BANK_ACCOUNT_REACTIVATED Bật lại tại Pay2S
BANK_ACCOUNT_ENABLED Bộ đồng bộ quan sát tài khoản hoạt động trở lại
BANK_ACCOUNT_DISABLED Bộ đồng bộ quan sát tài khoản bị vô hiệu hóa
BANK_ACCOUNT_DISCONNECTED Bộ đồng bộ quan sát tài khoản bị xóa/ẩn
BANK_ACCOUNT_STATE_SYNCED Đồng bộ trạng thái ban đầu của tài khoản cũ

Thông báo quan sát trạng thái có account_status, previous_account_status, bank_unlink_confirmed; không suy ra đã hủy phía ngân hàng khi bank_unlink_confirmed=false. Thay đổi từ màn hình Pay2S bên ngoài Hosted Link được phát hiện qua đối soát định kỳ, có thể trễ hơn event phát trực tiếp từ phiên.

Payload mẫu

{
  "event_id": "0123456789abcdef0123456789abcdef",
  "version": "1.0",
  "timestamp": 1791691200,
  "environment": "uat",
  "event": "BANK_ACCOUNT_LINKED",
  "metadata": {
    "session_id": "SESSION_ID",
    "application_id": 2,
    "customer_reference": "CUSTOMER_DEMO",
    "bank_code": "ACB",
    "status": "connected",
    "purpose": "LINK_BANK_ACCOUNT",
    "state": "FINISHED_BANK_ACCOUNT_LINK",
    "unlink_mode": "bank",
    "account": {
      "id": 1,
      "bank_code": "ACB",
      "account_number": "P2S99999999",
      "user_bank_id": 1,
      "simulated": 1
    },
    "bank_profile": "acb_personal",
    "account_type": "personal",
    "connection_type": "openapi"
  },
  "sequence": 1
}

event_id xác định một event và giữ nguyên khi retry. sequence là thứ tự phát sinh, có thể có khoảng trống; không đảm bảo thứ tự nhận. timestamp trong body là lúc tạo event, không đổi khi retry. Giữ số tài khoản dưới dạng chuỗi để bảo toàn số 0 đầu.

Các phiên dùng loại ngân hàng mới có thêm metadata.bank_profile, metadata.account_type (personal, business, household) và metadata.connection_type (openapi, rpa). Những trường này bổ sung thông tin, không chứa username, mật khẩu hoặc OTP. Các phiên ACB cá nhân cũ có thể không có các trường bổ sung.

Xác thực webhook

Content-Type: application/json
Authorization: Bearer WEBHOOK_TOKEN
X-Pay2S-Event-Id: EVENT_ID
X-Pay2S-Timestamp: DELIVERY_UNIX_TIMESTAMP
X-Pay2S-Signature: v1=HEX_HMAC_SHA256

Chữ ký là HMAC-SHA256 với khóa bằng token webhook và chuỗi đầu vào:

X-Pay2S-Timestamp + "." + X-Pay2S-Event-Id + "." + raw_request_body
  1. Kiểm tra Bearer token bằng phép so sánh an toàn.
  2. Kiểm tra timestamp header; có thể dùng cửa sổ 5 phút nếu đồng hồ server đồng bộ. Mỗi lần gửi có timestamp và chữ ký mới, không kiểm tra độ mới bằng timestamp trong body.
  3. Tính HMAC trên body nguyên bản, không parse rồi JSON-encode lại; so sánh chữ ký an toàn.
  4. Parse JSON, đối chiếu event ID header với body, environment, metadata.application_id và khách/phiên thuộc hệ thống của bạn.
  5. Lưu event bền vững với khóa duy nhất (environment, application_id, event_id). Chỉ trả 2xx khi lưu thành công; xử lý nghiệp vụ trong hàng đợi riêng.

Ví dụ công thức PHP (các biến lấy từ header/body nguyên bản):

$expected = 'v1=' . hash_hmac(
    'sha256',
    $timestampHeader . '.' . $eventIdHeader . '.' . $rawBody,
    $webhookToken
);
$validSignature = hash_equals($expected, $signatureHeader);

Đoạn này chỉ minh họa chữ ký, cần thực hiện đầy đủ các bước kiểm tra và lưu bền vững ở trên. Không ghi token, OTP hoặc toàn bộ dữ liệu tài khoản vào log.

Chống trùng và thứ tự

Khi nào retry và khi nào dừng?

Kết quả gửi Xử lý
HTTP 2xx, không lỗi transport delivered, dừng retry
Timeout/lỗi mạng hoặc HTTP ngoài 2xx Tự retry; 3xx không được tự chuyển hướng, 4xx cũng retry hiện tại
Hết 8 lần gửi tổng cộng failed, dừng tự động
Thiếu URL/token hoặc ứng dụng không còn đủ quyền hoạt động skipped, không tự retry

Khoảng chờ sau mỗi lần thất bại: 1 phút, 2 phút, 5 phút, 10 phút, 30 phút, 60 phút, 120 phút. Mỗi request có timeout tổng 8 giây. HTTP 200 kèm body {"success":false} vẫn được tính thành công: worker không đọc JSON phản hồi.

Nút Gửi lại trong lịch sử sự kiện cho phép gửi lại event failed/skipped sau khi sửa cấu hình; giữ nguyên event ID và đặt lại bộ đếm lần gửi. Cấu hình URL mới không tự phát lại toàn bộ lịch sử skipped.

Hệ thống dùng hàng đợi bền vững và worker chạy liên tục để gửi event mới và retry đến hạn. Không cam kết thời gian giao cố định khi có backlog hoặc endpoint phản hồi chậm. Có thể nhận trùng nếu bên nhận đã lưu nhưng phản hồi bị mất, nên luôn chống trùng.

Kiểm tra với Webhook.site

So sánh event_id trong JSON, không dùng mã request của công cụ. Nhiều request với ID khác nhau có thể là các bước của cùng phiên. Nếu cùng event ID lặp, xem Số lần gửi / HTTP trong lịch sử sự kiện Pay2S. Chỉ gửi thử dữ liệu phù hợp với môi trường thử nghiệm.

Request và response đối chiếu ngày 11/10/2026

Các mẫu dưới đây giữ cấu trúc trường và kiểu dữ liệu. Khóa, chữ ký, URL phiên đã được thay bằng placeholder; danh sách chỉ giữ tối đa hai phần tử và dữ liệu ảnh/PDF/XML dài được rút gọn. Không sao chép placeholder để gọi API thật.

Đối chiếu mã nguồn backend; giá trị minh họa. Payload theo BankHubEvents.php. ACK minh họa từ receiver; webhook có Bearer token và chữ ký HMAC trên raw body.

POST {webhook_url}

Body:

{
  "event_id": "0123456789abcdef0123456789abcdef",
  "version": "1.0",
  "timestamp": 1791691200,
  "environment": "uat",
  "event": "BANK_ACCOUNT_LINKED",
  "metadata": {
    "session_id": "SESSION_ID",
    "application_id": 2,
    "customer_reference": "CUSTOMER_DEMO",
    "bank_code": "ACB",
    "status": "connected",
    "purpose": "LINK_BANK_ACCOUNT",
    "state": "FINISHED_BANK_ACCOUNT_LINK",
    "unlink_mode": "bank",
    "account": {
      "id": 1,
      "bank_code": "ACB",
      "account_number": "P2S99999999",
      "user_bank_id": 1,
      "simulated": 1
    },
    "bank_profile": "acb_personal",
    "account_type": "personal",
    "connection_type": "openapi"
  },
  "sequence": 1
}

HTTP 200. Response:

{
  "success": true
}
Tìm trong 57 trang tài liệu · Esc để đóng