Google Play Developer API订阅延期请求报错:期望到期时间无效
嘿,我看你在给Android应用内订阅做延期计费的时候碰到了这个头疼的400错误,咱们一步步拆解问题,把它搞定!
首先先明确你遇到的错误信息:
Exception was thrown while updating listingcom.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request { "code" : 400, "errors" : [ { "domain" : "androidpublisher", "message" : "The desired expiry time for the subscription is not valid.", "reason" : "subscriptionDeferInvalidTime" } ], "message" : "The desired expiry time for the subscription is not valid." }
这个错误的核心就是你设置的延期后到期时间不符合Google Play的规则,结合你贴的代码,我发现了几个关键问题点:
问题出在哪?
先看你代码里的这段关键逻辑:
long desiredTime = expiryTime + 10*60*1000; // 原到期时间加10分钟 long expectedTime = expiryTime; info.setDesiredExpiryTimeMillis(desiredTime); info.setExpectedExpiryTimeMillis(expectedTime); request.setDeferralInfo(sdi); // 这里有个明显的笔误!
1. 变量名笔误(最可能的直接原因)
你定义的延期信息变量是info,但最后设置请求的时候写的是sdi,这会导致请求里根本没有正确的延期参数,Google自然会返回时间无效的错误!这个低级错误很容易被忽略,先把它改过来。
2. 到期时间可能不是最新的
你说expiryTime来自之前的purchases.get响应,但如果在获取这个时间和调用defer之间,订阅状态已经发生变化(比如用户自己续期了,或者有其他操作修改了到期时间),那你用的expiryTime就过时了。Google用expectedExpiryTimeMillis来做并发校验,必须和当前订阅的实际到期时间完全一致,否则就会报错。
3. 延期时长的规则限制
Google Play对订阅延期有严格的时长要求:
- 单次延期不能少于1分钟(你的10分钟是满足的,但如果是更短的时长就会触发错误)
- 延期后的总到期时间不能超过当前时间加上10年
- 延期时长不能超过当前订阅周期的99倍(比如月订阅最多延期99个月)
你的10分钟看起来没问题,但也要确认是否符合订阅计划的规则。
怎么修复?
我给你整理了修正后的代码,同时把注意点都标出来了:
完整修正后的代码
class DeferBilling extends AsyncTask<String, Void, String> { protected String doInBackground(String... urls) { try { Preconditions.checkArgument(!Strings.isNullOrEmpty(ApplicationConfig.PACKAGE_NAME), "ApplicationConfig.PACKAGE_NAME cannot be null or empty!"); final AndroidPublisher service = AndroidPublisherHelper.init( ApplicationConfig.APPLICATION_NAME, ApplicationConfig.SERVICE_ACCOUNT_EMAIL, getBaseContext()); final AndroidPublisher.Purchases purchases = service.purchases(); // 关键第一步:先获取最新的订阅状态,拿到当前真实的到期时间 SubscriptionPurchase currentPurchase = purchases.subscriptions() .get(ApplicationConfig.PACKAGE_NAME, GlobalClass.subscriptionID, GlobalClass.subscriptionToken) .execute(); long latestExpiryTime = currentPurchase.getExpiryTimeMillis(); // 第二步:计算合法的延期时间(这里是10分钟,你可以根据需求调整) long deferralMinutes = 10; long desiredTime = latestExpiryTime + deferralMinutes * 60 * 1000; // 可选:加个校验,确保不超过10年的上限 long maxAllowedExpiry = System.currentTimeMillis() + 10L * 365 * 24 * 60 * 60 * 1000; if (desiredTime > maxAllowedExpiry) { throw new IllegalArgumentException("延期后的到期时间不能超过10年"); } // 第三步:构建请求参数,注意变量名不要写错! SubscriptionPurchasesDeferRequest request = new SubscriptionPurchasesDeferRequest(); SubscriptionDeferralInfo info = new SubscriptionDeferralInfo(); info.setDesiredExpiryTimeMillis(desiredTime); info.setExpectedExpiryTimeMillis(latestExpiryTime); request.setDeferralInfo(info); // 这里之前的sdi改成info // 执行延期请求 SubscriptionPurchasesDeferResponse response = purchases.subscriptions() .defer(ApplicationConfig.PACKAGE_NAME, GlobalClass.subscriptionID, GlobalClass.subscriptionToken, request) .execute(); System.out.println(response); return response.toString(); } catch (Exception e) { System.out.println("Exception thrown" + e.toString()); return ""; } } protected void onPostExecute(String feed) { } }
额外要注意的点
- 确保你的服务账号在Google Play Console里有正确的权限:至少要分配开发者权限或者专门的订阅管理权限,否则API调用会被拒绝
- 测试的时候建议用Google Play的测试账号和测试订阅计划,别用真实用户的订阅来测试,避免出问题
- 如果还是报错,可以开启API的调试日志,看看实际发送的请求参数是什么,这样更容易定位问题
内容的提问来源于stack exchange,提问作者Kriti Sharma

