Guide
Webhook 接入
Onerway 支持通过 Webhook 向商户系统推送异步通知。商户需提供可访问的接收地址,并在服务端完成签名校验、幂等控制与业务处理。
接入要求
- 根据业务需要开发通知接收接口
- 确保接口地址可被 Onerway 正常访问
- 确保接口具备签名校验能力
- 确保接口具备幂等处理能力
注意
请提供稳定、可公网访问的回调地址。如回调地址发生变更,请及时联系 Onerway 技术团队更新配置。
通知方式
| 项目 | 说明 |
|---|---|
| 请求方法 | POST |
Content-Type | application/json;charset=UTF-8 |
| 签名算法 | HMAC-SHA256 |
请求头
| Header Key | 类型 | 必填 | 说明 | 格式 / 长度约束 |
|---|---|---|---|---|
Content-Type | string | 是 | 固定为 application/json;charset=UTF-8 | 固定值 |
x-timestamp | string | 是 | Unix 时间戳 | 秒级时间戳字符串 |
x-signature | string | 是 | 使用 webhook_secret 计算出的十六进制签名 | 64 位十六进制字符串 |
验签规则
签名原文:
text
x-timestamp + "." + rawBody签名算法:
text
HMAC-SHA256(webhook_secret, x-timestamp + "." + rawBody) -> Hexcontent 定义
text
content = x-timestamp + "." + rawBody其中:
x-timestamp为请求头中的时间戳rawBody为 HTTP 原始请求体字符串signature为请求头中的x-signature
验签要点
rawBody必须使用 HTTP 原始请求体,不要先格式化 JSON 再参与计算- 商户侧应同时校验
x-timestamp的时间窗口,防止重放攻击 - 商户侧应直接使用 Onerway 提供的明文
webhook_secret参与验签 - 签名校验通过后再进行业务处理
验签代码示例
Java
java
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class HmacSHA256Util {
private static final String HMAC_SHA256 = "HmacSHA256";
public static String signHmacSHA256(String webhookSecret, String content) throws Exception {
Mac mac = Mac.getInstance(HMAC_SHA256);
SecretKeySpec keySpec = new SecretKeySpec(
webhookSecret.getBytes(), HMAC_SHA256);
mac.init(keySpec);
byte[] result = mac.doFinal(content.getBytes());
return HexUtil.encodeHexStr(result);
}
public static boolean verifyHmacSHA256(String webhookSecret, String content, String signature)
throws Exception {
String expected = signHmacSHA256(webhookSecret, content);
return StrUtil.equalsAnyIgnoreCase(expected, signature);
}
}Python
python
import hashlib
import hmac
HMAC_SHA256 = hashlib.sha256
def sign_hmac_sha256(webhook_secret: str, content: str) -> str:
return hmac.new(
webhook_secret.encode("utf-8"),
content.encode("utf-8"),
HMAC_SHA256
).hexdigest()
def verify_hmac_sha256(webhook_secret: str, content: str, signature: str) -> bool:
expected = sign_hmac_sha256(webhook_secret, content)
return hmac.compare_digest(expected.lower(), signature.lower())PHP
php
<?php
class HmacSHA256Util
{
private const HMAC_SHA256 = 'sha256';
public static function signHmacSHA256(string $webhookSecret, string $content): string
{
return hash_hmac(self::HMAC_SHA256, $content, $webhookSecret);
}
public static function verifyHmacSHA256(string $webhookSecret, string $content, string $signature): bool
{
$expected = self::signHmacSHA256($webhookSecret, $content);
return hash_equals(strtolower($expected), strtolower($signature));
}
}标准请求结构
| 字段 | 类型 | 说明 | 格式 / 长度约束 |
|---|---|---|---|
requestId | string | 本次通知请求 ID | - |
eventType | string | 事件类型,不同业务模块取值不同 | 由具体业务模块定义 |
createdAt | string | 通知创建时间 | 时间字符串,格式为 YYYY-MM-DD HH:mm:ss |
version | string | 报文版本 | 由具体业务模块定义 |
data | object | 具体业务数据 | 以模块文档定义为准 |
具体业务模块的通知事件、字段定义和示例报文,请以对应模块文档为准。
商户返回要求
| 场景 | 要求 |
|---|---|
| 验签通过且业务已受理 | 返回 2xx |
| 业务处理失败 | 建议先落库并返回 2xx,后续由商户内部补偿 |
| 验签失败或请求非法 | 返回非 2xx,平台将视为失败 |
幂等建议
- 使用
requestId作为通知级幂等标识 - 建议先记录原始通知,再异步处理业务更新
- 业务模块如有更细粒度的幂等组合键,请参考对应模块文档