Skip to content

Guide

Webhook 接入

Onerway 支持通过 Webhook 向商户系统推送异步通知。商户需提供可访问的接收地址,并在服务端完成签名校验、幂等控制与业务处理。

POST CallbackHMAC-SHA256x-signature

接入要求

  • 根据业务需要开发通知接收接口
  • 确保接口地址可被 Onerway 正常访问
  • 确保接口具备签名校验能力
  • 确保接口具备幂等处理能力

注意

请提供稳定、可公网访问的回调地址。如回调地址发生变更,请及时联系 Onerway 技术团队更新配置。

通知方式

项目说明
请求方法POST
Content-Typeapplication/json;charset=UTF-8
签名算法HMAC-SHA256

请求头

Header Key类型必填说明格式 / 长度约束
Content-Typestring固定为 application/json;charset=UTF-8固定值
x-timestampstringUnix 时间戳秒级时间戳字符串
x-signaturestring使用 webhook_secret 计算出的十六进制签名64 位十六进制字符串

验签规则

签名原文:

text
x-timestamp + "." + rawBody

签名算法:

text
HMAC-SHA256(webhook_secret, x-timestamp + "." + rawBody) -> Hex

content 定义

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));
    }
}

标准请求结构

字段类型说明格式 / 长度约束
requestIdstring本次通知请求 ID-
eventTypestring事件类型,不同业务模块取值不同由具体业务模块定义
createdAtstring通知创建时间时间字符串,格式为 YYYY-MM-DD HH:mm:ss
versionstring报文版本由具体业务模块定义
dataobject具体业务数据以模块文档定义为准

具体业务模块的通知事件、字段定义和示例报文,请以对应模块文档为准。

商户返回要求

场景要求
验签通过且业务已受理返回 2xx
业务处理失败建议先落库并返回 2xx,后续由商户内部补偿
验签失败或请求非法返回非 2xx,平台将视为失败

幂等建议

  • 使用 requestId 作为通知级幂等标识
  • 建议先记录原始通知,再异步处理业务更新
  • 业务模块如有更细粒度的幂等组合键,请参考对应模块文档