PHP服务端确认Google订阅购买遇OAuth2令牌缺失问题求助
问题描述
我尝试在不安装任何第三方包的情况下,通过PHP服务端调用Google Android Publisher API确认订阅购买。虽已创建API密钥,但仍收到错误提示:Request is missing required authentication credential. Expected OAuth 2 access token。
请问我该从何处获取并如何生成OAuth 2 access token?API密钥为何无法满足需求?
参考了Google官方文档的订阅确认接口,我的PHP代码如下:
$data = []; $packageName = $request->package_name; // 替换为你的应用包名 $token = $request->purchase_token; // 替换为订阅token try { // 替换为实际值 $url = "https://androidpublisher.googleapis.com/androidpublisher/v3/applications/$packageName/purchases/subscriptionsv2/tokens/$token"; // 设置cURL选项 $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 注意:仅用于演示,生产环境必须验证证书 // 设置Authorization头 $apiKey = '38ee606c94233649f666666666666666666'; // 替换为安全存储在.env中的实际API密钥 $authorizationHeader = "Authorization: Bearer " . $apiKey ; curl_setopt($ch, CURLOPT_HTTPHEADER, [$authorizationHeader]); // 执行请求 $response = curl_exec($ch); // 检查错误 if (curl_errno($ch)) { $error = curl_error($ch); $data = $error; return new ProductResource(['status' => '1', 'message' => 'Purchases subscriptions acknowledge curl_errno', 'result' => $resultSave, 'data' => $response ]); } else { // 处理响应 $data = json_decode($response, true); // 在此处理响应数据 // echo "Subscription details: " . json_encode($data, JSON_PRETTY_PRINT); } if ($resultSave) { return new ProductResource(['status' => '1', 'message' => 'Purchases subscription saved in database and acknowledge successfully', 'result' => $resultSave, 'data' => $response ]); } else { return new ProductResource(['status' => '1', 'message' => 'Purchases subscriptions not saved in database and acknowledge successfully', 'result' => $resultSave, 'data' => $response ]); } } catch (Throwable $e) { // echo "Exception: " . $e->getMessage(); $data = $e->getMessage(); return new ProductResource(['status' => '0', 'message' => 'Failed to acknowledge subscriptions purchases catch (Throwable) ', 'result' => $resultSave, 'data' => $data]); }
运行代码后收到如下错误:
{ "error": { "code": 401, "message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.", "errors": [ { "message": "Login Required.", "domain": "global", "reason": "required", "location": "Authorization", "locationType": "header" } ], "status": "UNAUTHENTICATED", "details": [ { "@type": "type.googleapis.com/google.rpc.ErrorInfo", "reason": "CREDENTIALS_MISSING", "domain": "googleapis.com", "metadata": { "service": "androidpublisher.googleapis.com", "method": "androidpublisher.SubscriptionPurchasesV2Service.Get" } } ] } }
解决方案
一、API密钥无法使用的原因
Google Android Publisher API涉及应用私有购买数据的访问,属于需要服务级身份验证的接口。API密钥仅适用于公开数据查询(如部分公开Google Maps数据),无法证明服务端有权限访问你的应用订阅数据,因此必须使用OAuth 2.0服务账号身份验证。
二、生成OAuth 2.0 Access Token的步骤
需通过Google服务账号生成Access Token,具体流程:
- 登录Google Cloud控制台,创建服务账号,并为其分配
Android Publisher角色(确保该账号拥有目标应用的订阅数据访问权限)。 - 下载该服务账号的JSON密钥文件,存放在服务端安全目录(禁止暴露到公网)。
- 基于JSON密钥生成JWT令牌,向Google Token端点请求Access Token。
手动生成JWT并获取Access Token的PHP代码示例
function getGoogleAccessToken($serviceAccountJsonPath) { $serviceAccount = json_decode(file_get_contents($serviceAccountJsonPath), true); // 构建JWT头部 $header = json_encode(['alg' => 'RS256', 'typ' => 'JWT']); // 构建JWT载荷 $iat = time(); $exp = $iat + 3600; // Token有效期1小时 $payload = json_encode([ 'iss' => $serviceAccount['client_email'], 'scope' => 'https://www.googleapis.com/auth/androidpublisher', 'aud' => 'https://oauth2.googleapis.com/token', 'iat' => $iat, 'exp' => $exp ]); // 编码头部和载荷 $base64Header = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header)); $base64Payload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload)); // 生成签名 $signature = ''; openssl_sign("$base64Header.$base64Payload", $signature, $serviceAccount['private_key'], OPENSSL_ALGO_SHA256); $base64Signature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature)); // 拼接JWT $jwt = "$base64Header.$base64Payload.$base64Signature"; // 请求Access Token $ch = curl_init('https://oauth2.googleapis.com/token'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ 'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion' => $jwt ])); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); // 生产环境必须开启 $response = curl_exec($ch); curl_close($ch); $tokenData = json_decode($response, true); return $tokenData['access_token'] ?? null; }
三、修改订阅验证代码
在原代码中,替换API密钥的Authorization头为获取到的Access Token:
// 替换为你的服务账号JSON密钥路径 $accessToken = getGoogleAccessToken('/path/to/your/service-account.json'); if (!$accessToken) { return new ProductResource(['status' => '0', 'message' => 'Failed to get access token', 'result' => $resultSave, 'data' => []]); } // 设置Authorization头 $authorizationHeader = "Authorization: Bearer " . $accessToken; curl_setopt($ch, CURLOPT_HTTPHEADER, [$authorizationHeader]);
注意事项
- 服务账号JSON密钥需妥善保管,禁止提交到代码仓库或暴露给客户端。
- Access Token有效期为1小时,建议缓存Token,避免每次请求重复生成。
- 生产环境必须开启
CURLOPT_SSL_VERIFYPEER,关闭会带来安全风险。
内容的提问来源于stack exchange,提问作者Zeone line
相关产品推荐
相关产品推荐

