共计 2678 个字符,预计需要花费 7 分钟才能阅读完成。
背景痛点
最近在做一个需要集成 ChatGPT Plus 订阅的 Android 应用,发现 Google Play 内购和 ChatGPT API 结合时确实会遇到不少坑。这里总结下常见问题:

- 订阅状态不同步 :用户已经付款成功,但服务端迟迟收不到验证结果
- 退款争议处理 :用户申请退款后,如何及时关闭 API 访问权限
- 跨设备同步 :用户在 A 设备购买后,如何在 B 设备立即生效
- 沙盒测试限制 :测试账号购买后,有效期只有 5 分钟,经常来不及调试
技术方案
直接支付 vs Play Billing
刚开始考虑过直接对接支付渠道,但很快发现几个致命问题:
- 违反 Google Play 政策(必须使用官方内购)
- 需要自行处理税务和退款
- 无法利用 Play Console 的订阅管理功能
最终选择 Play Billing 方案,虽然接入复杂些,但长期来看更可靠。
三方验证流程
整个购买验证流程可以分为三个关键环节:
- 客户端通过 BillingClient 发起购买
- 服务端用 PurchaseToken 向 Google 验证
- 验证通过后,服务端开通对应 ChatGPT API 权限
flowchart LR
A[客户端] -->| 发起购买 | B[Google Play]
B -->| 返回 Purchase| A
A -->| 上传 PurchaseToken| C[服务端]
C -->| 验证 Purchase| B
C -->| 开通 API 权限 | D[OpenAI]
代码实现
BillingClient 初始化(Kotlin)
val billingClient = BillingClient.newBuilder(context)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases() // 必须开启
.build()
billingClient.startConnection(object : BillingClientStateListener {override fun onBillingSetupFinished(billingResult: BillingResult) {if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
// 查询已有购买记录
val purchases = billingClient.queryPurchases(QueryPurchasesParams.newBuilder()
.setProductType(BillingClient.ProductType.SUBS)
.build()).purchasesList
// 同步到服务端
syncPurchasesToServer(purchases)
}
}
override fun onBillingServiceDisconnected() {// 实现自动重连逻辑}
})
PurchaseToken 验证(服务端伪代码)
def verify_purchase(token):
# 获取 Google 公钥
cert_url = "https://www.googleapis.com/robot/v1/metadata/x509/..."
response = requests.get(cert_url)
certs = response.json()
# 解码 JWT
header = jwt.get_unverified_header(token)
cert = certs[header['kid']]
public_key = load_pem_x509_certificate(cert).public_key()
try:
# 验证签名和时间有效性
payload = jwt.decode(
token,
public_key,
algorithms=["RS256"],
audience="your.package.name",
issuer="https://android.clients.google.com"
)
# 检查订阅状态
if payload['purchaseState'] == 0: # 已购买
return {
'valid': True,
'product_id': payload['productId'],
'expiry_time': payload['expiryTimeMillis']
}
except Exception as e:
logger.error(f"验证失败: {str(e)}")
return {'valid': False}
生产级考量
网络中断处理
建议采用三级缓存策略:
- 内存缓存:最新购买状态
- 本地存储:SharedPreferences 记录关键时间戳
- 服务端备份:定期同步购买凭证
GDPR 合规要点
- 存储 PurchaseToken 时要加密
- 提供用户数据导出功能
- 退款后 30 天内删除关联的 API 调用记录
政策红线
特别注意这些会导致应用下架的行为:
- 引导用户到网站完成支付
- 提供兑换码绕过内购
- 未正确处理「待处理交易」状态
避坑指南
测试环境配置
- 在 Play Console 添加测试 License
- 设备上登录测试账号(邮箱需白名单)
- 使用特定 SKU 前缀(android.test.purchased)
待处理交易处理
purchasesUpdatedListener = PurchasesUpdatedListener { billingResult, purchases ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
purchases?.forEach { purchase ->
if (purchase.purchaseState == Purchase.PurchaseState.PENDING) {// 显示 "等待支付完成" 提示} else {
// 正常处理购买
handlePurchase(purchase)
}
}
}
}
生命周期管理
建议实现这些状态监听:
- onPurchaseExpired:关闭 API 权限
- onGracePeriod:显示续期提醒
- onAccountHold:暂停服务但保留数据
自查清单
完成集成后,建议逐项检查:
- [] 测试账号能完成完整购买流程
- [] 服务端能正确解析各种 PurchaseToken
- [] 退款后 API 调用立即被拒绝
- [] 跨设备登录时订阅状态同步
- [] 符合 Google Play 政策所有要求
整个集成过程比预想的复杂,特别是状态同步和异常处理部分。建议开发时准备好 Google Play Developer API 的配额,因为验证接口有调用频次限制。遇到问题多查官方文档,最近他们更新了不少关于订阅状态的说明。
正文完
发表至: 技术开发
近一天内
