Documentation Index
Fetch the complete documentation index at: https://waffo.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
每个 Webhook 请求的 Header 都包含 X-SIGNATURE,该签名使用 Waffo 私钥生成。商户必须使用 Waffo 公钥来验证签名。
获取 Waffo 公钥
登录 Merchant Portal → Integration 菜单,即可查看和复制 Waffo 公钥。
需要 Dev 或 Admin 角色权限才能访问该页面。
推荐方式:使用 SDK
SDK 的 handleWebhook() 方法会自动执行签名验证、事件解析、路由以及响应签名:
app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
const body = req.body.toString();
const signature = req.headers['x-signature'] as string;
const result = await waffo.webhook().handleWebhook(body, signature);
res.setHeader('X-SIGNATURE', result.responseSignature);
res.setHeader('Content-Type', 'application/json');
res.status(200).send(result.responseBody);
});
手动验证
如果你需要手动处理(不使用 SDK),验证步骤如下:
获取签名
从请求 Header 中获取 X-SIGNATURE。
获取原始 Body
获取原始请求 Body 字符串(不要先 JSON parse 再 stringify)。
验证签名
使用 Waffo 公钥 + SHA256WithRSA 验证签名。
对响应签名
你必须在响应中设置 X-SIGNATURE Header(使用商户私钥对响应 body 进行签名)。
手动示例
import { createVerify, createSign } from 'crypto';
function verifyWaffoSignature(body: string, signature: string): boolean {
const verify = createVerify('SHA256');
verify.update(body);
return verify.verify(process.env.WAFFO_PUBLIC_KEY!, signature, 'base64');
}
function signResponse(responseBody: string): string {
const sign = createSign('SHA256');
sign.update(responseBody);
return sign.sign(process.env.MERCHANT_PRIVATE_KEY!, 'base64');
}
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const body = req.body.toString();
const signature = req.headers['x-signature'] as string;
if (!verifyWaffoSignature(body, signature)) {
const failedBody = JSON.stringify({ message: 'failed' });
res.setHeader('X-SIGNATURE', signResponse(failedBody));
return res.status(200).send(failedBody);
}
// 处理事件...
const successBody = JSON.stringify({ message: 'success' });
res.setHeader('X-SIGNATURE', signResponse(successBody));
res.status(200).send(successBody);
});
注意事项
- 你必须在处理事件之前验证签名。不要先响应再验证。
- 响应必须包含
X-SIGNATURE Header,否则 Waffo 会将该次投递视为失败。
- 使用原始请求 body 来验证签名;不要先 JSON parse 再 stringify。
- SDK 提供了完整的 webhook 处理流水线。相比手动处理,推荐使用 SDK。