You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在CodeIgniter4中实现与Node/Express的JSON.stringify一致的处理以完成SHA256 Webhook验证

Webhook SHA256签名验证:CodeIgniter4与Node.js JSON序列化匹配方案

问题的核心在于Node.js的JSON.stringify和PHP的json_encode默认行为存在差异,加上你先解析请求体再重新编码的操作,导致生成的payload和Delyva用于签名的原始字符串不一致。下面是两个按优先级排序的解决方案:


方案1:直接使用原始请求体计算签名(最可靠)

Webhook签名通常基于发送方实际传输的原始JSON字节流计算,而非解析后再序列化的结果。你的Node示例代码虽然用了解析后再stringify的方式,但更准确的做法是直接拿原始请求体计算签名,完全避免解析和编码带来的差异。

在CodeIgniter4中获取原始请求体非常简单:

public function postTracking(): ResponseInterface {
    // Set your API secret
    $apiSecret = 'dx6ed8365ad507451595c151b6b61e3dbdcfbb987f';
    
    // 直接获取原始请求体(不做解析)
    $rawPayload = $this->request->getRawInput();
    
    // 从请求头获取签名
    $signature = $this->request->getHeaderLine('X-Delyvax-Hmac-Sha256');
    
    // 计算期望的签名
    $expected = base64_encode( hash_hmac('sha256', $rawPayload, $apiSecret, true) );

    // 签名验证逻辑
    if ($signature !== $expected) {
        return $this->respond(['status' => 'error', 'message' => 'Invalid signature'], 401);
    }

    // 处理Webhook事件...
    return $this->respond([
        'status' => 'success',
        'sign' => $signature,
        'computed' => $expected,
    ]);
}

这个方法直接使用Delyva发送的原始JSON字符串,完全匹配签名计算的数据源,是最不容易出错的方式。


方案2:调整json_encode参数匹配JSON.stringify行为(需解析数据时使用)

如果你必须先解析请求体再处理数据,需要让PHP的json_encode输出和Node的JSON.stringify完全一致的字符串。两者的核心差异可以通过以下参数修正:

  • JSON_UNESCAPED_SLASHES:不转义斜杠(和JSON.stringify一致)
  • JSON_UNESCAPED_UNICODE:不转义Unicode字符
  • JSON_PRESERVE_ZERO_FRACTION:保留浮点数的小数部分(比如14.54不会被转为14或14.540000)
  • 用getJSON(true)解析为关联数组,而非stdClass,保证键顺序和原始JSON一致(PHP 7.3+默认保留关联数组的键顺序)

调整后的代码:

public function postTracking(): ResponseInterface {
    // Set your API secret
    $apiSecret = 'dx6ed8365ad507451595c151b6b61e3dbdcfbb987f';
    
    // 解析为关联数组,保留原始键顺序
    $parsedBody = $this->request->getJSON(true);
    
    // 按JSON.stringify的规则编码
    $payload = json_encode(
        $parsedBody,
        JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION
    );
    
    // 从请求头获取签名
    $signature = $this->request->getHeaderLine('X-Delyvax-Hmac-Sha256');
    
    // 计算期望的签名
    $expected = base64_encode( hash_hmac('sha256', $payload, $apiSecret, true) );

    // 签名验证逻辑
    if ($signature !== $expected) {
        return $this->respond(['status' => 'error', 'message' => 'Invalid signature'], 401);
    }

    // 处理Webhook事件...
    return $this->respond([
        'status' => 'success',
        'sign' => $signature,
        'computed' => $expected,
    ]);
}

额外排查点

如果签名仍然不匹配,可以手动对比Node生成的payload和PHP生成的payload,重点检查:

  • 键的顺序是否一致(JSON.stringify保留插入顺序,PHP 7.3+关联数组也保留)
  • 浮点数格式是否一致(比如14.54是否被转为14.540000)
  • 空对象/数组的处理(比如personnel: {}是否被转为[])
  • 是否有多余的转义字符

内容的提问来源于stack exchange,提问作者NM AA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 12:27:38