tgpay cryptoAPI
crypto-payapiinvoicesrefunds

Tài liệu API: hóa đơn và hoàn tiền

8 phút đọcCập nhật lần cuối: 9 thg 10, 2026

Các phương thức về hóa đơn: createInvoice, getInvoices, deleteInvoice, refundInvoice. Các quy ước chung (xác thực, cấu trúc phản hồi, số tiền, spend_id) nằm ở trang tài liệu Merchant API; phần hướng dẫn từng bước là Nhận thanh toán bằng hóa đơn.

createInvoice

POST /pay/api/createInvoice — scope invoices, giới hạn 60 mỗi phút.

Tham sốKiểuBắt buộcÝ nghĩa
currency_typestringkhôngcrypto (mặc định) hoặc fiat
assetstringchế độ cryptotài sản dùng để thu tiền, ví dụ USDT. Dùng tham số này thay cho fiat
fiatstringchế độ fiatloại tiền pháp định dùng để định giá (các dòng is_fiat của getCurrencies)
accepted_assetsstring / arraykhôngchỉ ở chế độ fiat: các tài sản người thanh toán được phép trả — một chuỗi phân tách bằng dấu phẩy ("USDT,GRAM") hoặc một mảng JSON. Bỏ trống = mọi tài sản được hỗ trợ
amountstringcó, trừ khi đặt open_amountchuỗi thập phân dương: đơn vị tài sản ở chế độ crypto, đơn vị tiền pháp định (tối đa 2 chữ số thập phân) ở chế độ fiat
open_amountbooleankhôngphần mở rộng, chỉ ở chế độ crypto: không cố định số tiền — người thanh toán tự nhập lúc trả (quyên góp và tiền tip). Loại trừ lẫn nhau với amount
descriptionstringkhôngtối đa 1024 ký tự, hiện cho người thanh toán
hidden_messagestringkhôngtối đa 2048 ký tự, chỉ lộ ra cho người thanh toán sau khi trả
payloadstringkhôngtối đa 4096 ký tự dữ liệu của riêng bạn, được trả lại nguyên vẹn trên hóa đơn và webhook
allow_commentsbooleankhôngcho phép người thanh toán đính kèm lời nhắn (mặc định true)
allow_anonymousbooleankhôngcho phép người thanh toán giấu danh tính (mặc định true)
paid_btn_namestringkhôngnút hiện sau khi trả: viewItem, openChannel, openBot hoặc callback
paid_btn_urlstringkhôngURL http(s) của nút — bắt buộc khi đã đặt paid_btn_name
swap_tostringkhôngtự động quy đổi các khoản thanh toán nhận được sang tài sản này. Không bảo đảm quy đổi thành công: nếu lúc thanh toán không quy đổi được thì khoản thanh toán vẫn thành công, chỉ là chưa quy đổi
expires_inintegerkhôngsố giây tới khi hóa đơn hết hạn, tối đa 2678400 (31 ngày); bỏ trống hoặc 0 = thời hạn mặc định. Đọc expiration_date trong kết quả
rate_lock_secondsintegerkhôngphần mở rộng, chỉ ở chế độ fiat: đóng băng tỷ giá quy đổi ngay lúc tạo, trong khoảng thời gian này (xem bên dưới)

Mô tả, nội dung sau thanh toán và nút bấm có thể bị ẩn với người trả khi đang được kiểm tra hoặc chưa được duyệt. Số tiền hóa đơn vẫn có thể được thanh toán.

Kết quả — và cũng là payload của webhook invoice_paid — là đối tượng invoice. Các trường chính của nó:

  • Danh tính và trạng thái: invoice_id, hash (id công khai nằm trong pay_url), status (active / paid / expired), pay_url — liên kết t.me bạn gửi cho người thanh toán (bot_invoice_url, mini_app_invoice_url, web_app_invoice_url là các tên gọi thay thế của nó).
  • Số tiền: amount (mệnh giá — đơn vị tiền pháp định trên hóa đơn fiat, còn lại là crypto; null khi hóa đơn số tiền mở chưa được trả), amount_minor, và trên hóa đơn fiat đã thanh toán còn có paid_asset / paid_amount / paid_fiat_rate — số crypto thực sự bị thu và tỷ giá đã dùng.
  • Phí: fee_asset / fee_amount, chốt tại thời điểm thanh toán — con số chuẩn để ghi sổ (fee và usd_rate là tên gọi Crypto Bot cũ, không nên dùng nữa). Xem phí và hạn mức.
  • Người thanh toán: paid_by_user_id (null khi họ chọn ẩn danh), paid_anonymously, comment.
  • Hoàn tiền (phần mở rộng): refunded_amount / refunded_minor (cộng dồn) và refunded_at, ghi nhận khi đã hoàn đủ toàn bộ.
  • Khóa tỷ giá (phần mở rộng): rate_lock_until và rate_lock_rates — các tỷ giá đã chốt theo từng tài sản, null khi không yêu cầu khóa.
  • Như lúc tạo: description, hidden_message, payload, paid_btn_name / paid_btn_url, expiration_date (expires_at là tên gọi thay thế), các trường quy đổi (swap_to, is_swapped, swapped_to, swapped_rate, swapped_output, …).

Lỗi: 400 invalid_currency (lẫn lộn asset với fiat), 400 invalid_amount, 400 unknown_asset, 400 unsupported_fiat, 400 paid_btn_url_required, và với yêu cầu khóa tỷ giá thì có 400 rate_lock_fiat_only, 409 ratelock_disabled, 409 rate_unavailable (không có tỷ giá mới cho một tài sản được chấp nhận — hãy thử lại).

Hóa đơn được thanh toán thế nào

Người thanh toán trả bằng số dư ví của họ trong Mini App — tức thì, không phí mạng lưới, nếu đủ số dư và đã xác nhận thanh toán. Họ cũng có thể nạp tiền cho hóa đơn từ ví bên ngoài: nếu có tùy chọn này, ứng dụng hiển thị địa chỉ nạp và hạn thanh toán. Tiền được nạp vào ví của chính người trả. Tự động thanh toán yêu cầu có khoản nạp vào địa chỉ đó, đủ số dư khả dụng, tài khoản không bị hạn chế chi tiền, đồng thời yêu cầu thanh toán và hóa đơn chưa hết hạn. Khoản nạp đến muộn vẫn ở trong ví; nó không chứng minh hóa đơn đã được trả. Cả hai cách đều cho bạn một hóa đơn paid thông thường và sự kiện invoice_paid nếu đã cấu hình webhook URL — không cần xử lý thêm tham số hay trường nào.

Trên hóa đơn fiat, số crypto được tính tại thời điểm thanh toán và làm tròn lên theo đơn vị nhỏ nhất của tài sản đã chọn. Phí dành cho người bán sau đó được trừ từ số crypto này. Nếu không có tỷ giá đã khóa còn hiệu lực hoặc tỷ giá mới, khoản thanh toán thất bại ở phía người trả chứ không chạy theo một tỷ giá cũ.

Khóa tỷ giá trên hóa đơn fiat

Truyền rate_lock_seconds để chốt tỷ giá hiện tại của mọi tài sản được chấp nhận ngay lúc tạo hóa đơn. Trong lúc khóa còn hiệu lực, người thanh toán thấy đúng các số tiền đã chốt và khoản thanh toán quy đổi theo tỷ giá đã chốt — bạn gánh rủi ro tỷ giá suốt khoảng đó. Máy chủ điều chỉnh thời lượng yêu cầu cho phù hợp với phạm vi hỗ trợ. Đọc rate_lock_until trong kết quả để biết hạn thực tế.

Khi khóa hết hiệu lực, hóa đơn chưa hết hạn vẫn có thể được trả theo tỷ giá lúc thanh toán. Muốn hai thời hạn trùng nhau, hãy dùng expires_in trong phạm vi thời lượng khóa được hỗ trợ và đối chiếu expiration_date với rate_lock_until trong phản hồi.

getInvoices

GET /pay/api/getInvoices — scope read. Bộ lọc: asset, fiat, invoice_ids (phân tách bằng dấu phẩy), status (active / paid / expired — expired là phần mở rộng; active loại ra các hóa đơn đã quá hạn), cùng offset / count. Trả về {"items": [invoice, …]}, mới nhất trước.

deleteInvoice

POST /pay/api/deleteInvoice — scope invoices. Một tham số: invoice_id. Hủy một hóa đơn chưa thanh toán và trả về true. Lỗi: 404 invoice_not_found, 409 invoice_already_paid — hóa đơn đã trả thì không xóa được, tiền đã đi rồi.

refundInvoice

POST /pay/api/refundInvoice — scope refunds, giới hạn 30 mỗi phút. Đây là phần mở rộng so với Crypto Bot: hoàn toàn bộ hoặc một phần số crypto thực tế đã trả từ số dư ứng dụng về người thanh toán, kể cả người trả ẩn danh mà không lộ danh tính. Với hóa đơn fiat, dùng paid_asset và paid_amount; hoàn tiền không phải một lần quy đổi tiền pháp định mới.

Tham sốKiểuBắt buộcÝ nghĩa
invoice_idintegercóhóa đơn đã thanh toán
amountstringkhôngsố crypto cần hoàn, theo tài sản đã trả ban đầu (paid_asset với hóa đơn fiat). Bỏ trống = toàn bộ phần chưa hoàn. Tổng các lần hoàn không vượt quá số crypto đã trả ban đầu
spend_idstringkhôngkhóa idempotency — hãy dùng nó, để yêu cầu gửi lại sau khi hết thời gian chờ trả về kết quả đã lưu thay vì hoàn tiền hai lần

Kết quả là đối tượng invoice đã cập nhật, với refunded_amount / refunded_minor cộng dồn; refunded_at được ghi nhận khi hóa đơn đã hoàn đủ. Trạng thái vẫn là paid. Phí dịch vụ không được hoàn. Ứng dụng cần đủ số dư ở tài sản thanh toán ban đầu, kể cả khi swap_to đã quy đổi khoản thu sang tài sản khác. Mỗi lần hoàn tiền đều gửi webhook refund_completed — loại cần bật thủ công.

Lỗi: 404 invoice_not_found, 409 invoice_not_paid, 409 already_refunded (không còn gì để hoàn), 409 amount_too_big (lớn hơn phần chưa hoàn còn lại), 409 insufficient_funds, 400 invalid_amount, và cặp lỗi của spend_id: 409 idempotency_conflict / 409 idempotency_in_progress.