PAYATHON 2026

使用 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 交易。

备注:内容仅供参考。