通过 Google Developer API 取消订阅会退款吗?
结论
可以。通过 Google Play Developer API 执行”停止续订”不会自动退款,也不会立即终止用户已经付费获得的订阅权益。用户仍可使用订阅服务,直到当前订阅的 expiryTime。到期后,Google Play 不再自动扣款。
如果订阅已经下架,并准备逐步停止支持,建议调用 purchases.subscriptionsv2.cancel,并使用:
DEVELOPER_REQUESTED_STOP_PAYMENTS
这样可以阻止后续付款,同时禁止用户在 Google Play 中恢复该订阅。不要调用退款或撤销接口。
为什么会出现不同说法
“取消订阅”、“退款”和”撤销权益”是三个不同的操作:
| 操作 | 停止续订 | 退款 | 立即终止访问 |
|---|---|---|---|
| Cancel subscription | 是 | 否 | 否 |
| Refund order | 通常否 | 是 | 通常否 |
| Revoke subscription | 是 | 是或按接口规则处理 | 是 |
Google Play Console 不同历史版本的按钮、文案和操作入口可能不同,社区讨论中也常把取消交易、退款订单和取消订阅混在一起。不能只根据 Console 中的”Cancel”字样判断操作是否会退款,应以具体操作的确认页面和 API 语义为准。
仅将订阅商品从商店下架,也不一定会取消现有用户的自动续订。要让所有存量订阅停止续费,需要逐个处理仍处于有效状态的购买 token。
推荐操作步骤
1. 获取所有有效订阅
从自己的订阅数据库中找出仍然有效或可能继续续订的记录。至少需要以下信息:
- 应用包名
packageName - 订阅购买 token
purchaseToken - 当前到期时间
- 用户与购买 token 的关联关系
不要用订单号代替 purchaseToken。
2. 查询订阅当前状态
调用:
GET https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptionsv2/tokens/{purchaseToken}
根据返回的 subscriptionState、lineItems[].expiryTime 等字段,确认订阅是否仍然有效,以及是否已经停止续订。
3. 停止后续付款
调用:
POST https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptionsv2/tokens/{purchaseToken}:cancel
Content-Type: application/json
Authorization: Bearer ACCESS_TOKEN
请求体:
{
"cancellationContext": {
"cancellationType": "DEVELOPER_REQUESTED_STOP_PAYMENTS"
}
}
curl 示例:
curl -X POST \
"https://androidpublisher.googleapis.com/androidpublisher/v3/applications/com.example.app/purchases/subscriptionsv2/tokens/PURCHASE_TOKEN:cancel" \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"cancellationContext": {
"cancellationType": "DEVELOPER_REQUESTED_STOP_PAYMENTS"
}
}'
这个调用只用于停止后续扣款,不会退款。现有权益应保留到 Google Play 返回的到期时间。
4. 按实际到期时间控制访问权限
服务端不应在 cancel 请求成功后立即关闭权限,而应继续根据订阅状态和 expiryTime 判断:
function hasAccess(subscription, now) {
const expiryTimes = subscription.lineItems
.map(item => Date.parse(item.expiryTime))
.filter(Number.isFinite);
const finalExpiryTime = Math.max(...expiryTimes);
return now.getTime() < finalExpiryTime;
}
如果订阅包含多条 lineItems,不要默认第一条就是最终到期的项目。
5. 到期后再停止支持
继续处理 Google Play Real-time Developer Notifications,并定期调用 purchases.subscriptionsv2.get 校验最终状态。只有用户的最终 expiryTime 已经过期,并且不存在宽限期、账号保留期或其他有效权益时,才能关闭访问权限。
旧版 API
旧系统中常用的接口是:
POST /androidpublisher/v3/applications/{packageName}/purchases/subscriptions/{subscriptionId}/tokens/{token}:cancel
它同样用于停止自动续订,不会退款。不过,新实现应优先使用 purchases.subscriptionsv2。如果现有代码仍在使用旧接口,需要对照当前 Google Play Developer API 文档,检查接口的可用状态和迁移要求。
注意事项
- 不要调用
revoke。撤销通常会立即终止订阅权益,不符合”允许用户使用到已付费周期结束”的要求。 - 不要调用订单退款接口,除非确实需要退钱。退款和停止续订是两个独立的业务决定。
- 对已经移除的订阅,优先选择
DEVELOPER_REQUESTED_STOP_PAYMENTS。如果使用USER_REQUESTED_STOP_RENEWALS,用户在某些情况下仍可能从 Google Play 恢复订阅。 - API 返回成功,只代表取消请求已经被接受。访问权限仍应根据重新查询到的订阅状态和到期时间判断。
- 暂停、宽限期、账号保留期、延期和替换订阅都可能改变最终到期时间,因此不能只依赖本地最初保存的日期。
- 批量处理时,应保存每个 token 的请求结果,并保证操作具有幂等性。失败记录需要重试,不要因为单个请求失败而中断整个批次。
- Google Play Console 的界面和操作文案可能随时间变化。人工操作前,应仔细检查确认页面是否同时包含退款或撤销权益。
备注:内容仅供参考。
