PAYATHON 2026

如何通过 Google Api 测试 In App Billing v3 订阅取消?

支付小周

结论

可以使用 Google Play 许可测试账号测试取消订阅。先用测试账号购买订阅,再前往 Google Play 的“付款和订阅”页面手动取消,最后由服务器重新查询该 purchaseToken 的订阅状态。

需要注意,取消订阅不会让订阅立即失效。用户取消后,通常仍可使用订阅权益,直到当前计费周期结束。此时常见的状态是:

  • 自动续订已关闭;
  • 订阅权益暂时有效;
  • 到达过期时间后,订阅才会失效。

服务器不能只在购买时验证一次订阅状态。应通过 Real-time Developer Notifications 接收状态变更通知,并定期调用 Google Play Developer API 进行校验。

如何执行取消测试

1. 配置测试账号

在 Google Play Console 中,将测试用的 Google 账号配置为许可测试账号,并确保该账号可以安装测试轨道中的应用。

测试前需要确认:

  • Play 商店登录的是测试账号;
  • 安装包来自 Google Play 测试轨道;
  • 应用包名和签名与 Play Console 中的版本一致;
  • 订阅商品已经启用,并对相应测试轨道开放。

许可测试订阅通常采用加速后的续订周期,便于测试续订和过期。具体周期和续订次数可能随 Google Play 的测试规则调整,因此不要将其写死在业务代码中。

2. 购买测试订阅

购买成功后,应用需要将以下信息发送给自己的服务器:

{
  "packageName": "com.example.app",
  "subscriptionId": "premium_monthly",
  "purchaseToken": "purchase-token-from-google-play"
}

服务器使用 purchaseToken 查询这笔订阅。不要只保存订单号,因为在续订、升级和替换订阅时,订单与购买令牌的含义并不相同。

3. 在 Google Play 中取消

使用购买订阅的测试账号打开:

Google Play → 头像 → 付款和订阅 → 订阅

选择对应应用的订阅,然后点击“取消订阅”。

也可以在浏览器中打开该 Google 账号的 Google Play 订阅管理页面进行取消。取消后不要立即删除用户权益,因为当前计费周期通常尚未结束。

服务器如何验证取消状态

如果后端仍在使用旧版订阅查询接口,可以发送以下请求:

GET https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptions/{subscriptionId}/tokens/{purchaseToken}

例如:

curl \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://androidpublisher.googleapis.com/androidpublisher/v3/applications/com.example.app/purchases/subscriptions/premium_monthly/tokens/PURCHASE_TOKEN"

旧版接口的响应中常见以下字段:

{
  "startTimeMillis": "1710000000000",
  "expiryTimeMillis": "1710003600000",
  "autoRenewing": false,
  "cancelReason": 0,
  "userCancellationTimeMillis": "1710001800000"
}

这些字段的含义如下:

  • autoRenewing: false:订阅不会继续自动续订,通常表示用户已经取消订阅,或自动续订已被关闭;
  • expiryTimeMillis:当前订阅权益预计失效的时间;
  • cancelReason:取消原因,只能在接口实际返回该字段时使用;
  • userCancellationTimeMillis:用户执行取消操作的时间,该字段不一定会出现在所有状态下。

判断用户是否仍有订阅权益时,不能只检查 autoRenewing:

function evaluateLegacySubscription(subscription, now = Date.now()) {
  const expiryTime = Number(subscription.expiryTimeMillis);

  return {
    entitled: expiryTime > now,
    autoRenewing: subscription.autoRenewing === true,
    canceled:
      subscription.autoRenewing === false &&
      subscription.userCancellationTimeMillis != null
  };
}

用户刚取消订阅时,结果可能是:

entitled = true
autoRenewing = false
canceled = true

计费周期结束后,结果才会变为:

entitled = false
autoRenewing = false

使用当前订阅状态接口

新接入或正在升级的系统,应优先评估 Google Play Developer API 的 purchases.subscriptionsv2.get:

GET https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptionsv2/tokens/{purchaseToken}

示例:

curl \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://androidpublisher.googleapis.com/androidpublisher/v3/applications/com.example.app/purchases/subscriptionsv2/tokens/PURCHASE_TOKEN"

该接口会返回订阅级状态和 lineItems。判断订阅权益时,至少需要结合以下信息:

  • subscriptionState;
  • lineItems[].expiryTime;
  • 自动续订计划相关字段;
  • 是否处于暂停、宽限期、账号保留或待处理状态。

不要看到状态名称中包含 CANCELED 就直接撤销服务。订阅即使已经取消,只要尚未过期,仍应根据有效期和具体状态判断是否保留权益。

推荐的完整处理方式

只依赖客户端在取消后主动通知服务器并不可靠。用户可能直接在 Play 商店取消订阅,而应用之后可能没有再次启动。可以按以下流程处理:

  1. 购买成功后,客户端将 purchaseToken 发送给服务器。
  2. 服务器调用 Google Play Developer API 验证购买。
  3. 服务器保存用户、商品与 purchaseToken 的对应关系。
  4. 配置 Real-time Developer Notifications,接收取消、续订、过期和暂停等事件。
  5. 收到通知后,服务器使用通知关联的购买令牌再次查询 Developer API。
  6. 服务器根据查询结果更新权益,不能完全依赖通知内容。
  7. 定期校验未收到通知或通知处理失败的记录。

通知只负责告诉服务器“订阅状态发生了变化”。服务器更新状态时,应以 Google Play Developer API 的查询结果为依据。

测试时应验证的状态

测试范围不应只有“购买成功”和“用户取消”,至少还应覆盖:

  • 首次购买并确认;
  • 正常自动续订;
  • 用户取消但订阅尚未过期;
  • 取消后恢复订阅;
  • 到期后权益失效;
  • 支付失败后的宽限期或账号保留状态;
  • 退款或撤销;
  • 升级、降级或替换订阅。

如果只测试取消流程,最重要的断言是:取消后自动续订关闭,但订阅权益一直保留到有效期结束。订阅过期后,服务器才拒绝提供订阅服务。

In-app Billing v3 是较早期的客户端接口。如果项目仍在直接使用它,建议规划迁移到当前版本的 Google Play Billing Library。无论客户端使用哪个版本,服务器都应通过 Google Play Developer API 验证订阅的最终状态。

备注:内容仅供参考。