HTML 表单
为站点、网店或机器人接入加密货币支付的方式之一。买家点击按钮,带收款单的支付页面随即打开。
表单只能以 POST 方式提交到 https://dash.bitsby.app/invoices/form,包含两个字段:
| 字段 | 内容 |
|---|---|
data | 收款单字段 JSON 的 base64url 编码 |
signature | data 字符串的 HMAC-SHA256,64 个十六进制字符 |
每个项目以这种方式创建的待支付收款单同一时刻最多 50 张。
表单示例
<form method="post" action="https://dash.bitsby.app/invoices/form" target="_blank">
<input type="hidden" name="data" value="eyJwcm9qZWN0SWQiOiJhMWIyYzNkNC01ZTZm...">
<input type="hidden" name="signature" value="f77d6da1be35bd2900e0bfed9f202b04...">
<button type="submit">Pay</button>
</form>
两个值由店铺服务器在渲染页面时填入。密钥绝不出现在页面标记中。
target="_blank" 属性可选:加上它,购物车会留在原标签页中。
容器的构建方式
- 构建包含收款单字段的 JSON 对象。
- 将其编码为 base64url,得到
data字符串。 - 计算
signature = HMAC-SHA256(data, formSecret)。
签名针对整个 data 字符串,因此 JSON 的键顺序、缩进和 Unicode 转义风格都无关紧要。接收端也接受标准 base64,带不带填充均可。
签名后不要重新生成 data:哪怕只变动一个字节,签名都必须重新计算。
签名的计算方法和四种语言的现成示例见 HTML 表单签名。
data 中的键
| 键 | 是否必填 | 内容 |
|---|---|---|
projectId | 是 | 控制台设置中的项目 ID,uuid |
amountFiat | 是 | 金额,1 到 100000,最多两位小数。可以是字符串 "10.50" 或数字 10.5 |
currencyFiat | 是 | 法币:USD、EUR 或 RUB |
timeToPay | 是 | 支付期限,单位为小时:0.5、1、3、6、12。可以是字符串或数字 |
description | 否 | 面向买家的描述,最多 1000 个字符 |
serviceData | 否 | 店铺侧的订单标识,最多 1000 个字符。不向买家展示,但在表单源代码中可见——不要放入敏感信息 |
output | 否 | errors——显示校验错误的详情 |
可选键可以不写进 JSON——等同于空字符串。键的顺序任意,接收端会忽略未知的键。
值为 JSON 字符串或数字。布尔值、数组和嵌套对象不算字段值,送达时视为空字符串。
data 内容示例:
{
"projectId": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"amountFiat": 10.5,
"currencyFiat": "USD",
"timeToPay": 1,
"description": "Order #7, delivery",
"serviceData": "order-7"
}
这里金额和支付期限以数字传入;字符串同样可以。可选的 output 完全没有设置。
金额格式
只允许数字和小数点,最多两位小数:10、10.00、1000.50。首尾空格会被去除。
其他任何格式都会被拒绝——千位分隔符、用逗号代替小数点、科学计数法、数字前的正负号、货币符号。没有四舍五入:第三位小数不会被截断,而是导致拒绝。
表单密钥
密钥位于控制台项目设置的表单密钥 (Form secret) 字段中,由服务器在创建项目时签发。不能自行设定值;密钥只能重新签发——例如在泄露时。
密钥必须留在店铺的服务器上。一旦出现在店面 HTML 中,签名就失去了意义。
表单密钥和 Webhook 密钥是两把不同的密钥,不要混淆。
订单标识
请始终填写 serviceData:放入自己的订单标识。该值会在付款通知中原样返回——店铺靠它找到订单。
填写了 serviceData 就能防止重复。重新提交表单或双击按钮不会创建第二张收款单:买家会被引导到已存在的待支付收款单。匹配依据的是订单字段——项目、金额、币种、描述和 serviceData——而不是 data 的字节。
每个订单的值必须唯一。值相同的话,第二位买家会进入第一位买家的待支付收款单。
空的 serviceData 会失去这层保护:没有可用来识别重复的依据,每次提交都会创建新收款单。重复的收款单会耗尽 50 张待支付收款单的上限,而其中一张被支付后,店铺也无从把付款对应到订单。
错误与调试模式
表单要经过两道检查:先查签名和容器,再查字段值。
| 情况 | 服务的显示 |
|---|---|
| 签名不符、容器无法读取、项目属于他人或不存在 | 通用错误,不给出原因 |
| 签名相符,但某个字段填写有误 | 通用错误。带 "output":"errors" 时——详细错误 |
| 两道检查都通过 | 带收款单的支付页面 |
第一道检查在任何设置下都不会透露原因。
JSON 中的 "output":"errors" 键值对为第二道检查开启详情:服务会指出字段、列出允许的值,并在达到 50 张待支付收款单上限时报告。该键位于签名容器内部,无法从外部注入。
调试表单期间在 JSON 中加入 "output":"errors";调试完成后删除该键并重新构建容器。
如果签名本身不符,把自己的 data 和签名与 HTML 表单签名中的参考数据对比。
接下来
表单创建的收款单与来自 API 或控制台的收款单处理方式相同:
- 付款确认送达 Webhook URL。用 Webhook 密钥验证通知签名,并只依据它来发货。
- 买家返回站点通过成功跳转地址 (Successful URL) 和失败跳转地址 (Unsuccessful URL) 配置——见收款单生命周期页面的《将买家带回店铺站点》一节。到达这些地址并不代表付款成功。
- 收款单状态和付款检索窗口见收款单生命周期。
- 买家转错了金额——见付款关联与金额不一致。