tgpay cryptoAPI
crypto-payapireferencetokens

Tài liệu Merchant API

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

Các quy ước chung của mọi phương thức, cùng các phương thức tra cứu chỉ đọc. Trang chi tiết từng nhóm: hóa đơn và hoàn tiền, chuyển tiền và Lì xì, gói đăng ký, webhook.

API tuân theo quy ước Crypto Bot cho các phương thức được mô tả ở đây. Đổi base URL và token, rồi thử các phương thức tích hợp đang dùng trên testnet. Các chức năng bổ sung được đánh dấu phần mở rộng bên dưới.

Bản đặc tả OpenAPI 3.1 máy đọc được cũng có tại: crypto-pay-openapi.yaml · crypto-pay-openapi.json. Hãy nạp nó vào trình sinh code, API client hoặc công cụ AI, nhưng hãy xem cả trang từng phương thức: đặc tả có thể chưa cập nhật các phần mở rộng mới.

Base URL và xác thực

Mọi phương thức đều nằm ở https://app.tgpaycrypto.com/pay/api/<methodName>. Trên mạng thử nghiệm, base là https://testnet.tgpaycrypto.com/pay/api — ứng dụng và token riêng, coin không có giá trị.

Hãy xác thực mọi yêu cầu bằng token đặt trong header TgPayCrypto-API-Token (TgCryptoPay-API-Token, tên dùng trước ngày 2026-09-07, và Crypto-Pay-API-Token được chấp nhận như tên gọi thay thế để tương thích; nếu gửi nhiều header cùng lúc thì header chuẩn được ưu tiên). Token thiếu, sai hoặc đã bị thu hồi — cũng như token giới hạn quyền đã hết hạn, token của ứng dụng đã xóa hoặc có chủ sở hữu bị chặn — trả về 401 unauthorized.

Token có dạng <app_id>:<secret> và chỉ hiện một lần, lúc tạo hoặc lúc đổi token mới; máy chủ chỉ lưu bản băm của nó. Xem Bắt đầu dành cho lập trình viên.

Token và scope

Hai loại thông tin xác thực dùng chung một cách xác thực:

  • Token chính — toàn quyền với mọi phương thức, và là khóa duy nhất ký webhook.
  • Token giới hạn quyền (phần mở rộng) — tối đa 10 token chưa thu hồi, kể cả token đã hết hạn, cho mỗi ứng dụng. Mở Thêm → Merchant API, chọn ứng dụng rồi mở Quyền truy cập và token. Tạo token trong mục Token giới hạn quyền, mỗi token có một nhãn và một tập scope con. Thu hồi một token là tức thì và không đụng tới các token khác; đổi token chính cũng không đụng tới chúng. Token có thể có hạn sử dụng; sau đó không còn xác thực được.
ScopePhương thức mà nó mở
(mọi token hợp lệ)getMe, getCurrencies, getExchangeRates
readgetBalance, getStats, getInvoices, getChecks, getTransfers, getSubscriptionPlans, getSubscriptions
invoicescreateInvoice, deleteInvoice
refundsrefundInvoice
payoutstransfer, transferBatch
checkscreateCheck, deleteCheck
subscriptionscreateSubscriptionPlan, archiveSubscriptionPlan, cancelSubscription
webhooksupdateApp

Các scope cộng dồn và độc lập với nhau — invoices không kéo theo read, nên máy chủ chỉ tạo hóa đơn có thể dùng token không có quyền xem số dư hoặc lịch sử thanh toán. Gọi phương thức nằm ngoài phạm vi quyền của token sẽ trả về 403 scope_required. getMe cho biết scopes của token đang dùng (null với token chính) và token_name, nên bạn có thể kiểm tra quyền của token đang dùng.

Chế độ chi trả

Scope quyết định token được gọi những phương thức nào; còn chế độ chi trả của ứng dụng quyết định bất kỳ token nào của nó — kể cả token chính — được chi trả cho ai. Một ứng dụng mới chi trả được cho bất kỳ ai ngay từ phút đầu; chủ sở hữu có thể giới hạn người nhận bằng cách mở ứng dụng trong Thêm → Merchant API, rồi vào Cài đặt chi trả → Chi trả qua API. Giống hạn mức hằng ngày, cài đặt này giới hạn những gì người có token bị lộ có thể làm. Muốn mở rộng lại phạm vi người nhận, cần mã PIN hoặc sinh trắc học; API chỉ đọc được chế độ này (getMe → payout_mode).

payout_modetransfer, transferBatch và createCheck được làm gì
anychi trả cho bất kỳ người dùng nào, tạm giữ khoản chi trả cho một @username chưa biết, tạo Lì xì không gán người nhận — mặc định, đúng như Crypto Bot
allowlistchỉ chi trả cho những người dùng Telegram có trong danh sách người nhận của ứng dụng. Mọi người nhận được gán sẵn hoặc nêu trong danh sách Lì xì đều phải được phép nhận. Người khác, @username chưa biết và Lì xì không gán người nhận đều nhận 403 recipient_not_allowed
offkhông gì cả — mọi lệnh gọi chi trả đều trả về 403 payouts_disabled; hóa đơn, gói đăng ký, hoàn tiền và webhook vẫn chạy bình thường

Khi một khoản chi trả bị từ chối, chủ sở hữu được báo trong Telegram (tối đa một lần mỗi giờ cho mỗi ứng dụng) và có thể đổi chế độ. Hạn mức chi trả mỗi ngày vẫn áp dụng bên cạnh chế độ này. Còn được chi trả cho những mục đích nào thì do điều khoản Merchant API quy định.

Yêu cầu và phản hồi

  • Các phương thức đọc dùng GET với tham số trên query string. Các phương thức động tới tiền chỉ nhận POST — tham số truyền dưới dạng JSON body, form-urlencoded hoặc query param (body thắng khi xung đột); multipart/form-data bị từ chối. Body có Content-Type khác (text/plain hoặc không có) sẽ bị bỏ qua và chỉ query param được đọc. Tham số dạng mảng (transferBatch.items, createCheck.recipients) chỉ dùng được trong JSON body.
  • Phản hồi do trình xử lý API trả về dùng chung một cấu trúc JSON: thành công {"ok": true, "result": …}, lỗi {"ok": false, "error": {"code": <HTTP status>, "name": "<error_name>"}}. Hãy rẽ nhánh theo error.name — đó là chuỗi máy đọc ổn định.
  • Tham số query sai kiểu trên phương thức GET (ví dụ offset=abc) có thể bị từ chối trước khi trình xử lý API chạy. Hãy kiểm tra mã trạng thái HTTP; đừng mặc định mọi phản hồi lỗi đều có cấu trúc trên. Gửi tham số query đúng kiểu dữ liệu yêu cầu.

Số tiền

  • Số tiền crypto là chuỗi thập phân theo đơn vị tài sản ("10.5") — không bao giờ là số JSON, kể cả trong yêu cầu: {"amount": 10.5} trả về 400 invalid_request; hãy gửi {"amount": "10.5"}. Điều này cũng áp dụng cho mọi tham số chuỗi khác (asset, spend_id, comment, …).
  • Số tiền có nhiều chữ số thập phân hơn tài sản cho phép sẽ được làm tròn tới độ chính xác của tài sản (làm tròn lên khi đúng nửa) chứ không bị từ chối; số tiền làm tròn thành 0 trả về 400 invalid_amount.
  • Phản hồi còn kèm amount_minor (phần mở rộng): giá trị nguyên theo đơn vị nhỏ nhất, dưới dạng chuỗi, vì số nguyên cỡ wei có thể vượt giới hạn biểu diễn số nguyên chính xác của Number trong JavaScript.
  • decimals của từng tài sản lấy từ getCurrencies — hãy để phần tính toán số tiền chạy theo đó thay vì gán cứng.
  • Số tiền pháp định có tối đa 2 chữ số thập phân.
  • Tỷ giá là chuỗi thập phân, không bao giờ là số.

Phân trang

Các phương thức danh sách (getInvoices, getChecks, getTransfers, getSubscriptions) nhận offset (mặc định 0) và count (mặc định 100, tối đa 1000; getSubscriptions tối đa 500) và trả về {"items": […]}, mới nhất trước. Bộ lọc theo ID (invoice_ids, check_ids, transfer_ids) là danh sách số nguyên phân tách bằng dấu phẩy. Mọi dấu thời gian đều là chuỗi ISO 8601.

Idempotency: spend_id

Các phương thức động tới tiền nhận một khóa spend_id do bên gọi tự sinh (1–64 ký tự): bắt buộc với transfer và với từng phần tử của transferBatch, tùy chọn với createCheck và refundInvoice (phần mở rộng — vẫn nên dùng). transfer và các phần tử của transferBatch dùng chung một không gian khóa. Thử lại với cùng khóa và cùng tham số sẽ trả về kết quả gốc thay vì chuyển tiền hai lần, nên một yêu cầu hết thời gian chờ luôn an toàn để thử lại. Cùng khóa nhưng khác tham số trả về 409 idempotency_conflict; thử lại trong lúc lần gọi gốc còn đang chạy trả về 409 idempotency_in_progress. Yêu cầu thất bại với lỗi 4xx không tiêu hao khóa: thử lại với cùng khóa sẽ chạy lại yêu cầu đó.

Giới hạn tần suất

Giới hạn tính theo từng ứng dụng, dùng chung cho mọi token của nó, và mỗi phương thức có bộ đếm riêng. Vượt giới hạn thì trả về 429 rate_limited:

Phương thứcGiới hạn
createInvoice, createCheck60 mỗi phút
refundInvoice30 mỗi phút
transfer30 mỗi phút
transferBatch10 mỗi phút
updateApp10 lần mỗi phút
createSubscriptionPlan20 lần mỗi phút

Thêm hai giới hạn nữa để chống dò đoán token hoặc người nhận:

  • Xác thực thất bại: sau 30 yêu cầu thiếu token hoặc token không hợp lệ từ cùng một địa chỉ IP trong một phút, mọi yêu cầu từ địa chỉ đó đều nhận 429 rate_limited cho tới hết phút — kể cả khi dùng token hợp lệ. Đừng để một tác vụ cấu hình sai dùng chung IP với lưu lượng production.
  • Người nhận không xác định: sau 10 lần tra một user_id hoặc @username không tồn tại trong một ứng dụng trong một phút (transfer, phần tử của transferBatch, createCheck có pin_to_username), mọi lần tra người nhận trong ứng dụng đó trả về 429 rate_limited cho tới hết phút.

Có thể áp dụng thêm giới hạn yêu cầu. Khi gặp 429 rate_limited, tăng thời gian chờ trước khi thử lại. Trong lúc nền tảng bảo trì, các phương thức ghi trả về 503 maintenance còn các phương thức đọc vẫn chạy bình thường.

Phương thức tra cứu và tài khoản

getMe

GET /pay/api/getMe — không có tham số. Trả về danh tính của ứng dụng: app_id, name, payment_processing_bot_username, webhook_url, webhook_events (các loại webhook mở rộng mà ứng dụng đã bật), hai trường cho biết thông tin token scopes / token_name nói ở trên, và payout_mode (off / allowlist / any), daily_payout_max_usd_cents (0 = chủ sở hữu không đặt hạn mức) và daily_payout_spent_usd_cents (số đã chi trong 24 giờ gần nhất).

updateApp

POST /pay/api/updateApp — quyền webhooks. Tham số tùy chọn: name (1–64 ký tự), webhook_url (URL HTTPS công khai, tối đa 256 ký tự; chuỗi rỗng sẽ xóa URL), và webhook_events (mảng hoặc danh sách tên sự kiện bổ sung được hỗ trợ, phân cách bằng dấu phẩy). Danh sách rỗng chỉ giữ lại invoice_paid. Trả về các trường ứng dụng giống getMe.

Lỗi: 400 invalid_request (name ngoài 1–64 ký tự, hoặc webhook_url không phải địa chỉ HTTPS công khai hay dài quá 256 ký tự), 400 invalid_name (tên trông như liên kết hoặc @username), 400 app_name_reserved (tên sử dụng tên TgPay Crypto), và 400 invalid_webhook_events (tên sự kiện không được hỗ trợ; invoice_paid cũng bị từ chối trong danh sách này vì không cần đăng ký nhận riêng).

getBalance

GET /pay/api/getBalance — không có tham số. Trả về một mảng, mỗi tài sản được hỗ trợ một dòng, kể cả khi bằng không (tài sản đã bị tắt chỉ hiện khi ứng dụng vẫn còn số dư của nó): currency_code, available (số dư khả dụng), onhold (tiền Lì xì chưa được nhận và các khoản chi trả cho @username đang chờ) và amount_minor.

getCurrencies

GET /pay/api/getCurrencies — không có tham số. Danh sách chuẩn những gì API hỗ trợ: các dòng crypto (is_blockchain: true) và các loại tiền pháp định mà hóa đơn có thể định giá theo (is_fiat: true). Mỗi dòng mang code, name, decimals và cờ is_stablecoin.

getExchangeRates

GET /pay/api/getExchangeRates — không có tham số. Tỷ giá crypto sang tiền pháp định: source, target, rate (chuỗi thập phân), is_valid, is_crypto và is_fiat. Các tỷ giá này chỉ mang tính tham khảo. is_valid: true không bảo đảm tỷ giá còn mới. Nếu không lấy được tỷ giá, kết quả có thể là mảng rỗng. Dùng số tiền và tỷ giá đã ghi trên hóa đơn để đối soát thanh toán thực tế.

getStats

GET /pay/api/getStats — tùy chọn start_at / end_at (ISO 8601; cửa sổ mặc định là 24 giờ gần nhất). Trả về volume (giá trị USD của các hóa đơn đã thanh toán trong khoảng đó, ước tính theo tỷ giá hiện tại; tài sản không có giá được tính là 0), conversion (đã trả/đã tạo, theo phần trăm), unique_users_count, created_invoice_count, paid_invoice_count, và hai mốc thực tế của cửa sổ. volume và conversion là số JSON, không phải chuỗi. Ngày không đọc được sẽ trả về 400 invalid_date.

Lỗi mà mọi phương thức đều có thể trả về

HTTPerror.nameKhi nào
401unauthorizedtoken thiếu, sai hoặc đã bị thu hồi
403scope_requiredtoken không có scope của phương thức
403payouts_disabledchế độ chi trả của ứng dụng đang là off (chỉ các phương thức chi trả)
403recipient_not_allowedngười nhận không có trong danh sách người nhận của ứng dụng (chỉ các phương thức chi trả)
400invalid_requesttham số không đọc được hoặc không hợp lệ (POST)
403daily_cap_exceededvượt hạn mức chi trả hằng ngày của ứng dụng
403verification_requiredchủ sở hữu bị hạn chế giao dịch chi tiền; áp dụng cho chuyển tiền, Lì xì và hoàn tiền
503merchant_payouts_pausedtạm dừng trừ tiền từ số dư ứng dụng
429rate_limitedvượt giới hạn tần suất
503maintenancenền tảng đang bảo trì (các phương thức ghi)
500internal_errorlỗi máy chủ ngoài dự kiến

Các lỗi riêng của từng phương thức được liệt kê trên trang của phương thức đó.

Lỗi không theo cấu trúc JSON

Chỉ dựa vào phản hồi lỗi dạng văn bản thuần hoặc HTML thì chưa thể xác định lỗi đến từ lớp nào. Hãy kiểm tra trạng thái HTTP, nội dung và các header của phản hồi.

Nếu Cloudflare trả về error code: 1010, máy khách đã bị từ chối dựa trên dấu hiệu nhận dạng trình duyệt trước khi yêu cầu đến API. Phạm vi quyền của token không phải nguyên nhân gây ra lỗi Cloudflare này. User-Agent bị thiếu hoặc không chuẩn có thể kích hoạt lớp bảo vệ mạng. Hãy thử gửi header User-Agent có tên nhận diện máy khách của bạn, chẳng hạn MyShop/1.0, rồi gửi lại yêu cầu; cách này không đảm bảo khắc phục được lỗi. Nếu lỗi vẫn còn, hãy gửi cho bộ phận hỗ trợ trạng thái HTTP, nội dung và các header của phản hồi sau khi xóa token và cookie.