如何从 in-app billing API 中移除已取消的订阅?
结论
in-app billing 客户端 API 无法删除订阅记录,订阅也不能通过 consume 处理。
paymentpending 表示付款尚未完成。遇到这类购买时,应用只能等待 Google Play 将其更新为成功或取消状态。此时不要授予订阅权益,也不要尝试确认或消费购买。
如果代码仍能读取到已经过期的订阅,先确认查询结果属于“当前有效购买”还是“历史购买”,同时检查是否受本地缓存或测试账号混用影响。
处理步骤
1. 确认购买状态
使用 Google Play Billing Library 查询当前订阅,并按照 PurchaseState 分别处理:
billingClient.queryPurchasesAsync(
QueryPurchasesParams.newBuilder()
.setProductType(BillingClient.ProductType.SUBS)
.build()
) { billingResult, purchases ->
if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
return@queryPurchasesAsync
}
for (purchase in purchases) {
when (purchase.purchaseState) {
Purchase.PurchaseState.PURCHASED -> {
// 购买成功后再验证 purchaseToken、授予权益并 acknowledge。
handlePurchasedSubscription(purchase)
}
Purchase.PurchaseState.PENDING -> {
// 不授予权益,等待 Google Play 更新状态。
showPaymentPendingMessage()
}
else -> {
// 不视为有效订阅。
}
}
}
}
不要仅凭 purchaseTime 或 isAutoRenewing 判断订阅是否有效:
isAutoRenewing == false只表示订阅不会再次自动续订,并不意味着购买记录会立即消失。purchaseTime记录的是购买发生时间,不一定是当前订阅周期的准确到期时间。- 购买处于
PENDING状态时,不能授予权益,也不应执行确认购买的逻辑。
2. 区分当前购买和历史记录
通过购买历史接口查询时,结果中仍可能包含已经取消或过期的订阅。历史记录不是可删除的列表,也不能直接用来判断用户当前是否拥有权益。
应用启动或恢复购买时,应使用 queryPurchasesAsync() 查询当前购买。生产环境中,最好将 purchaseToken 发送到后端,再由后端通过 Google Play Developer API 验证订阅的实际状态和到期时间。
3. 成功购买后及时确认
只有购买状态变为 PURCHASED 后,才能确认购买:
private fun handlePurchasedSubscription(purchase: Purchase) {
if (!purchase.isAcknowledged) {
val params = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
billingClient.acknowledgePurchase(params) { result ->
if (result.responseCode == BillingClient.BillingResponseCode.OK) {
// 建议在服务端验证成功后授予权益。
}
}
}
}
不要对订阅调用 consumeAsync()。消费操作适用于可以重复购买的应用内商品,不适用于订阅。
4. 测试账号看不到订阅时
依次检查以下内容:
- Google Play 商店当前登录的账号,是否就是发起测试购买的许可测试账号。
- 应用是否从 Google Play 测试轨道安装,而不是使用不同签名或不同账号安装的本地包。
- 当前读取的是否为应用本地或服务端持久化的旧购买数据,而不是本次
queryPurchasesAsync()返回的结果。 - 清除应用自身缓存,重新连接
BillingClient后再次查询。不要把“清除数据”当成取消 Google Play 订阅的方法。
付款待处理状态通常由 Google Play 管理。应用端没有接口可以强制将 PENDING 改为 CANCELED。只有付款最终失败或超时后,Google Play 才会更新购买状态。
服务端取消或撤销
如果持有有效的 purchaseToken,并且项目已正确配置 Google Play Developer API,可以通过服务端取消或撤销订阅。具体使用哪个接口,取决于当前采用的订阅 API 版本:
- 取消通常只会阻止后续续订,用户可能仍可使用权益直至当前周期结束。
- 撤销通常会立即终止权益。
- 对于已经取消、过期或仍处于付款待处理状态的测试购买,不一定还能再次执行这些操作。
这些操作用于在服务端管理购买状态,不能从 Google Play 的购买历史中“删除记录”。调用前应先查询订阅状态,并正确处理“购买不存在”“已经过期”或“当前状态不允许操作”等响应。
注意事项
- 不要根据客户端字段自行推算或覆盖 Google Play 的订阅状态。
- 不要向
PENDING购买授予内容或会员权限。 - 不要尝试消费订阅。
- 不要要求用户通过重复购买覆盖旧的待处理订单。
- 订阅取消或过期后,历史购买记录仍可能保留。应用只需停止将其识别为有效权益,无须删除记录。
- 如果
queryPurchasesAsync()长时间返回明显已经过期的待处理测试购买,应记录账号、商品 ID、订单 ID、purchaseToken和查询响应,再通过 Google Play Console 支持渠道排查测试订单状态。
备注:内容仅供参考。
