PAYATHON 2026

Amazon FPS IPN Verification 出现 Invalid signature 错误

数据小许

结论

“将所有变量的首字母改为大写”并不准确。需要调整的是传给 validateRequest() 的请求参数名,也就是 $params 数组的键,而非 PHP 局部变量名。

Amazon FPS 在校验签名时会区分参数名的大小写。旧版 PHP 示例或 SDK 可能使用首字母大写的参数名构造待签名字符串,而 IPN 请求中的参数名以小写字母开头,两者不一致就会导致 Invalid signature。确认问题由这种版本兼容差异引起后,可以在验证前用 ucfirst() 转换参数键。

修改方法

不要直接修改 $_POST,也不要改动参数值。新建一个数组,只转换键名:

<?php

require_once 'Amazon/FPS/Client.php';

class Amazon_FPS_IPNVerificationSampleCode
{
    public static function test()
    {
        $params = array();

        foreach ($_POST as $key => $value) {
            $params[ucfirst($key)] = $value;
        }

        // 必须填写接收此次 IPN 请求的实际 URL
        $urlEndPoint = 'https://www.example.com/ipn.php';

        $client = new Amazon_FPS_Client(
            AWS_ACCESS_KEY_ID,
            AWS_SECRET_ACCESS_KEY
        );

        echo $client->validateRequest(
            $params,
            $urlEndPoint,
            'POST'
        ) . "\n";
    }
}

Amazon_FPS_IPNVerificationSampleCode::test();
?>

转换后的参数键类似这样:

signature        -> Signature
signatureVersion -> SignatureVersion
certificateUrl   -> CertificateUrl
transactionId    -> TransactionId

改动的是数组键。将 $params 改成 $Params,或将 $urlEndPoint 改成 $UrlEndPoint,都不会影响签名内容,也无法解决问题。

为什么大小写会导致验证失败

签名所用的规范化字符串通常包含:

  • HTTP 请求方法;
  • 接收 IPN 的主机和路径;
  • 参数名称;
  • 参数值;
  • 参数的排序和编码方式。

参数名本身也是签名数据的一部分,所以 signatureVersion 和 SignatureVersion 会被视为两个不同的名称。只要验证端构造的字符串与 Amazon 签名时使用的字符串不一致,校验就会返回 Invalid signature。

仍然报错时应检查什么

首字母转换只适用于旧示例或旧 SDK 与 IPN 参数大小写不兼容的情况,不能解决所有签名错误。还需要逐项检查:

  1. $urlEndPoint 必须与 Amazon 实际请求的公开 URL 完全一致,包括协议、域名、端口和路径。
  2. 如果请求经过反向代理或负载均衡器,不要用内部 HTTP 地址代替外部 HTTPS 地址。
  3. 必须传入原始 IPN 参数。验证前不要执行 trim()、HTML 转义、字符替换或数值格式化。
  4. 验证 POST 请求时,第三个参数应保持为 "POST"。
  5. 不要对 PHP 已经解析的值再次调用 urldecode(),否则 +、空格和百分号编码可能发生变化。
  6. 确认 FPS SDK 与示例代码的版本兼容。

如果 SDK 已经能够按 IPN 的原始参数名正确处理签名,统一调用 ucfirst() 反而会破坏验证。决定是否保留这段转换逻辑之前,应先比较转换前后的参数键,并检查 validateRequest() 内部期望的参数命名方式。

备注:内容仅供参考。