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 参数大小写不兼容的情况,不能解决所有签名错误。还需要逐项检查:
$urlEndPoint必须与 Amazon 实际请求的公开 URL 完全一致,包括协议、域名、端口和路径。- 如果请求经过反向代理或负载均衡器,不要用内部 HTTP 地址代替外部 HTTPS 地址。
- 必须传入原始 IPN 参数。验证前不要执行
trim()、HTML 转义、字符替换或数值格式化。 - 验证
POST请求时,第三个参数应保持为"POST"。 - 不要对 PHP 已经解析的值再次调用
urldecode(),否则+、空格和百分号编码可能发生变化。 - 确认 FPS SDK 与示例代码的版本兼容。
如果 SDK 已经能够按 IPN 的原始参数名正确处理签名,统一调用 ucfirst() 反而会破坏验证。决定是否保留这段转换逻辑之前,应先比较转换前后的参数键,并检查 validateRequest() 内部期望的参数命名方式。
备注:内容仅供参考。
