开发者

Webhook

发票状态发生变化时,CryBit会向您商户的webhook URL发送一个带有JSON请求体的签名POST请求。

设置

在商户设置的集成选项卡中设置webhook URL,并勾选需要的事件:paid、expired、refunded、cancelled。该URL必须是公开可访问的HTTP或HTTPS地址,端口为80、443、8080或8443;系统不会跟随重定向。该设置针对商户,而非在发票请求中指定。

请求头

请求头值
X-CryBit-Event事件名称,例如payment.paid。
X-CryBit-Signaturet=<unix time>,v1=<hex>:使用您的签名密钥对"<t>.<raw body>"计算的HMAC-SHA256值。

校验签名

签名密钥显示在商户设置中webhook URL旁边。请对原始请求体计算签名,以恒定时间方式进行比较,并拒绝超过五分钟的请求。

PHP

$secret = getenv('CRYBIT_WEBHOOK_SECRET');
$body   = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_CRYBIT_SIGNATURE'] ?? '';

parse_str(str_replace(',', '&', $header), $p);   // t=…&v1=…
$t = (int) ($p['t'] ?? 0);
$expected = hash_hmac('sha256', $t . '.' . $body, $secret);

if (abs(time() - $t) > 300 || !hash_equals($expected, $p['v1'] ?? '')) {
    http_response_code(400);
    exit;
}
// the request is genuine: store the event, answer 200, do the rest later
http_response_code(200);

Node.js

import crypto from 'node:crypto'

export function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((kv) => kv.split('=')))
  const t = Number(parts.t)
  const expected = crypto.createHmac('sha256', secret).update(t + '.' + rawBody).digest('hex')
  const ok = parts.v1 && parts.v1.length === expected.length
    && crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected))
  return ok && Math.abs(Date.now() / 1000 - t) <= 300
}

投递与重试

  • 请尽快以2xx状态码响应:CryBit大约会等待十二秒。
  • 如果没有收到响应,CryBit会依次在15秒、1分钟、5分钟、15分钟、30分钟后重试,随后是2小时、6小时和24小时。
  • 除429以外的4xx响应会终止重试。
  • 同一事件可能会被多次发送:请以order_id或发票uuid为键进行幂等处理。
  • 如果错过了某次webhook,可通过 GET /api/v1/payments/{uuid} 查询状态:该接口始终是最终依据。

FAQ

我可以查看已投递的内容吗?

可以。商户账户中的API日志页面会列出请求和webhook投递记录,包括状态码和请求体内容。

更多

获取API密钥