Google Play 的 in-app billing for subscriptions 如何延长免费期?
结论
可以。Google Play Developer API 提供 purchases.subscriptions.defer,可以延后自动续订订阅的到期时间和下一次扣款时间。
如需补偿 30 天服务,可以按以下步骤处理:
- 获取用户当前订阅的
purchaseToken。 - 查询并确认订阅当前的到期时间。
- 在该到期时间上增加 30 天。
- 调用
purchases.subscriptions.defer完成延期。
不要只修改应用自己的数据库,因为这不会改变 Google Play 的实际扣款时间,还会造成应用权益状态与 Google Play 订阅状态不一致。
调用地址
REST API:
POST https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptions/{subscriptionId}/tokens/{token}:defer
请求体:
{
"deferralInfo": {
"expectedExpiryTimeMillis": "CURRENT_EXPIRY_TIME_MILLIS",
"desiredExpiryTimeMillis": "NEW_EXPIRY_TIME_MILLIS"
}
}
字段说明:
expectedExpiryTimeMillis:服务器认为该订阅当前的到期时间。desiredExpiryTimeMillis:延期后的新到期时间。- 两个字段都是 Unix Epoch 毫秒时间戳,在 JSON 中以字符串表示。
expectedExpiryTimeMillis 用于并发保护。如果查询完成后,订阅发生了续订、取消恢复或其他状态变化,原来的到期时间可能已经失效。此时 API 会拒绝修改,以免覆盖最新状态。
延长 30 天的计算方式
应在当前到期时间上增加 30 天,不要直接使用”当前时间加 30 天”:
desiredExpiryTimeMillis =
expectedExpiryTimeMillis + 30 × 24 × 60 × 60 × 1000
也就是:
desiredExpiryTimeMillis =
expectedExpiryTimeMillis + 2592000000
例如,当前到期时间是:
1790000000000
延期 30 天后是:
1792592000000
实际开发时,最好使用日期时间库计算,避免混淆时间单位或发生整数溢出。
REST 调用示例
curl -X POST \
"https://androidpublisher.googleapis.com/androidpublisher/v3/applications/com.example.app/purchases/subscriptions/monthly_plan/tokens/PURCHASE_TOKEN:defer" \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"deferralInfo": {
"expectedExpiryTimeMillis": "1790000000000",
"desiredExpiryTimeMillis": "1792592000000"
}
}'
成功响应类似:
{
"newExpiryTimeMillis": "1792592000000"
}
ACCESS_TOKEN 必须通过有权访问相应 Google Play Console 应用的服务账号获取,并使用 Android Publisher API 所需的 OAuth 权限:
https://www.googleapis.com/auth/androidpublisher
Java 调用示例
使用 Google API Java Client 时,可以构造 SubscriptionPurchasesDeferRequest:
long currentExpiryTimeMillis = 1790000000000L;
long thirtyDaysMillis = 30L * 24 * 60 * 60 * 1000;
long desiredExpiryTimeMillis =
currentExpiryTimeMillis + thirtyDaysMillis;
SubscriptionDeferralInfo deferralInfo =
new SubscriptionDeferralInfo()
.setExpectedExpiryTimeMillis(currentExpiryTimeMillis)
.setDesiredExpiryTimeMillis(desiredExpiryTimeMillis);
SubscriptionPurchasesDeferRequest request =
new SubscriptionPurchasesDeferRequest()
.setDeferralInfo(deferralInfo);
SubscriptionPurchasesDeferResponse response =
androidPublisher.purchases()
.subscriptions()
.defer(
"com.example.app",
"monthly_plan",
purchaseToken,
request
)
.execute();
System.out.println(
"New expiry time: " + response.getNewExpiryTimeMillis()
);
具体的类名和方法签名可能会随 Google API Java Client 版本变化。使用较新的客户端时,应以该版本生成的 Android Publisher API 类型为准。
后台补偿流程
管理员工具可以采用以下流程:
- 由客服确认用户身份和订单。
- 从服务器保存的订阅记录中获取
purchaseToken。 - 调用 Google Play Developer API 查询最新的订阅状态。
- 确认订阅仍然有效,并且属于正确的应用和订阅商品。
- 读取 Google Play 返回的当前到期时间。
- 在这个时间上增加 30 天。
- 调用
purchases.subscriptions.defer。 - 保存延期原因、操作人员、原到期时间、新到期时间和 API 响应。
- 再次查询订阅状态,确认本地权益与 Google Play 保持一致。
应用应在购买流程中将 purchaseToken 上传到可信服务器。不要让管理员直接使用用户提交的 token 执行操作,也不要把服务账号凭据放入 Android application。
免费试用配置需要注意
初始的 30 天或 60 天免费试用,应通过 Google Play Console 中的订阅 base plan 和 offer 配置,让 Google Play 管理试用资格和首次扣款时间。
如果所谓的”免费试用”只是应用在本地实现的计时逻辑,Google Play 不会知道这段试用期。此时,defer 只能延期由 Google Play 管理的订阅周期,无法修正纯本地的试用状态。
另外,defer 只能延期现有订阅,不能为任意账号创建一段新订阅。用户必须已经拥有 Google Play API 能识别的订阅购买记录。
其他方案
如果订阅类型、账号状态或 API 限制导致 defer 无法调用,还可以考虑:
- 在应用服务器中添加独立的补偿权益,补偿到期后再恢复以 Google Play 状态为准。
- 对已经产生费用的订单进行全额或部分退款,但退款和延长服务是两回事。
- 创建专用优惠。不过,优惠通常适用于一类符合条件的用户,不适合临时补偿单个客户。
本地补偿权益不会改变 Google Play 的下一次扣款日期。如果需求明确要求”30 天后再扣款”,应优先使用 purchases.subscriptions.defer。调用前还要查阅当前的 Google Play Developer API:purchases.subscriptions.defer,确认目标订阅类型、延期范围和账号权限仍符合最新限制。
备注:内容仅供参考。
