加密货币支付 API 接入:账单、签名与 Webhook 全解析

一份实用的开发者教程:一次签名请求创建账单,一个 Webhook 端点接收状态,再加上保证安全可信的关键细节。

更新于: 阅读约 7 分钟
加密货币支付 API 接入:创建账单的签名 POST 请求与 Webhook 回调

加密货币支付 API 接入需要两部分:一个在服务器端为每笔订单创建账单并返回收银台链接的请求,以及一个在链上检测到并确认付款时接收签名状态更新的 Webhook 端点。在 mistKET,两者都是纯 HTTPS 和 JSON,用 HMAC-SHA256 签名,无需任何 SDK。

本指南先用代码走一遍完整流程,然后讲解区分演示版和生产环境的安全与可靠性细节。

一张图看懂架构

每一次加密货币支付 API 接入都遵循同样的五步循环:

  1. 创建订单 — 后端调用 POST /api/v1/invoices,传入金额、币种、订单号和回调地址。
  2. 跳转 — 把客户引导到返回的 checkout_url。
  3. 付款 — 客户在托管页面选择网络并付款;汇率在账单有效期内锁定。
  4. Webhook — mistKET 向你的回调地址 POST invoice.paid、invoice.confirmed 或异常事件。
  5. 履约 — 验证签名,把订单标记为已付款并发货。

账户审核通过后,你会在商户后台获得 API Key、API Secret 和 Webhook Secret。完整文档位于后台的 API 页面。

第 1 步:签名并发送账单请求

每个 API 请求携带三个请求头。签名是用你的 API Secret 对字符串 {timestamp}.{raw body} 计算的十六进制 HMAC-SHA256:

POST /api/v1/invoices
X-Api-Key: mp_live_…
X-Api-Timestamp: 1760000000
X-Api-Signature: hex(hmac_sha256("{ts}.{body}", secret))

{
  "amount": "184.50",
  "currency": "USD",
  "order_id": "2041",
  "callback_url": "https://yourstore.com/webhooks/mistket"
}

最简 PHP 实现如下:

$body = json_encode([
  'amount' => '184.50', 'currency' => 'USD',
  'order_id' => '2041',
  'callback_url' => 'https://yourstore.com/webhooks/mistket',
]);
$ts  = (string) time();
$sig = hash_hmac('sha256', $ts . '.' . $body, $apiSecret);
// send $body with X-Api-Key, X-Api-Timestamp, X-Api-Signature

对你实际发送的原始字节签名。签名后又重新编码 JSON(空格或键顺序不同)是签名错误的头号原因。

第 2 步:跳转到托管收银台

调用成功会返回 201 Created,包含账单 UUID、状态(new)和 checkout_url。把 UUID 与订单关联保存,然后跳转客户。托管页面显示准确金额、二维码、倒计时和实时状态,并适配移动端。客户在页面上选择网络:TRON、BNB Smart Chain、Ethereum 或 Arbitrum 上的 USDT,以及 TRX、BNB 和 ETH,除非你在 API 调用中固定了网络。

除了创建账单,API 还支持列出、查询和取消账单,在购物车变化或订单被放弃时很有用。

第 3 步:接收并验证 Webhook

状态变化时,mistKET 会向你的回调地址 POST JSON,并带上以下请求头:

请求头用途
X-Mistket-Event事件名称,例如 invoice.confirmed
X-Mistket-Timestamp参与签名的 Unix 时间戳
X-Mistket-Signaturesha256= + 用 Webhook Secret 对 {timestamp}.{raw body} 计算的 HMAC-SHA256
X-Mistket-Delivery投递 ID,便于记录日志

PHP 验签示例:

$raw = file_get_contents('php://input');
$ts  = $_SERVER['HTTP_X_MISTKET_TIMESTAMP'] ?? '';
$got = $_SERVER['HTTP_X_MISTKET_SIGNATURE'] ?? '';
$exp = 'sha256=' . hash_hmac('sha256', $ts . '.' . $raw, $webhookSecret);
if (!hash_equals($exp, $got) || abs(time() - (int) $ts) > 300) {
  http_response_code(401); exit;
}
$event = json_decode($raw, true);

使用常量时间比较(PHP 用 hash_equals,Python 用 hmac.compare_digest,Node.js 用 crypto.timingSafeEqual),并拒绝过期的时间戳以防止重放攻击。关于该算法的背景,请参阅 RFC 2104(HMAC)。

要点:唯一可信的依据是 Webhook,而不是客户浏览器跳回你的网站。没有有效签名,绝不把订单标记为已付款。

Webhook 载荷示例

一笔已确认的付款以简洁的 JSON 文档送达。最常用的字段是事件、订单号、状态以及应付和实付金额:

POST https://yourstore.com/webhooks/mistket
X-Mistket-Event: invoice.confirmed
X-Mistket-Signature: sha256=…

{ "event": "invoice.confirmed", "data": {
  "order_id": "2041", "status": "confirmed",
  "pay_amount": "184.5", "paid_amount": "184.5",
  "pay_currency": "USDT", "network": "TRC20" } }

把 pay_currency 和 network 随订单一起保存,之后做账和处理客服问题时都用得上。

Node.js 与 Python 验签

另外两种常用语言的同样校验:

// Node.js (Express with express.raw for this route)
const exp = 'sha256=' + crypto.createHmac('sha256', secret)
  .update(ts + '.' + rawBody).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(exp), Buffer.from(sig));

# Python
exp = 'sha256=' + hmac.new(secret.encode(), f'{ts}.{raw}'.encode(),
                           hashlib.sha256).hexdigest()
ok = hmac.compare_digest(exp, sig)

两种情况下都要确保读取的是原始请求体。很多框架会自动解析 JSON,一旦解析后再序列化,字节就与签名对不上了。

本地测试

Webhook 需要公网 HTTPS 地址,本地电脑无法直接接收。两个实用方案:尽早把处理程序部署到测试服务器,或用内网穿透工具临时暴露本地端口。创建指向测试回调地址的小额账单,然后在商户后台查看投递日志,准确了解发送了什么、你的服务器返回了什么。

测试期间尽量把每种事件都触发一次:已付款、已确认、少付和过期。当处理程序的每个分支都用真实 Webhook 跑过之后,生产环境中就很少会有意外;即便出了问题,你也清楚投递日志会是什么样子。

第 4 步:处理每一种状态

Webhook 载荷包含订单号、状态、应付金额(pay_amount)、实付金额(paid_amount)、币种和网络。把每个事件映射到一个动作:

事件含义典型处理
invoice.paid已检测到转账,等待确认显示“已收到付款,确认中”
invoice.confirmed已达到所需确认数标记已付款并发货
invoice.underpaid低于你设置的容差等待补款或联系客户
invoice.overpaid超出应付金额发货,并决定如何处理差额
invoice.expired有效期内未收到有效付款释放库存,订单保持未付款

少付容差、累计到总额的部分付款以及宽限期内的延迟付款都由 mistKET 处理,你的代码只需对最终结果做出响应。

可靠性:重试与幂等

如果你的端点宕机或返回错误,Webhook 会按退避策略重试数小时。这意味着你有时会收到同一事件两次。请按订单号让处理程序保持幂等:

  • 尽快返回 2xx,把耗时任务放到队列中处理。
  • 发货前先检查订单是否已标记为已付款。
  • 记录投递 ID 和事件以便排查问题;后台也提供 Webhook 投递日志。

安全检查清单

加密货币支付 API 接入涉及资金,应像对待其他支付代码一样严格:

  • API Secret 和 Webhook Secret 只保存在服务器端,绝不放进前端代码或移动 App。
  • 在后台用 IP 白名单限制 API 访问。
  • 拒绝超出短时间窗口的请求时间戳。
  • 根据库存预留逻辑配置账单有效期。
  • 金额一律用字符串或定点小数处理,绝不使用浮点数。
提示:上线前自己付一张小额账单,故意少付一次查看少付流程,再让一张账单过期。不到一小时你就能测完所有分支。

下一步

以上就是完整的加密货币支付 API 接入:一次签名请求、一个经过验证的 Webhook 和一张状态映射表。同一流程的非开发者视角,请阅读网站如何接入加密货币收款。要做机器人而不是网站?请看Telegram 机器人加密货币收款。通过 Telegram @mistnetwork 申请 API 密钥,在开发者专区查看示例,并在商户登录页面管理密钥。

常见问题

接入加密货币支付 API 需要 SDK 吗?
使用 mistKET 不需要。它基于纯 HTTPS 和 JSON,任何能发送 HTTP 请求并计算 HMAC-SHA256 的语言都能接入。
mistKET 的 API 请求如何认证?
每个请求发送 X-Api-Key、X-Api-Timestamp 和 X-Api-Signature,签名是用 API Secret 对 '{timestamp}.{raw body}' 计算的十六进制 HMAC-SHA256。
如何验证加密货币支付 Webhook?
用 Webhook Secret 对 '{timestamp}.{raw body}' 重新计算 HMAC-SHA256,以常量时间与 X-Mistket-Signature 比较,并拒绝过旧的时间戳。
服务器漏收了 Webhook 怎么办?
Webhook 会按退避策略重试数小时,你也可以通过 API 查询账单状态。请按订单号保持处理程序幂等。
能通过 API 指定某个网络吗?
可以。你可以在 API 调用中固定网络;否则由客户在托管付款页面上选择。
哪个事件应该触发发货?
在 invoice.confirmed 时发货。invoice.paid 只用于显示进度,underpaid、overpaid 和 expired 事件按你的策略处理。

相关文章