tgpay cryptoAPI
crypto-payapiwebhookssignature

Tài liệu API: webhook

5 phút đọcCập nhật lần cuối: 2 thg 11, 2026

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 khiPayload
invoice_paidmột hóa đơn được thanh toán — không cần bật riêngđối tượng invoice
invoice_expiredmột hóa đơn quá hạn mà chưa ai trảđối tượng invoice
check_activatedmộ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_expiredLì 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_completedlệ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_returnedlệ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_completedmột lần hoàn tiền được thực hiệnđối tượng invoice kèm các trường refunded_*
subscription_activatedmột người dùng đăng ký góiđối tượng subscription
subscription_chargedmộ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_cancelledmột gói đăng ký bị hủy, dù do bên nàođối tượng subscription
subscription_expiredhế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_id trướ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.