API 参考:账单和退款
账单相关的方法:createInvoice、getInvoices、deleteInvoice、refundInvoice。通用约定
(鉴权、外层结构、金额、spend_id)见商户 API 参考页面;
手把手的走一遍在用账单收款。
createInvoice
POST /pay/api/createInvoice——权限范围 invoices,每分钟 60 次。
| 参数 | 类型 | 必填 | 含义 |
|---|---|---|---|
currency_type | string | 否 | crypto(默认)或 fiat |
asset | string | crypto 模式 | 要收的币种,比如 USDT。不能和 fiat 同时给 |
fiat | string | fiat 模式 | 标价用的法币(getCurrencies 里 is_fiat 的那些行) |
accepted_assets | string / array | 否 | 只在 fiat 模式下有效:付款人可以用来付的币种——逗号分隔的字符串("USDT,GRAM")或一个 JSON 数组。不给 = 全部支持的币种 |
amount | string | 是,除非设了 open_amount | 正的小数字符串:crypto 模式下是币种单位,fiat 模式下是法币单位(最多 2 位小数) |
open_amount | boolean | 否 | 扩展,只在 crypto 模式下有效:不定额——付款人在付的时候自己填(打赏和捐款)。跟 amount 互斥 |
description | string | 否 | 最多 1024 个字符,显示给付款人 |
hidden_message | string | 否 | 最多 2048 个字符,付款人付完才看得到 |
payload | string | 否 | 最多 4096 个字符的您自己的数据,会在账单和 webhook 里原样回传 |
allow_comments | boolean | 否 | 允许付款人附一条留言(默认 true) |
allow_anonymous | boolean | 否 | 允许付款人隐藏身份(默认 true) |
paid_btn_name | string | 否 | 付款后的按钮:viewItem、openChannel、openBot 或 callback |
paid_btn_url | string | 否 | 那个按钮的 http(s) URL——设了 paid_btn_name 就必须给 |
swap_to | string | 否 | 把收到的付款自动换成这个币种。尽力而为:付款时换不成的话,付款照样成功,只是没换 |
expires_in | integer | 否 | 多少秒之后账单过期,最大 2678400(31 天);不给或 0 = 永不过期 |
rate_lock_seconds | integer | 否 | 扩展,只在 fiat 模式下有效:在开单时把折算汇率锁定这么长一段时间(见下) |
返回的结果——也就是 invoice_paid webhook 的 payload——是账单对象。它的主要字段:
- 标识和状态:
invoice_id、hash(pay_url里那个公开 ID)、status(active/paid/expired)、pay_url——您发给付款人的那条t.me链接 (bot_invoice_url、mini_app_invoice_url、web_app_invoice_url都是它的别名)。 - 金额:
amount(票面金额——法币账单上是法币单位,否则是加密货币;不定额账单没付掉之前 是null)、amount_minor,以及已付法币账单上的paid_asset/paid_amount/paid_fiat_rate——实际收的加密货币和用的汇率。 - 手续费:
fee_asset/fee_amount,在付款时确定并记录——这是您记账时的权威数字 (fee和usd_rate是已废弃的 Crypto Bot 别名)。见 费用和限额。 - 付款人:
paid_by_user_id(付款人选了匿名时是null)、paid_anonymously、comment。 - 退款(扩展):
refunded_amount/refunded_minor(累计)和refunded_at,全额退完 之后写入。 - 汇率锁定(扩展):
rate_lock_until和rate_lock_rates——记录下来的各币种汇率, 没申请锁定时是null。 - 开单时给的那些:
description、hidden_message、payload、paid_btn_name/paid_btn_url、expiration_date(expires_at是别名),以及兑换相关字段(swap_to、is_swapped、swapped_to、swapped_rate、swapped_output等等)。
错误:400 invalid_currency(asset 和 fiat 搞混了)、400 invalid_amount、
404 unknown_asset、400 unsupported_fiat、400 paid_btn_url_required,以及跟汇率锁定
有关的 400 rate_lock_fiat_only、409 ratelock_disabled、409 rate_unavailable
(某个接受的币种取不到最新汇率——重试即可)。
账单是怎么被付掉的
付款人在 Mini App 里用自己的钱包余额付——瞬间完成,没有网络手续费。付款人也可以
用外部钱包充值来支付这张账单:应用给他一个充值地址,他的转账落进他自己的钱包,钱一到账单就
自动结清。两种方式您看到的都一样:一张正常的 paid 账单和一个 invoice_paid webhook
——没有额外的参数或字段要处理。
法币账单上的加密货币数额是在付款时算的,向上取整,取整方向对您有利,所以您拿到的绝不会少于法币 票面值。要是取不到最新汇率,付款会在付款人那一侧失败,而不是按一个过时的汇率成交。
给法币账单锁汇率
传 rate_lock_seconds,就会在开单时把每一个接受币种当下的汇率固定下来。锁定有效期间,
付款人看到的正是固定下来的那几个数额,付款也按固定下来的汇率折算——这段窗口里的汇率风险由您
承担。服务器会把这个窗口夹在 60 秒和平台上限(目前是 15 分钟)之间。
锁定失效之后,账单照样能付,只是自动回到按付款时折算。您要是希望报价一过账单就作废,
把 expires_in 设成同样的值。
getInvoices
GET /pay/api/getInvoices——权限范围 read。过滤条件:asset、fiat、invoice_ids
(逗号分隔)、status(active / paid / expired——expired 是扩展;active 不含已经
过了期限的账单),外加 offset / count。返回 {"items": [invoice, …]},按时间倒序。
deleteInvoice
POST /pay/api/deleteInvoice——权限范围 invoices。一个参数:invoice_id。取消一张
没付掉的账单,返回 true。错误:404 invoice_not_found、409 invoice_already_paid
——已付的账单删不掉,钱已经动了。
refundInvoice
POST /pay/api/refundInvoice——权限范围 refunds,每分钟 30 次。相对 Crypto Bot 的扩展:
把一张已付账单的票面金额——或其中一部分——从您的应用余额退还给付款人,匿名付款的也能退,
而且不会暴露他是谁。
| 参数 | 类型 | 必填 | 含义 |
|---|---|---|---|
invoice_id | integer | 是 | 那张已付账单 |
amount | string | 否 | 要退的数额,用账单的币种。不给 = 还没退的余数全退。部分退款可以累加到票面金额为止 |
spend_id | string | 否 | 幂等键——请用上它,好让超时重试变成重放而不是退两次 |
返回的是更新后的账单对象,带累计的 refunded_amount / refunded_minor;账单全额退完之后
refunded_at 才写入。状态一直是 paid。平台手续费不退。每一次退款都会触发需要订阅的
refund_completed webhook。
错误:404 invoice_not_found、409 invoice_not_paid、409 already_refunded(没什么可退了)、
409 amount_too_big(超过还没退的余数)、409 insufficient_funds、400 invalid_amount,
以及 spend_id 那一对 409 idempotency_conflict / 409 idempotency_in_progress。
这篇文章帮上忙了吗?
谢谢反馈。