PHP中验证Apple内购通知JWS的问题求助
解决Apple内购通知JWS解码验证错误的方案
错误原因
你混淆了Apple共享密钥的用途:验证Apple发送的内购通知JWS需要用Apple的公钥,而非共享密钥。共享密钥是用来生成你请求Apple App Store API时的JWT令牌的,不能用来验证Apple发来的签名。你直接把共享密钥传入Key对象,ES256是ECDSA非对称加密算法,OpenSSL无法将对称密钥解析为合法公钥,所以抛出了格式错误。
正确验证步骤及代码
验证Apple内购通知的JWS,需要先从Apple官方获取对应公钥,再匹配JWS头部的kid字段找到正确密钥,最后完成签名验证和解码:
require_once dirname(__FILE__) . '/vendor/autoload.php'; use Firebase\JWT\JWT; use Firebase\JWT\JWK; // 获取Apple内购公钥集合(建议缓存,不要每次请求都拉取) $jwksContent = file_get_contents('https://api.storekit.itunes.apple.com/inApps/v1/publicKeys'); $jwks = json_decode($jwksContent, true); // 读取Apple发送的通知内容 $rawNotification = file_get_contents('php://input'); $notificationData = json_decode($rawNotification, true); $signedPayload = $notificationData["signedPayload"]; // 解析JWS头部,提取kid字段用于匹配公钥 $jwsParts = explode('.', $signedPayload); $headerRaw = str_replace(['-', '_'], ['+', '/'], $jwsParts[0]); $header = json_decode(base64_decode($headerRaw), true); $targetKid = $header['kid']; // 从公钥集合中找到匹配的JWK $matchedJwk = null; foreach ($jwks['keys'] as $key) { if ($key['kid'] === $targetKid) { $matchedJwk = $key; break; } } if (!$matchedJwk) { die('无法找到匹配的Apple内购公钥'); } // 将JWK转换为可用于验证的密钥对象 $verificationKey = JWK::parseKey($matchedJwk); // 解码并验证JWS签名 try { $decodedPayload = JWT::decode($signedPayload, $verificationKey); // 处理解码后的内购数据 var_dump($decodedPayload); } catch (Exception $e) { echo '签名验证失败: ' . $e->getMessage(); }
关键注意事项
- 缓存公钥:不要每次处理通知都请求Apple的公钥接口,建议将公钥缓存1小时以上,减少请求开销
- 共享密钥的正确用法:当你需要主动调用Apple App Store API(比如查询订单、退款)时,才用共享密钥生成JWT令牌,作为请求的
Authorization头 - 环境要求:确保PHP环境已安装OpenSSL扩展,且版本支持ES256算法
内容的提问来源于stack exchange,提问作者Camille Gallet
相关产品推荐
相关产品推荐

