Android Billing v3 Subscription 提示 Product Not Found
明确结论
这种情况通常不是 launchBillingFlow() 的代码问题,也和 receipt validation 尚未实现无关。最常见的原因是应用并非通过对应的 Google Play 测试轨道安装,或者当前安装包的签名、包名、测试账号与 Play Console 中的应用不匹配。
querySkuDetailsAsync() 能够成功执行并返回商品,只能说明商品 ID 和类型基本可以查询,不能证明当前安装的应用和正在使用的 Google 账号具备购买资格。
优先检查安装方式和签名
不要只通过 Android Studio 安装一个 versionCode 与测试轨道相同的 build。versionCode 相同,并不代表它就是 Google Play 认可的测试版本。
建议按以下步骤重新测试:
- 将测试账号加入 Closed testing 测试人员列表。
- 使用测试人员邀请链接完成 opt-in。
- 在设备上登录该测试账号。
- 卸载通过 Android Studio、ADB 或其他方式安装的应用。
- 从 Google Play 商店的测试页面重新安装应用。
- 等待订阅商品和测试轨道配置同步后,再尝试购买。
如果启用了 Play App Signing,Google Play 分发的 APK 或由 AAB 生成的安装包可能使用应用签名密钥签名,而本地 build 通常使用 debug key 或 upload key。即使两者的包名和 versionCode 完全相同,本地安装包也不等同于 Play 分发版本。直接从测试轨道安装,可以排除这类签名差异。
检查测试账号
设备上发起购买的 Google 账号必须符合测试条件。
需要确认:
- 该账号已经加入对应 Closed testing 轨道的测试人员列表。
- 该账号已经打开测试邀请链接并加入测试。
- Google Play 商店当前使用的确实是这个账号,而不只是设备中添加了这个账号。
- 如果使用 License Testing,该账号也已经加入 Play Console 的 license testers 列表。
- 测试账号所在的国家或地区可以购买该订阅。
- 账号没有受到付款资料、家庭账号、企业设备策略或年龄限制。
- 不要混淆测试账号和应用所有者账号。设备上有多个 Google 账号时,很容易选错。
排查时,可以暂时从设备中移除其他 Google 账号,清除 Google Play 商店缓存,再通过测试链接进入应用页面并安装。
Closed testing 测试人员名单和 License Testing 名单不是一回事。前者决定账号能否获取测试版本,后者会影响测试购买行为。为了减少变量,可以将测试账号同时加入两处。
检查订阅商品状态
在 Play Console 中确认订阅不仅已经创建,而且处于可购买状态。根据当时使用的订阅配置界面,至少需要检查:
- Product ID 与代码中的 SKU 完全一致,包括大小写。
- 商品类型是 subscription,而不是 in-app product。
- 订阅及其价格配置已经激活。
- 已经配置价格和可销售的国家或地区。
- 商品没有处于草稿、停用或配置不完整的状态。
- 测试账号所在地区包含在商品的销售范围内。
- 应用包名与创建该订阅商品的 Play Console 应用完全一致。
Play Console 的配置传播可能存在延迟。刚发布测试版本或刚激活商品时,不要立即根据失败结果判断代码有问题。可以等待一段时间后重新测试,但官方没有保证固定的同步时长。
检查包名、签名和版本来源
需要核对以下三项:
applicationId
签名证书
应用安装来源
applicationId 必须与 Play Console 中的 package name 完全一致。不要只看 manifest 中显示的包名,因为 Gradle 的 applicationIdSuffix、product flavor 或 build type 都可能改变最终包名,例如:
buildTypes {
debug {
applicationIdSuffix ".debug"
}
}
如果测试轨道发布的是:
com.example.app
而本地安装的是:
com.example.app.debug
那么它们就是两个不同的应用。
本地 release build、debug build 和 Play 分发 build 也可能使用不同的签名。最可靠的排查方法仍然是卸载本地版本,再从 Google Play 测试轨道重新安装。
记录完整的 BillingResult
不要只根据弹窗内容判断问题。launchBillingFlow() 会同步返回一个 BillingResult,后续结果还会通过 PurchasesUpdatedListener 返回。两处都要记录 responseCode 和 debugMessage:
BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setSkuDetails(product)
.build();
BillingResult result =
billingClient.launchBillingFlow(activity, billingFlowParams);
Log.d("Billing", "launch responseCode=" + result.getResponseCode()
+ ", debugMessage=" + result.getDebugMessage());
购买回调也要完整记录:
@Override
public void onPurchasesUpdated(
@NonNull BillingResult billingResult,
@Nullable List<Purchase> purchases) {
Log.d("Billing", "purchase responseCode="
+ billingResult.getResponseCode()
+ ", debugMessage="
+ billingResult.getDebugMessage());
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& purchases != null) {
for (Purchase purchase : purchases) {
// 后续进行确认、发放权益和服务端校验
}
}
}
如果同步调用返回的结果不是 OK,说明购买流程没有正常启动。如果同步结果是 OK,但 Play 弹窗随后提示商品不存在,则应重点检查 Play 账号、安装来源、签名和商品的可用范围。
确认传入的是查询返回的 SkuDetails
传给 setSkuDetails(product) 的对象应直接来自本次 querySkuDetailsAsync() 返回的列表。不要自行构造,也不要使用其他商品类型的对象或复用旧连接中缓存的对象:
billingClient.querySkuDetailsAsync(
params,
(billingResult, skuDetailsList) -> {
if (billingResult.getResponseCode()
!= BillingClient.BillingResponseCode.OK) {
Log.e("Billing", billingResult.getDebugMessage());
return;
}
if (skuDetailsList == null || skuDetailsList.isEmpty()) {
Log.e("Billing", "No subscription SKU returned");
return;
}
SkuDetails selectedProduct = skuDetailsList.get(0);
BillingFlowParams flowParams =
BillingFlowParams.newBuilder()
.setSkuDetails(selectedProduct)
.build();
BillingResult launchResult =
billingClient.launchBillingFlow(activity, flowParams);
Log.d("Billing", launchResult.getResponseCode()
+ ": " + launchResult.getDebugMessage());
});
还要确认查询使用的是:
BillingClient.SkuType.SUBS
而不是:
BillingClient.SkuType.INAPP
与此错误关系不大的配置
以下事项对完整的订阅系统很重要,但通常不会直接导致购买弹窗出现 “The item you were attempting to purchase could not be found”:
- 服务端 receipt/token validation 尚未实现。
- Google Play Developer API 尚未在客户端调用。
- Real-time Developer Notifications 尚未配置。
- 后端尚未同步订阅状态。
购买令牌校验发生在购买创建之后。当前错误出现在商品购买流程的启动阶段,因此应先排查 Play 分发、账号资格和商品配置。
Google Play Developer API 的服务账号配置主要供后端查询和管理购买状态使用,不是客户端通过 launchBillingFlow() 找到商品的必要条件。
建议的最短排查路径
按以下顺序操作,通常能较快定位问题:
- 确认订阅商品已经激活,并向测试账号所在地区开放。
- 确认 SKU、
applicationId和商品类型完全正确。 - 将测试账号同时加入 Closed testing 和 License Testing。
- 使用该账号打开 opt-in 链接并加入测试。
- 卸载所有本地安装的版本。
- 从 Google Play 商店的测试页面重新安装应用。
- 确认 Play 商店当前使用的就是测试账号。
- 完整记录
launchBillingFlow()和onPurchasesUpdated()返回的BillingResult。 - 如果仍然失败,再检查 Play App Signing、付款资料、国家或地区限制,以及商品配置的同步状态。
Billing Library 3.0.1 已经是旧版接口。新项目或仍在维护的项目应计划迁移到当前受支持的 Google Play Billing Library。迁移会涉及 ProductDetails、offer token 等新接口,但仅升级依赖无法解决测试账号、签名或安装来源不匹配的问题。
备注:内容仅供参考。
