Android Google Play Subscription 未自动续订
结论
这次续订失败并不是 react-native-iap 引起的。订阅的自动续订由 Google Play Billing 管理,客户端库只负责发起购买并返回 purchaseToken。
从时间戳来看,这笔订阅的有效期约为 5 分钟:
startTimeMillis = 1627031259058
expiryTimeMillis = 1627031557423
相差约 298 秒
这个时长符合 Google Play 测试订阅的加速周期,不是正常的生产订阅周期。测试订阅经过有限次数的加速续订后,Google Play 会自动将其取消。如果使用了模拟付款被拒的测试支付方式,续订也会因为扣款失败而终止。两种情况都可能返回:
{
"autoRenewing": false,
"cancelReason": 1
}
应先检查测试账号、测试支付方式,以及订阅是否已经走完 Google Play 规定的测试生命周期。
返回字段说明
响应中的几个字段含义如下:
acknowledgementState: 1:购买已经确认,不会因为没有调用 acknowledge 而被退款。autoRenewing: false:Google Play 不会再为这笔订阅发起下一次自动扣款。cancelReason: 1:订阅由系统取消。常见原因包括续订扣款失败,测试订阅被系统终止时也可能出现这个值。expiryTimeMillis:当前已支付或已授予的订阅权益截止时间。paymentState: null:查询时订阅已经过期或取消,该字段可能为空,不能仅凭它判断首次购买是否成功。userCancellationTimeMillis: null:没有用户主动取消的时间记录,因此更可能是系统终止。
cancelReason: 1 只能说明订阅由系统取消,无法单独确定具体是哪一种付款问题。
优先检查测试环境
1. 确认是否使用了 License Tester
检查发起购买的 Google 账号是否已加入 Google Play Console 的 License Testing 列表。
测试订阅采用加速后的续订周期,而且不会无限续订。达到测试续订次数上限后,Google Play 会自动结束订阅。加速周期和续订次数可能随着时间或 Play Billing 测试机制的变化而调整,应以当前 Google Play 官方测试文档和控制台配置为准。
如果订阅经历了若干次短周期续订后才返回这个结果,通常只是测试生命周期正常结束。重新发起一笔测试购买即可继续验证流程。
2. 检查测试支付方式
购买时,注意 Google Play 支付页面中选择的测试支付方式。测试环境通常可以模拟以下结果:
- 支付始终成功;
- 支付始终被拒绝;
- 延迟支付;
- 续订扣款失败。
如果选择了会拒绝付款的测试方式,首次购买可能成功,但下一次续订会因扣款失败而被系统取消。
测试自动续订时,应选择能够持续成功扣款的测试支付方式,再重新购买订阅。
3. 检查订阅商品类型
确认 Google Play Console 中配置的是自动续订订阅,而不是预付费方案。预付费方案到期后不会自动续订,只能由用户延期或重新购买。
还需要检查:
- 对应 base plan 是否处于有效状态;
- base plan 是否启用了 auto-renewing;
- 测试版本是否包含正确的商品配置;
- 应用包名、
subscriptionId和购买账号是否一致。
如果生产环境也发生同样的问题
真实用户的订阅可能因以下原因被 Google Play 取消:
- 信用卡过期;
- 银行拒绝扣款;
- 余额不足;
- 支付资料异常;
- Google Payments 风控;
- 用户所在地区或付款方式不再可用;
- 订阅进入宽限期和账号保留期后,扣款仍未完成。
应用无法强制 Google Play 再次扣款,也不应在客户端修改订阅状态。通常可以引导用户打开 Play 商店的订阅管理页面,更新付款方式或重新订阅:
import { Linking } from 'react-native';
const packageName = 'com.example.app';
const subscriptionId = 'premium_monthly';
const url =
`https://play.google.com/store/account/subscriptions` +
`?sku=${encodeURIComponent(subscriptionId)}` +
`&package=${encodeURIComponent(packageName)}`;
await Linking.openURL(url);
如果 Google Play 已经彻底取消订阅,用户通常需要重新购买。应用重复查询并不能让原来的 purchaseToken 恢复续订。
服务端应如何验证
服务端不能只读取 autoRenewing,至少还要判断:
expiryTimeMillis是否晚于当前时间;- 当前订阅状态;
- 是否处于 grace period 或 account hold;
- 是否已经取消;
- 是否发生商品升级、降级或重新购买;
- 是否出现新的或关联的 purchase token。
使用旧版 purchases.subscriptions.get 时,可以按下面的方式判断:
const response = await androidPublisher.purchases.subscriptions.get({
packageName: 'com.example.app',
subscriptionId: 'premium_monthly',
token: purchaseToken,
});
const subscription = response.data;
const expiryTime = Number(subscription.expiryTimeMillis);
const isEntitled = expiryTime > Date.now();
const result = {
isEntitled,
autoRenewing: subscription.autoRenewing === true,
cancelReason: subscription.cancelReason ?? null,
acknowledgementState: subscription.acknowledgementState,
};
判断时要区分”当前是否拥有权益”和”到期后是否继续续订”。
expiryTimeMillis > 当前时间
表示用户当前仍可能拥有订阅权益,而:
autoRenewing === false
只表示订阅到期后不会继续自动扣款。用户主动取消订阅后,在已付款周期结束前,也可能处于”仍有权益但不再续订”的状态。
新实现建议使用 Google Play Developer API 提供的订阅查询接口,并按照订阅生命周期状态进行判断,不要只依赖旧接口中的 cancelReason。具体接口版本和状态枚举应以项目当前使用的 Google API 客户端及官方文档为准。
建议的完整排查流程
- 确认购买账号是否为 License Tester。
- 根据
startTimeMillis和expiryTimeMillis判断是否为加速测试订阅。 - 检查购买时选择的测试支付方式是否会模拟扣款失败。
- 查看这笔测试订阅此前是否已经发生过多次短周期续订。
- 确认商品对应的是自动续订 base plan。
- 在 Google Play Console 中检查订单和订阅状态。
- 重新购买一笔测试订阅,并记录每次服务端查询的结果。
- 接入 Real-time Developer Notifications,在续订、取消、宽限期和账号保留状态变化时更新服务端数据。
- 对生产订单检查付款失败、宽限期和 account hold,不要把所有
cancelReason: 1都归因于客户端错误。
这份响应中的 acknowledgementState 已经是 1,无需重点排查购买确认逻辑。更应该检查的是测试订阅生命周期和测试付款方式。如果订阅在经历若干个约 5 分钟的周期后被取消,通常属于 Google Play 测试环境的预期行为。如果第一次续订就失败,则应重点检查所选的测试支付方式和订阅 base plan 配置。
备注:内容仅供参考。
