Bollard API
Bollard 帮你的网站收稳定币。买家在 Bollard 的托管收银台为这一单付款:按页面金额转账,到账确认后自动完成。你的网站只需要两件事:下单时创建收款单,收到 webhook 后发货。
金额一律用美分(整数)表示,币种固定为 usd。稳定币按 1:1 计价。
鉴权
在控制台“开发者”页复制密钥,放在请求头里。密钥只能在服务器端使用。
Authorization: Bearer sk_test_…
付款流程
- 买家在你的网站点“结账”,你的后端调用 创建收款单,把浏览器跳到返回的
url。 - 买家在收银台看到应付金额和他的固定收款地址,用钱包或从交易所转账;转少了可以补转,凑够后自动完成。
- 付款完成后,Bollard 向你的 webhook 地址发送
checkout.session.completed,并把浏览器跳回success_url。 - 你的网站验证签名后发货。webhook 可能延迟,订单页可以同时调用 查询收款单 兜底。
创建收款单
| customer必填 | 你网站里的用户 ID 或邮箱。同一个值对应同一个客户:同一个收款地址、同一份抵用金。每位客户同时只保留一张待付收款单,新建时会作废他之前未付的单(发送 checkout.session.expired)。string |
| customer_email | 收银台上显示的邮箱。 string |
| line_items必填* | 商品列表,每项 name、unit_amount(美分)、quantity。合计不低于 $0.50,不高于 $10,000。array |
| amount | 不传 line_items 时,直接给总金额(美分)。 integer |
| success_url必填 | 付款完成后跳回的地址。 string |
| cancel_url | 收银台“返回”按钮的地址。 string |
| metadata | 最多 20 个键值,会原样出现在 webhook 和付款记录里,适合放订单号。 object |
curl http://localhost:3000/v1/checkout/sessions \ -H "Authorization: Bearer sk_test_…" \ -H "Content-Type: application/json" \ -d '{ "customer": "ana@example.com", "customer_email": "ana@example.com", "line_items": [{ "name": "Marrow Sans 字体家族", "unit_amount": 4900, "quantity": 1 }], "success_url": "https://your-site.com/orders/1042", "cancel_url": "https://your-site.com/cart", "metadata": { "order_id": "1042" } }'
返回
{
"id": "cs_8Hk2bQ…",
"object": "checkout.session",
"url": "http://localhost:3000/checkout/cs_8Hk2bQ…",
"status": "open", // open | complete | expired
"payment_status": "unpaid",
"amount_total": 4900,
"amount_received": 0, // 这一单已到账的转账
"amount_remaining": 4900, // 还需转多少
"credit_applied": 0, // 用抵用金抵扣的部分
"amount_overpaid": 0, // 付款完成时多转、存为抵用金的部分
"currency": "usd",
"customer": "cus_4QpL…",
"client_reference_id": "ana@example.com",
"metadata": { "order_id": "1042" },
"payment": null,
"expired_reason": null, // timeout | replaced
"expires_at": 1760947200
}
收款单 24 小时后过期。过期或被新单替代后,再转到这个客户地址的钱会算到他最新的待付单上;没有待付单时存为抵用金。
抵用金
多转的部分、订单失效后才到账的款项,存为买家在你网站的抵用金,下次结账自动抵扣。抵用金不能提现、兑换或转给他人。抵用金够付整单时,收银台会请买家点一下确认,不会自动扣。
查询收款单
返回同样的对象。status 为 complete 表示已付款,payment 是付款 ID。
客户
提前为用户建档,拿到他的固定收款地址。参数:customer(必填)、email。
{ "id": "cus_4QpL…", "external_id": "ana@example.com", "balance": 120 /* 抵用金,美分 */, "deposit_address": "0x4AE4…8375" }
同一个收款地址在 Plasma、BNB Chain、Ethereum 上通用,可以存进交易所地址簿反复使用。
付款列表
按时间倒序。每笔付款包含 amount、fee(平台费)、net(计入你可结算余额的部分)。
Webhook
在控制台“设置”页填写接收地址。Bollard 用 POST 发送 JSON,你的服务返回 2xx 即视为成功;否则按 10 秒、1 分钟、5 分钟、30 分钟、2 小时、6 小时重试。同一事件可能送达多次,请用事件 id 去重。
{
"id": "evt_9mT…",
"type": "checkout.session.completed",
"created": 1760860800,
"data": { "object": { /* 收款单对象 */ } }
}
事件类型
| checkout.session.completed | 收款单已付款。发货用这个。 |
| checkout.session.expired | 收款单没付款就失效了:超时,或同一客户下了新单(expired_reason 为 replaced)。可以把对应订单作废。 |
| payment.succeeded | 一笔付款成功,包含平台费和净额。 |
| deposit.succeeded | 一笔转账已确认到账,计入客户当前的待付单;没有待付单时存为抵用金。 |
| payout.paid | 结算已到账(USDT 链上转账已确认,或银行结算)。 |
| payout.failed | USDT 结算的链上交易失败,金额已退回可结算余额。 |
验证签名
每个请求带 Bollard-Signature: t=时间戳,v1=签名。签名是用 webhook 签名密钥对 时间戳.原始请求体 做 HMAC-SHA256 的十六进制结果。请用原始请求体验证,并拒绝 5 分钟以外的时间戳。
import { createHmac, timingSafeEqual } from "node:crypto"; function verify(secret, rawBody, header) { const { t, v1 } = Object.fromEntries(header.split(",").map((kv) => kv.split("="))); if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex"); return v1?.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected)); }
错误
出错时返回非 2xx 状态码和下面的结构:
{ "error": { "code": "amount_too_small", "message": "单笔最低 $0.50" } }
| 401 invalid_api_key | 密钥缺失或无效。 |
| 400 bad_* | 参数不对,message 会说明哪个参数。 |
| 404 *_not_found | 对象不存在,或不属于你的账户。 |
测试环境
演示环境跑在三条本地模拟链上,代币没有真实价值。收银台里有“演示钱包”和“模拟从交易所提币”工具,可以直接给买家的收款地址转账,看完整的确认和到账过程。