使用 In-App-Billing Version 3 为何仍检测到 V2 交易?
结论
如果代码通过 IInAppBillingService 绑定 com.android.vending.billing.InAppBillingService.BIND,并在接口调用中传入 API 版本号 3,当前使用的就是 In-app Billing V3。
Google 检测到 V2 交易,不代表最新版应用中一定还包含 V2 代码。更常见的情况是,部分用户仍安装着旧版 APK,并继续通过 V2 购买。更新 Google Play 上的最新版本,无法远程删除用户设备中已经安装的旧版本。
排查时需要确认两点:
- 当前发布的所有 APK 是否只调用 V3。
- Google Play 上是否还有旧版 APK、分渠道版本或其他活跃版本在使用 V2。
如何从代码中确认使用的是 V3
V3 通常通过 IInAppBillingService 进行绑定:
Intent serviceIntent = new Intent(
"com.android.vending.billing.InAppBillingService.BIND"
);
serviceIntent.setPackage("com.android.vending");
bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
绑定成功后,V3 接口调用一般会明确传入版本号 3:
int response = billingService.isBillingSupported(
3,
getPackageName(),
"subs"
);
发起订阅购买时同样如此:
Bundle buyIntentBundle = billingService.getBuyIntent(
3,
getPackageName(),
sku,
"subs",
developerPayload
);
查询购买记录时应调用 V3 接口:
Bundle purchases = billingService.getPurchases(
3,
getPackageName(),
"subs",
null
);
这里:
"inapp"表示一次性商品。"subs"表示订阅。- 第一个参数
3才是本次请求实际使用的 Billing API 版本。
项目中存在 IInAppBillingService.aidl,或者辅助类中写有“支持 V3”的注释,都不能单独证明所有交易路径使用的都是 V3。还需要检查最终执行时传入的参数,以及项目中是否保留了另一套旧购买实现。
V2 代码通常有哪些特征
In-app Billing V2 通常依赖广播和旧式 BillingService。可以在整个项目和依赖库中搜索以下内容:
com.android.vending.billing.REQUEST_PURCHASE
com.android.vending.billing.GET_PURCHASE_INFORMATION
com.android.vending.billing.CONFIRM_NOTIFICATIONS
com.android.vending.billing.CHECK_BILLING_SUPPORTED
com.android.vending.billing.IN_APP_NOTIFY
com.android.vending.billing.PURCHASE_STATE_CHANGED
com.android.vending.billing.RESPONSE_CODE
还要检查是否存在以下旧类:
BillingService
ResponseHandler
PurchaseObserver
Consts
Security
这些类名不是官方判定依据,但常见于早期的 V2 示例代码。如果项目中同时保留了旧的 BillingService 和新的 IabHelper,部分页面、商品类型或异常回退路径仍可能调用 V2。
旧版清单还可能注册类似的组件:
<service android:name=".BillingService" />
<receiver android:name=".BillingReceiver">
<intent-filter>
<action android:name="com.android.vending.billing.IN_APP_NOTIFY" />
<action android:name="com.android.vending.billing.RESPONSE_CODE" />
<action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED" />
</intent-filter>
</receiver>
确认没有任何代码依赖这些组件后,可以删除对应的 service、receiver、旧类和相关资源。不要只删清单声明,却保留仍可能执行的 V2 调用,否则购买流程可能在运行时失败。
下面这项权限在 V2 和 V3 中都曾使用,不能用它判断应用是否还在使用 V2,也不要仅为了“移除 V2”而直接删除:
<uses-permission android:name="com.android.vending.BILLING" />
为什么仍然会检测到 V2 交易
最常见的原因是旧版本仍在运行。
即使当前商店版本已经使用 V3,仍可能有用户:
- 长期没有更新应用。
- 从其他应用市场或企业渠道安装了旧 APK。
- 使用仍处于有效状态的旧版分渠道 APK。
- 从备份中恢复了旧版本。
- 处于分阶段发布、测试轨道或历史发行配置中。
- 使用针对其他设备配置提供的 APK。
Google 的通知通常针对应用包名产生的交易活动,并不等同于最新版 APK 的静态代码扫描结果。邮件一般也无法定位到具体的 versionCode,因此不能只凭通知判断是哪一个版本发起了 V2 请求。
还要检查同一应用中是否有多套支付入口。例如,订阅页面可能已经改用 V3,但旧的一次性商品页面仍在调用 V2。
建议的排查步骤
1. 检查所有购买入口
除了订阅购买按钮,还要检查:
- 一次性商品购买。
- 订阅购买和续订相关查询。
- 恢复购买。
- 应用启动时的订单同步。
- 支付失败后的重试或回退逻辑。
- 免费试用、升级和降级入口。
- 已经下架但仍可通过代码进入的旧页面。
确认每条路径最终都调用 V3,并且 API 版本参数都是 3。
2. 检查最终发布包
检查实际上传到 Google Play 的 APK,不要只看当前开发目录。构建时可能会合入旧模块、第三方库或不同渠道的源集。
重点检查:
- 最终
AndroidManifest.xml中是否还有 V2 的service或receiver。 - DEX 中是否包含 V2 action 字符串。
- 不同 build variant 是否使用了不同的支付实现。
- Google Play 各发布轨道和设备配置中是否还有旧构建。
代码混淆可能改变类名,但通常不会移除发送请求时使用的 action 字符串。因此,搜索 V2 action 往往比只搜索类名更可靠。
3. 在运行时记录实际调用
可以在支付封装层记录以下非敏感信息:
Log.d("Billing", "version=3, type=subs, versionCode=" + BuildConfig.VERSION_CODE);
不要记录购买令牌、订单凭证、账号信息或完整的 developerPayload。这些日志只需帮助确认哪个应用版本、哪种商品类型进入了哪套 Billing 实现。
如果项目使用自定义封装,可以集中定义版本号,避免各处调用传入不同的值:
private static final int BILLING_API_VERSION = 3;
然后统一调用:
int response = billingService.isBillingSupported(
BILLING_API_VERSION,
getPackageName(),
"subs"
);
4. 处理仍在使用的旧版本
如果 V2 请求来自已经发布的旧 APK,仅删除当前项目中的 V2 代码,无法阻止旧安装继续运行。可以采取以下措施:
- 提高新版本的覆盖率,并提醒用户升级。
- 在自有服务端根据应用版本设置最低版本要求。
- 停止向旧版本返回可购买商品或支付入口。
- 对过旧版本显示强制升级页面。
这些措施要求应用拥有可控制的服务端或远程配置。实施强制升级前,还要考虑无法访问更新渠道的用户以及已购权益的恢复,避免用户无法继续使用已经付费的内容。
是否需要重新实现 V3
如果所有购买调用都通过 IInAppBillingService 执行,版本参数均为 3,而且最终发布包中不存在 V2 请求路径,就不必因为这封邮件重新实现 V3。问题更可能出在历史 APK 或遗漏的旧支付入口。
不过,In-app Billing V3 已经是历史接口。对于仍在维护的应用,不宜把继续使用 V3 当作长期方案。迁移时应遵循当前 Google Play Billing Library 的官方要求,因为支持版本和发布期限可能变化。迁移到新版库也不能代替对旧安装的处理。旧 APK 中的 V2 代码仍会存在,直到用户完成更新,或者应用通过自身的版本策略阻止旧支付流程。
最终应根据三类证据作出判断:当前源码中的实际调用、最终发布包的内容,以及各发布渠道中仍然活跃的历史版本。只检查 IInAppBillingService.aidl 或 IabHelper,无法排除 V2 交易。
备注:内容仅供参考。
