Tài liệu API: hóa đơn và hoàn tiền
Các phương thức về hóa đơn: createInvoice, getInvoices, deleteInvoice,
refundInvoice. Các quy ước chung (xác thực, envelope, 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 lần mỗi phút.
| Tham số | Kiểu | Bắt buộc | Ý nghĩa |
|---|---|---|---|
currency_type | string | không | crypto (mặc định) hoặc fiat |
asset | string | chế độ crypto | tài sản dùng để thu tiền, ví dụ USDT. Không được đi cùng fiat |
fiat | string | chế độ fiat | loại tiền pháp định dùng để định giá (các dòng is_fiat của getCurrencies) |
accepted_assets | string / array | không | chỉ ở 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ợ |
amount | string | có, trừ khi đặt open_amount | chuỗ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_amount | boolean | không | phầ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 |
description | string | không | tối đa 1024 ký tự, hiện cho người thanh toán |
hidden_message | string | không | tối đa 2048 ký tự, chỉ lộ ra cho người thanh toán sau khi trả |
payload | string | không | tố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_comments | boolean | không | cho phép người thanh toán đính kèm lời nhắn (mặc định true) |
allow_anonymous | boolean | không | cho phép người thanh toán giấu danh tính (mặc định true) |
paid_btn_name | string | không | nút hiện sau khi trả: viewItem, openChannel, openBot hoặc callback |
paid_btn_url | string | không | URL http(s) của nút — bắt buộc khi đã đặt paid_btn_name |
swap_to | string | không | tự động quy đổi các khoản thanh toán nhận được sang tài sản này. Chỉ ở mức nỗ lực tối đa: 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_in | integer | không | số 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 = không bao giờ hết hạn |
rate_lock_seconds | integer | không | phầ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) |
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 trongpay_url),status(active/paid/expired),pay_url— liên kếtt.mebạn gửi cho người thanh toán (bot_invoice_url,mini_app_invoice_url,web_app_invoice_urllà 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;nullkhi 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ổ (feevàusd_ratelà 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(nullkhi 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_untilvàrate_lock_rates— các tỷ giá đã chốt theo từng tài sản,nullkhi 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_atlà 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,
404 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. Họ cũng có thể nạp tiền cho hóa đơn từ ví bên ngoài: ứng dụng
hiện cho họ một địa chỉ nạp, khoản chuyển về ví của chính họ, và hóa đơn tự động
được thanh toán ngay khi tiền tới. Kiểu nào thì bạn cũng thấy y hệt nhau: một
hóa đơn paid bình thường và một webhook invoice_paid — không có tham số hay
trường nào phải xử lý thêm.
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 có lợi cho bạn, nên bạn không bao giờ nhận ít hơn mệnh giá tiền pháp định. Nếu không 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ủ giới hạn khoảng này trong phạm vi
từ 60 giây tới mức tối đa của nền tảng (hiện là 15 phút).
Khi khóa hết hiệu lực, hóa đơn vẫn trả được và lặng lẽ quay về quy đổi tại thời
điểm thanh toán. Nếu bạn muốn hóa đơn hết hạn cùng lúc với mức giá đã báo, hãy
đặt expires_in bằng đúng giá trị đó.
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 lần mỗi phút.
Đây là phần mở rộng so với Crypto Bot: trả mệnh giá của một hóa đơn đã thanh
toán — hoặc một phần trong đó — từ số dư ứng dụng của bạn về đúng người đã trả,
kể cả người thanh toán ẩn danh, mà không lộ ra họ là ai.
| Tham số | Kiểu | Bắt buộc | Ý nghĩa |
|---|---|---|---|
invoice_id | integer | có | hóa đơn đã thanh toán |
amount | string | không | số tiền cần hoàn, tính theo tài sản của hóa đơn. Bỏ trống = toàn bộ phần chưa hoàn còn lại. Các lần hoàn một phần cộng dồn tới tối đa là mệnh giá |
spend_id | string | không | khóa idempotency — hãy dùng nó, để một lần thử lại sau khi hết thời gian chờ được phát lại 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. Mỗi lần hoàn tiền đều
bắn 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.
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.