接入你的 App
Google Play 订阅验证
适合通过 Google Play 卖订阅、自己没有后端的安卓 App。App 用 API key 把购买凭证发给网关,网关用你的服务账号去问 Google,告诉你这个订阅现在是否有效。验证免费。
#开始之前
- 一个 Play Console 账号,需要是账号所有者或管理员,才能邀请用户。
- 一个能在 Google Cloud 控制台建项目的 Google 账号(任何 Google 账号都可以)。
- App 的包名(applicationId),例如 com.example.app;并且已在 Play Console 建好至少一个订阅商品。
- 操作大约 15 分钟;之后 Google 让权限生效最多要一天。
#1. 申请服务账号 JSON
这个 JSON 文件就是服务账号的密钥,网关只用它向 Google 查询你这个 App 的购买,不做别的。Google 叫它「服务账号密钥」,不是 OAuth 客户端文件(client_secret_….json)。
- 1
打开 Google Cloud 控制台,选一个项目
console.cloud.google.com用顶部的项目选择器。哪个项目都行;单独新建一个专门用来验票更清楚。
- 2
启用 Play API
API 和服务(APIs & Services)→ 库(Library)→ 搜索 Google Play Android Developer API → 启用(Enable)按完整名称搜索。如果按钮显示「管理(Manage)」,说明已经启用了。
- 3
创建服务账号
IAM 和管理(IAM & Admin)→ 服务账号(Service accounts)→ 创建服务账号(Create service account)名称随意,例如 play-verify。后面「授予项目访问权限」「授予用户访问权限」两个可选步骤直接跳过,它不需要任何 Cloud 角色。点「完成(Done)」。
- 4
下载它的 JSON 密钥
点开这个服务账号 → 密钥(Keys)→ 添加密钥(Add key)→ 创建新密钥(Create new key)→ 选 JSON → 创建(Create)浏览器会下载一个 .json 文件。请妥善保存:Google 不会再显示第二次,拿到它的人就能查询你 App 的购买记录。
- 5
复制服务账号邮箱
形如 play-verify@your-project.iam.gserviceaccount.com,在服务账号页面能看到,文件里的 client_email 也是它。下一步要用。
「创建新密钥」是灰的或被拒绝?
#2. 在 Play Console 给它开权限
- 1
邀请服务账号
play.google.com/console → 用户和权限(Users and permissions)→ 邀请新用户(Invite new users)「电子邮件地址」填上一步复制的服务账号邮箱。服务账号不需要接受邀请。
- 2
勾选这两项权限
应用权限(App permissions)→ 添加应用(Add app)→ 选你的 App勾选「查看财务数据、订单和取消调查问卷回复(View financial data, orders, and cancellation survey responses)」和「管理订单和订阅(Manage orders and subscriptions)」,其他都不用勾。如果改在「账号权限(Account permissions)」里勾这两项,就对你所有 App 生效。
- 3
发送邀请
应用(Apply)→ 邀请用户(Invite user)之后在「用户和权限」里能看到这个服务账号,状态为「有效(Active)」。
为什么要这两项权限
Google 最多要一天才让权限生效
#3. 填到 API 密钥上
- 1
打开 App 用的那个密钥
控制台 → API 密钥 → 编辑建议给 App 单独建一个密钥(见下方)。
- 2
填写「Google Play 订阅验证」
设置 Google Play 验证「包名」填 App 的 build.gradle 里的 applicationId,必须完全一致;选择第 1 步下载的 JSON 文件,保存。
- 3
看保存后的提示
保存前网关会先拿这个文件向 Google 试一次。如果 Google 还不认,弹窗会说明缺什么,通常是第 1 步的 API 没启用或第 2 步的权限还没生效。文件加密保存,之后不再显示;要更换就重新上传。
给 App 单独用一个 key
#4. 在 App 里验票
购买完成后马上验证一次;之后每次打开 App,用保存的凭证再验证一次。不要让 App 启动等着结果:先按上次保存的结果显示,验证返回后再更新。同一凭证 1 分钟内直接返回缓存结果。每个 key 默认每分钟最多验证 120 次,当前上限显示在 key 的 Google Play 设置里,需要更多可以联系客服为这个 key 单独调高。超过上限的请求返回 429,不会记进请求日志,所以要沿用上次保存的结果。
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:in_app_purchase/in_app_purchase.dart';
const apiKey = 'sk-YOUR_KEY';
Future<Map<String, dynamic>?> checkSubscription(String purchaseToken) async {
final res = await http.post(
Uri.parse('https://www.aisplice.store/v1/google-play/verify'),
headers: {
'Authorization': 'Bearer $apiKey',
'Content-Type': 'application/json',
// Request signing on for this key? Then this call must be signed too:
// ...signedHeaders('POST', '/v1/google-play/verify'),
},
body: jsonEncode({'purchase_token': purchaseToken}),
);
if (res.statusCode != 200) return null; // keep the last saved answer
return jsonDecode(res.body) as Map<String, dynamic>;
}
// In your purchaseStream listener:
Future<void> onPurchase(PurchaseDetails purchase) async {
if (purchase.status != PurchaseStatus.purchased &&
purchase.status != PurchaseStatus.restored) return;
final token = purchase.verificationData.serverVerificationData;
final status = await checkSubscription(token);
if (status != null && status['active'] == true) {
await saveEntitlement(token, status); // your local storage
unlockPaidFeatures();
}
if (purchase.pendingCompletePurchase) {
await InAppPurchase.instance.completePurchase(purchase); // acknowledges on Android
}
}#5. 决定解锁什么
| 验证结果 | App 怎么做 | 常见原因 |
|---|---|---|
| active: true, in_trial: true | 解锁,并按 expiry_time 显示“试用到期时间”。 | 试用中 |
| active: true | 解锁,有效到 expiry_time。 | 已付费,包括已取消自动续费但还没到期的 |
| subscribed: true, active: false | 不解锁,显示续订页面。 | 已过期、已暂停、账户保留 |
| subscribed: false | 不解锁,显示订阅页面。 | 无法识别的凭证、付款还没完成 |
| HTTP 502 / 503 | 沿用上次保存的结果,稍后重试。 | Google 没响应,或服务账号有问题 |
把 expiry_time 和结果一起保存。两次验证之间,到这个时间之前都当作可用,这样手机离线也能正常用。
#6. 每笔购买都要确认
不确认的购买 3 天后会被自动退款
#7. 保护 App 里的密钥
放在 App 里的 key 抓包就能看到。给这个 key 开启请求签名,这样别人复制走 key 也单独用不了;打包时做混淆,并给 key 设额度上限。
签名对这个 key 的所有调用都生效
#8. 查看验票记录
每次验证(包括命中缓存和失败的)都会出现在控制台 → 请求日志里,记为这个 key 下一条免费的“Google Play 验证”。可以按完整购买凭证、凭证末尾几位,或用户从 Google 收据邮件里发给你的订单号(GPA.…)搜索。
- google_play_api_disabled:JSON 文件所属的项目没有启用第 1 步的 Play API。
- google_play_permission_denied:第 2 步的邀请没做、没勾那两项权限、加到了别的 App,或者还没生效。
- google_play_credentials_invalid:这个 JSON 密钥已在 Google Cloud 被删除或停用。新建一个密钥,重新上传到 API 密钥上。
- 保存时提示「不是服务账号密钥」:你选的是 OAuth 客户端文件。请到服务账号的「密钥」标签页下载。
- 每次都提示找不到购买:密钥上填的包名和 App 的 applicationId 不一致,或者购买凭证来自别的 App。
- 许可测试人员的测试购买会返回 test_purchase: true。
还是没解决?
把请求 ID 或报错信息发给我们,我们来查。