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

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,具体流程:

  1. 登录Google Cloud控制台,创建服务账号,并为其分配Android Publisher角色(确保该账号拥有目标应用的订阅数据访问权限)。
  2. 下载该服务账号的JSON密钥文件,存放在服务端安全目录(禁止暴露到公网)。
  3. 基于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 09:27:05