开发者
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-Signature | t=<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投递记录,包括状态码和请求体内容。
