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:00Z",
"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 đượ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
các trang tài liệu tương ứng).
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 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; hãy phân biệt payload bằng kind trước khi dùng ID. 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 bất kỳ trước khi lần gửi hết thời gian chờ. Phản hồi sớm và xử lý tác vụ dài riê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 với khoảng cách tăng dần, tới giới hạn số lần của nền tảng. Không bảo đảm sự kiện được giao; dùng
getInvoicesđể đối soát thanh toán sau sự cố. - 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.