前提
为什么外贸卖家开始收 USDT
不依赖银行
跨境收款最痛的不是手续费,是能不能到账:开户被拒、账户被冻结、单笔被退回。稳定币转账不看开户行,也不看两国关系。
没有汇损与中间行
USDT 与美元锚定,收款、记账、结算同一单位。省掉换汇点差与中间行扣费,也省掉了对账时那几笔"少了 17 美元"。
7×24 且分钟级
周末、节假日、夜里照常收。链上确认以分钟计,而不是"T+2 个工作日"。
可编程
每一笔都有交易哈希与确认数,能自动对账、自动发货 —— 这是传统收款方式给不了的。
核心
一笔订单的完整生命周期
状态机是整个系统的骨架:只有链上核验能把单子推到"已确认",买家自己回传的交易哈希只能把它推到"已提交"。这条分界决定了它防得住伪造回执。
⚠️ 一条给开发者的纪律:永远不要按数字大小比较订单状态(比如"状态码 ≥ 3 就算付款成功")—— 状态码不是单调递增的语义,新状态加进来时那种写法会静默错判。
覆盖面
链与代币
按链的类型(chainType)归一化地址与校验规则,所以新增一条同类型的链不需要改结算逻辑。
一链一地址
同一个商户在不同链上用各自的地址,可分别启用/停用;收银台按买家选的链取对应地址。
地址按链归一化
EVM 地址统一小写、TRON 保留大小写 —— 这是为了"同一个地址只有一种存法",否则同一个钱包会被记成两条记录,对账时对不上。
只收自己启用的链
你在后台勾选要收的链与代币;没收的链不会出现在收银台上,买家也不会误付到不该付的地方。
集成要点
回调:带签名、会重试、可反查
回调会失败(你的站点重启、网络抖动、代码抛异常),所以它必须重试;而重试意味着可能重复投递,所以它必须可验签、可幂等。
签名(防伪造)
请求头带时间戳与 HMAC-SHA256 签名,对「时间戳 + 请求体」计算;事件类型在 X-Pay-Event 里(如 pay.confirmed)。验签失败一律丢弃。
重试(防漏单)
按退避重试:5s → 30s → 2m → 10m → 30m → 1h → 2h → 6h。你的接口只要回 2xx 就停止重试。
幂等(防重复发货)
用 merchantOrderNo 做主键去重 —— 收到重复回调时直接返回成功,不要重复发货。
反查(防被动)
别只信回调:定时用查询接口对一遍未结单。回调漏了你不知道,查询是你主动去问。
安全
开放接口怎么防
密钥只在服务端
API KEY 绝不能出现在浏览器里 —— 那等于把"替这个商户下单"的权限交给每一个访客。前端只用收银链接。
下单幂等
merchantOrderNo 是幂等键:超时重发、用户连点、回调重试都不会变成两张单。
日志不落敏感值
API KEY、验证码、SMTP 口令这类不进日志;日志里只有末四位或哈希。
跨域按路径收口
开放接口允许任意 Origin(它用请求头里的 KEY、不带 Cookie,没有 CSRF 面);其余接口一个 CORS 头都不加。
架构
两端分离:平台侧与商户侧
平台侧管运营(商户审核、链与代币、风控、对账),商户侧管生意(自己的后台、自己的订单与流水)。两边各自独立部署、各自独立的库 —— 一个商户的业务量涨起来,不会拖慢别人。
平台侧
配置链与代币、开通与管理商户、查看全平台收款报表。运行在平台自己的域名与库上。
商户侧
每个商户的后台:收款概览、收款地址、支付单、报表、设置。数据在自己的库里,跨商户不可见。
收银台
买家打开的那一页(/pay/cashier/m/{商户号})。独立域名、独立部署,刻意不被搜索引擎收录。
跨库只存 ID
插件之间不做跨库 JOIN —— 库之间的边界是硬的,所以某个库出问题不会连坐。代价是"要 join 的地方各供一次接口"。