使用服务账号验证Android订阅Token时遇401权限不足问题排查
如何解决Google Play订阅Token验证的401权限错误?
我需要验证应用内订阅的购买Token,但服务器端调用Google Play Developer API时持续返回权限不足的401错误,具体错误日志如下:
File "/usr/local/lib/python3.10/site-packages/googleapiclient/http.py", line 938, in execute raise HttpError(resp, content, uri=self.uri) googleapiclient.errors.HttpError: <HttpError 401 when requesting https://androidpublisher.googleapis.com/androidpublisher/v3/applications/com.mycompany.myapp/purchases/subscriptionsv2/tokens/TOKEN_PASSED_IN_FROM_MY_APP?alt=json returned "The current user has insufficient permissions to perform the requested operation.". Details: "[{'message': 'The current user has insufficient permissions to perform the requested operation.', 'domain': 'androidpublisher', 'reason': 'permissionDenied'}]">
已尝试的操作
- 创建自定义服务账号,将其邮箱添加至内部测试人员列表,同时在Play控制台「用户与权限」中分配Admin角色;等待24-48小时,修改订阅内容触发更新,均无效
- 用个人开发者账号在Google API Explorer中调用相同接口、传入相同Token可成功,但无法用服务账号登录测试
当前代码调用方式
service = googleapiclient.discovery.build("androidpublisher", "v3") # 使用从APP获取的Token验证购买 result = service.purchases().subscriptionsv2().get(packageName=PACKAGENAME, token=token).execute()
补充调试信息
- 服务账号的GCP IAM权限:
执行命令gcloud projects get-iam-policy myproject --flatten='bindings[].members' --format='table(bindings.role)' --filter='bindings.members:MY_SERVICE_ACCOUNT',返回角色为:
ROLE roles/financialservices.admin
- 服务器代码输出的凭据身份:
添加日志logger.debug(f"VERGOOGLE: service.http = {service._http.credentials._service_account_email}"),输出结果为:
VERGOOGLE: service.http = default
同时日志显示正在请求元数据服务:
Making request: GET http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/?recursive=true Making request: GET http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/SOMEID-compute@developer.gserviceaccount.com/token?scopes=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fandroidpublisher Refreshing credentials due to a 401 response. Attempt 1/2.
疑问与待排查点
- 为什么请求元数据服务时显示的是
SOMEID-compute@developer.gserviceaccount.com而非我创建的自定义服务账号邮箱? - 如何确保
default服务账号就是我配置的自定义服务账号? - 还有哪些调试方向可以定位权限问题?
关键调试与修复步骤
1. 确认当前使用的服务账号身份
从日志可以看出,你的代码正在使用默认Compute Engine服务账号(SOMEID-compute@developer.gserviceaccount.com),而非你在Play控制台授权的自定义服务账号——这是核心问题。
执行以下命令验证实例的默认服务账号:
gcloud compute instances describe YOUR_INSTANCE_NAME --zone YOUR_ZONE --format='value(serviceAccounts[0].email)'
如果返回的是SOMEID-compute@developer.gserviceaccount.com,说明确实在使用默认账号。
2. 切换到自定义服务账号
有两种方式让代码使用授权过的自定义服务账号:
方式一:为GCE实例绑定自定义服务账号
- 进入GCP控制台 → 计算引擎 → 实例
- 停止目标实例(必须停止才能修改配置)
- 点击「编辑」→ 在「服务账号」下拉菜单中选择你的自定义服务账号
- 保存并启动实例,重新测试API调用
方式二:代码中显式加载服务账号密钥
如果不想修改实例配置,可直接在代码中指定自定义服务账号的JSON密钥:
from google.oauth2 import service_account import googleapiclient.discovery # 加载自定义服务账号密钥 credentials = service_account.Credentials.from_service_account_file( 'path/to/your-service-account-key.json', scopes=['https://www.googleapis.com/auth/androidpublisher'] ) # 使用指定凭据构建服务 service = googleapiclient.discovery.build( "androidpublisher", "v3", credentials=credentials ) # 调用验证接口 result = service.purchases().subscriptionsv2().get( packageName=PACKAGENAME, token=token ).execute()
3. 检查权限配置正确性
即使切换了账号,也要确保:
- 自定义服务账号的邮箱已添加到Play控制台「用户与权限」中,分配**「开发者」或「订阅管理员」角色**(无需用Admin,遵循最小权限原则)
- 等待至少1小时让权限生效(无需等24小时,一般1小时内同步完成)
- 为服务账号分配GCP IAM角色
roles/androidpublisher.editor或roles/androidpublisher.viewer(financialservices.admin与Play开发者API权限无关)
4. 直接测试服务账号权限
用自定义服务账号密钥编写测试脚本,验证权限是否正常:
from google.oauth2 import service_account import googleapiclient.discovery credentials = service_account.Credentials.from_service_account_file( 'path/to/key.json', scopes=['https://www.googleapis.com/auth/androidpublisher'] ) service = googleapiclient.discovery.build("androidpublisher", "v3", credentials=credentials) try: result = service.purchases().subscriptionsv2().get( packageName="com.mycompany.myapp", token="TOKEN_FROM_APP" ).execute() print("验证成功:", result) except Exception as e: print("错误:", e)
如果脚本执行成功,说明权限配置正确,问题出在服务器实例的默认账号;如果仍报错,检查Play控制台是否正确添加了该服务账号。
内容的提问来源于stack exchange,提问作者Hoegje
相关产品推荐
相关产品推荐

