Tài liệu API: webhook
Webhook đẩy sự kiện về máy chủ của bạn sau khi chúng xảy ra. Hãy đặt Webhook URL (không bắt buộc) cho ứng dụng của bạn ở Thêm → Merchant API; từ đó nền tảng POST một JSON body có chữ ký cho mỗi sự kiện bạn đã đăng ký.
Cấu trúc sự kiện
{
"update_id": 123,
"update_type": "invoice_paid",
"request_date": "2026-08-11T12:00:00.123456Z",
"payload": { … }
}
update_id giữ nguyên qua các lần gửi lại của cùng một sự kiện — hãy chống
trùng theo trường này. request_date (UTC, ISO 8601 với độ chính xác đến micro giây) được ghi theo từng lần gửi.
payload là đối tượng đầy đủ ứng với loại sự kiện: đối tượng invoice cho các sự
kiện hóa đơn, đối tượng check, transfer hoặc subscription (cấu trúc nằm trên
trang của từng phương thức: hóa đơn, chuyển tiền và Lì xì,
gói đăng ký).
Các loại sự kiện
update_type | Được gửi khi | Payload |
|---|---|---|
invoice_paid | một hóa đơn được thanh toán — không cần bật riêng | đối tượng invoice |
invoice_expired | một hóa đơn quá hạn mà chưa ai trả | đối tượng invoice |
check_activated | một Lì xì hoặc phần của một người nhận được nhận | đối tượng check; lượt nhận theo danh sách còn có activated_slot |
check_expired | Lì xì theo danh sách hết hạn, phần chưa nhận được trả về ứng dụng | đối tượng check kèm status: "expired", recipients và các giá trị claimed_at |
transfer_completed | lệnh chuyển hoàn tất hoặc khoản chi trả qua blockchain được xác nhận | đối tượng transfer hoặc withdrawal |
transfer_returned | lệnh chuyển đang chờ chưa được nhận được trả về ứng dụng, hoặc khoản chi trả qua blockchain thất bại và tiền tạm giữ được trả lại | đối tượng transfer hoặc withdrawal |
refund_completed | một lần hoàn tiền được thực hiện | đối tượng invoice kèm các trường refunded_* |
subscription_activated | một người dùng đăng ký gói | đối tượng subscription |
subscription_charged | một kỳ được thu tiền — charge.kind cho biết là kỳ nào: initial, renewal hoặc resubscribe | đối tượng subscription + charge: {period_no, kind, asset, amount, fee, paid_at} |
subscription_cancelled | một gói đăng ký bị hủy, dù do bên nào | đối tượng subscription |
subscription_expired | hết thời gian gia hạn mà vẫn chưa trả | đối tượng subscription |
Khoản chi trả qua blockchain tạo trong ứng dụng cũng dùng các sự kiện transfer này. Payload có kind: "withdrawal", các trường mạng như network, address, txid và trạng thái completed hoặc failed. transfer_id của chúng thuộc dãy ID riêng. Chỉ dạng này mới có kind: payload của một lần chuyển tiền thông thường hoàn toàn không có trường kind, nên hãy coi payload có kind: "withdrawal" là khoản chi trả qua blockchain và payload không có kind là chuyển tiền. Tài liệu OpenAPI tải xuống mô tả cả hai dạng payload.
Mọi loại trừ invoice_paid đều cần bật thủ công (một phần mở rộng so với
Crypto Bot — phần tích hợp chỉ xử lý các sự kiện chuẩn của Crypto Bot sẽ không nhận
update_type khác trừ khi bạn chủ động bật thêm). Bật cho từng ứng dụng ở mục Sự
kiện webhook bổ sung trong mục Webhook của ứng dụng — các nút gạt dùng đúng định danh trong bảng trên. Lưu webhook URL để nhận sự kiện.
Kiểm tra chữ ký
Mỗi lần gửi đều mang header TgPayCrypto-API-Signature
(TgCryptoPay-API-Signature và Crypto-Pay-API-Signature là tên gọi thay thế
để tương thích, cùng một giá trị): chuỗi hex HMAC-SHA256 của nội dung thô trong yêu cầu, dùng khóa là bản băm
SHA-256 của token API chính của ứng dụng. Đây đúng là cơ chế của Crypto Bot,
nên code kiểm tra sẵn có chạy nguyên vẹn:
import hashlib, hmac
secret = hashlib.sha256(API_TOKEN.encode()).digest()
expected = hmac.new(secret, raw_body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, headers["TgPayCrypto-API-Signature"])
Hãy kiểm tra trên đúng các byte thô nhận được — một bản đã phân tích rồi tuần tự hóa lại có thể khác từng byte và làm hỏng phép kiểm tra. Chỉ token chính mới ký; token giới hạn quyền không bao giờ ký. Đổi token chính là đổi luôn khóa ký webhook ngay lập tức, nên hãy cập nhật secret trên máy chủ của bạn trong cùng một lượt.
Gửi và thử lại
- Một lần gửi được tính là thành công khi nhận được phản hồi 2xx trong vòng 10 giây. Phản hồi sớm và xử lý tác vụ dài riêng. Hệ thống không gửi tiếp yêu cầu tới URL chuyển hướng: phản hồi 3xx bị tính là lỗi, vì vậy hãy đăng ký URL cuối cùng.
- Mã lỗi, hết thời gian chờ hoặc lỗi kết nối khiến hệ thống thử lại: tối đa 17 lần, khoảng chờ bắt đầu từ 10 giây và tăng gấp đôi tới 8 giờ (có chút ngẫu nhiên), tổng cộng khoảng 3 ngày. Không bảo đảm sự kiện sẽ được gửi đến máy chủ của bạn; dùng
getInvoicesđể đối soát thanh toán sau sự cố. - Mỗi lần thử được gửi tới webhook URL đang lưu tại thời điểm đó: URL đã đổi sẽ nhận các lần thử lại còn chờ, còn xóa URL thì các lần đó bị hủy.
- Sau lần cuối cùng, lần gửi đó bị bỏ. Bản thân webhook URL không bao giờ bị tự động tắt — một endpoint chập chờn không âm thầm hủy đăng ký ứng dụng của bạn.
- Vì có thử lại, handler của bạn phải idempotent: hãy chống trùng theo
update_idtrước khi làm gì.
⚠️ Kiểm tra trước khi giao hàng
Ai cũng POST được tới webhook URL của bạn. Cho tới khi chữ ký được xác minh
thành công, hãy coi body là dữ liệu không đáng tin: đừng giao đơn hàng, đừng
cộng tiền cho ai, đừng đánh dấu bất cứ gì là đã trả. Thứ tự an toàn là: kiểm tra
chữ ký → chống trùng theo update_id → rồi mới hành động.
Bài viết này có giúp được bạn không?
Cảm ơn phản hồi của bạn.