Webhook 通知
当下发单或汇款明细状态发生变更时,系统会向您配置的 Webhook URL 以 POST 方式推送事件通知。
事件类型
| 事件 | 触发时机 |
|---|---|
payouts:status_changed | 下发单状态变更时 |
remits:status_changed | 汇款明细状态变更时 |
推送格式
系统以 POST 方式发送 JSON 请求,请求头包含:
| 请求头 | 说明 |
|---|---|
Content-Type | application/json |
X-Signature | HMAC-SHA256 签名(十六进制字符串) |
签名验证
每条推送请求的 X-Signature 使用 HMAC-SHA256 签名,验证方式:
expected_signature = HMAC-SHA256(webhook_secret, request_body)
webhook_secret:配置 Webhook 时设置的密钥request_body:推送请求正文的原始 JSON 字符串
推送正文中包含 event_time 字段,建议校验其与当前时间的差值(如超过 5 分钟则拒绝),以防止重放攻击。
签名验证代码
- Java
- Python
- PHP
- Go
- Node.js
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(webhookSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] hash = mac.doFinal(requestBody.getBytes(StandardCharsets.UTF_8));
StringBuilder expected = new StringBuilder();
for (byte b : hash) {
expected.append(String.format("%02x", b));
}
boolean valid = MessageDigest.isEqual(
expected.toString().getBytes(),
signature.getBytes()
);
import hmac
import hashlib
expected = hmac.new(
webhook_secret.encode("utf-8"),
request_body.encode("utf-8"),
hashlib.sha256
).hexdigest()
valid = hmac.compare_digest(expected, signature)
$expected = hash_hmac('sha256', $requestBody, $webhookSecret);
$valid = hash_equals($expected, $signature);
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
mac := hmac.New(sha256.New, []byte(webhookSecret))
mac.Write([]byte(requestBody))
expected := hex.EncodeToString(mac.Sum(nil))
valid := hmac.Equal([]byte(expected), []byte(signature))
const crypto = require('crypto');
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(requestBody)
.digest('hex');
const valid = crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
响应要求
收到推送后,请返回 HTTP 200 状态码表示接收成功。建议根据 event_time 和事件 ID 做好幂等处理。