学习笔记 · · 约 10 分钟

RevenueCat + Google Play 显示「商店暂时连不上」的排查实录

kxnotes · 共 5,117 字
google play iap store unavailable featured

发了正式版,内购却一直「商店暂时连不上」——一个国行账号埋的雷

折腾了大半天,最后发现根本不是代码的锅,也不是 RevenueCat 配错,而是我测试用的那台手机上登录的是中国区 Google 账号——而中国区账号在 Google Play 里压根不能买东西。

如果你也是国行开发者在接 Google Play 内购,急着用的话记住一句话就行:别在登录了中国区账号的环境里测内购。 剩下的,是我怎么一步步绕到这个结论的全过程,写出来既给自己留个档,也给后面踩同样坑的人省点时间。

起因

我的 App 是 Flutter 写的,内购走 RevenueCat。正式版已经上了 Google Play,后台显示「有效」、覆盖 176 个国家地区,真机也是规规矩矩从商店里装下来的。结果一进商店页面,永远是那行「商店暂时连不上」,点「重试」跟没点一样,纹丝不动。

「重试没反应」这个表现一开始把我带歪了,总觉得是不是按钮事件没绑好。后来才想明白:重试只是又去拉了一遍商品目录,还是失败、状态没变,所以界面看着像「没反应」。这是第一个要破的执念——别盯着 UI 猜,去看底层到底返回了什么。

排查的思路

内购这条调用链特别长:

你的 Dart 代码
  → RevenueCat SDK
    → Google Play Billing Library
      → 设备上的 Google Play(Finsky)
        → Google Play 后端(商品 / 商户 / 账号资格)

我给自己定的规矩是:从最里头(自己能改的代码)往最外头(Google 后端)一层层往外排,每排掉一层都得有证据,绝不靠「应该是这个吧」。内购排查最浪费时间的就是猜一个改一个,改完没用再猜下一个。

先把自己能改的全排掉

代码和 key。 release 包是用 --dart-define 把 RevenueCat 的 goog_ 公钥注进去的,而且我在 build.gradle.kts 里加了硬校验:任何 release 构建只要缺这个 key,直接 require 失败、构建都过不了。所以正式版肯定带着真 key,走的是真实现,不可能是降级空实现。这一层连查都不用查,当初那个硬校验白写了?不,正是它让我现在能一句话跳过这一层。

混淆。 proguard-rules.procom.revenuecat.purchases.**com.android.billingclient.** 都 keep 着,R8 没把它们裁了。

RevenueCat 后台。 六个商品(remove_adsstarter_pack、三档钻石、all_songs_pass)状态全是绿的 Published——这说明 RevenueCat 自己能从 Google Play 把商品拉到,商品 ID 对不上的可能性也排除了。Offering 设了默认,packages 也挂了,entitlement 该挂的挂了。

到这儿,自己这边能动的全是干净的。那问题只能在外面。

真机 logcat 给了第一条硬线索

App 里再怎么 print 也是雾里看花,得去设备上看原生 Billing 到底说了啥:

adb logcat -c            # 先清空
# 手机上:进商店页、点重试
adb logcat -d | grep -iE "Purchases|BillingClient|offering|Response code"

捞出来几行关键的:

W/BillingClient: getSkuDetails() failed for queryProductDetailsAsync. Response code: 6
E/[Purchases] - ERROR: Error when fetching products - DebugMessage: Billing Unavailable. ErrorCode: ERROR.
E/[Purchases] - ERROR: Error fetching offerings - PurchasesError(code=StoreProblemError, ... Billing Unavailable ...)
I/flutter: [rc] init failed: ... StoreProblemError ... Billing Unavailable

这里有两点很要命:

报错是从 W/BillingClient 这层冒出来的,比 RevenueCat 还早。也就是说,是 Google Play 自己拒绝返回商品详情,RevenueCat 只是老老实实把这个错误透传上来。看到这行我基本就踏实了——代码和 RevenueCat 配置可以彻底洗清嫌疑,问题在 Google Play 那边。

另一点是错误码 6(通用 ERROR),附带一句「Billing Unavailable」。范围一下从「整条链」缩到了「Google Play 端」。

两条看着很对、其实不是的弯路

锁定到 Google Play 之后,我第一反应是这两个老生常谈的原因,结果都不是,但值得记一笔:

一个是传播延迟。新 App 首发后,内购商品要在 Google 后端「传播」一阵才对真机生效,几小时到一两天都正常。我这 App 当天才 Live、商品头天才建,时间窗确实没到。听上去特别合理——但它不是这次的根因。

另一个是付款资料没验证。Play Console 的付款资料里挂着两个银行账号,都是红❗「请验证电汇」。我一度以为是它卡住了内购。后来才搞清楚:银行验证管的是「你怎么把钱提出来」,不影响「你能不能卖」——你完全可以先开卖、收入先攒着,回头再验证银行去提现。所以它不是商店加载不出来的原因(不过该补还是得补)。

这俩都长着一副「标准答案」的脸,但我没停在「八成是它」上,而是让日志继续往下说——结果还真不是它俩。

升级 Play 之后,错误「进化」了

我顺手把设备上的 Google Play 商店更到了最新版,再测,错误码居然变了:

W/BillingClient: getSkuDetails() failed for queryProductDetailsAsync. Response code: 3
E/[Purchases] - ERROR: ... ErrorCode: BILLING_UNAVAILABLE.
E/[Purchases] - ERROR: PurchasesError(code=PurchaseNotAllowedError,
                       'The device or user is not allowed to make the purchase.')

63,这其实是好事。code 6 那层是 Play 商店组件自己的通用故障,升级之后没了。现在的 code 3 是 BILLING_UNAVAILABLE,RevenueCat 把它翻译成 PurchaseNotAllowedError——「这个设备或用户没有购买资格」。

Google 对 BILLING_UNAVAILABLE 的官方解释里有一条我盯了很久:用户所在国家/地区不支持 Google Play 购买。 国行设备,这条嫌疑瞬间拉满。

而且注意,这个错误是在 getOfferings(拉商品目录)这一步就抛了,连商品列表都加载不出来——所以不是「能看不能买」,是得先让账号「有资格」,商店页才有戏。

一行 Finsky 日志一锤定音

继续在 logcat 里翻,看到一行 Finsky 打的日志,整件事就清楚了:

I/Finsky: Billing preferred account via installer for com.kx.melody2048: [zUDHnU64...]

via installer——Google Play 把「计费账号」绑给了当初安装这个 App 的那个账号

而我这台手机上,这份 App 是用中国区账号通过测试轨道装上去的。于是这条链就接上了:装它的是中国区账号 → 计费账号就是中国区账号 → 中国区账号在 Google Play 不能购买 → 拉商品直接 BILLING_UNAVAILABLE → 商店页一片空白。

还有个旁证:我想换美区账号去下载,Play 直接拦我,说「此应用已与另一个账号(中国区)建立关联,要加入 beta 计划请让开发者邀请中国区账号」。等于明牌告诉我,这 App 在这台设备上就是绑在中国区账号上的。

卡了我半天的一个念头

排查中间我一直有个反问:「我都发正式版了,凭什么还不行?」

后来想通了,这是国行开发者特别容易钻进去的牛角尖。得把两件事掰开:发正式版,解决的是「商品和应用对所有支持地区的真实用户可见、可买」;它管不着、也根本不可能管「中国区账号能不能用 Google Play 付款」——那是 Google 的地区限制,跟你发不发正式版、发哪条轨道一点关系都没有。中国区账号,正式版也好测试版也好,一样不能买。

反过来想这其实是好消息:我的正式版大概率是好的,是测试机的账号环境(中国区)把它挡在外面了。一个真·台湾或美国用户,拿对应地区账号、绑了付款方式,从正式版页面下下来,是能正常买的。只是我这个开发者用中国区账号死活复现不出来而已。

怎么解的

说穿了就一句:让支持地区的账号当上「安装 + 计费」的那个账号,把中国区账号从测试环境里请出去。具体几步:

  1. 换一个支持地区的账号(台湾 / 香港 / 美国 / 新加坡都行)来测,中国区账号别掺和。
  2. Play Console → 设置 → 许可测试,把这个账号加进去——这样走的是测试购买,不会真扣钱。
  3. 测试轨道的测试人员里也把它加上,免得被「没被邀请」拦住。
  4. 设备上先卸载 App,把中国区账号切走或干脆移除,再用支持地区账号重装(正式版已经 Live,直接从商店装就行)——这样计费账号才会换过来。
  5. 顺手把付款资料里那俩银行账号的电汇验证走完,红❗清掉(这步管提现,别留着)。

换成台湾区账号重装之后,商店页立马就出来了,六个商品规规矩矩排好,价格还是本地化的新台币(NT$)。收工。

经验

下次再碰到「商店连不上 / 内购拉不到商品」,我大概会按这个顺序看,每步都拿证据,不猜:

看什么怎么看正常长啥样
走的是真实现不是降级构建注入的 key / 启动日志logcat 有 configure with key
R8 没裁掉 Billingproguard-rules.pro有对应 -keep
确实是从 Play 装的adb shell pm list packages -i <包名>installer=com.android.vending
RevenueCat 商品和 Offering后台商品 Published、Offering 设默认且挂了 packages
设备 Play / GMS 版本adb shell dumpsys package com.android.vending版本别太老
原生 Billing 返回码adb logcat | grep BillingClientResponse code: 0
计费账号是哪个地区logcat preferred account via installer + Play 账号国家支持地区,别是中国
许可测试加了没Play Console → 设置 → 许可测试测试账号在列表里

最顶用的就一条命令,复现的时候挂着:

adb logcat -c && \
# 复现:开商店页、点重试 \
adb logcat -d | grep -iE "\[rc\]|Purchases|BillingClient|preferred account|Response code"

看到不同的东西,意思差很多:

  • Response code: 0packages 大于 0 —— 正常。
  • Response code: 3 / BILLING_UNAVAILABLE / PurchaseNotAllowed —— 账号没购买资格,去查 installer 绑的账号是不是中国区。
  • Response code: 6 / ERROR / Billing Unavailable —— 设备上 Play 商店组件抽风,先把 Play 商店升级了再说。
  • offerings 那行 all=[]、一个 offering 都没有 —— key 多半指到了错的或旧的 RevenueCat 项目。

回头看

这次能比较快收口,靠的是几件事。一是从代码往外一层层排,不跳层,先把自己能改的全洗干净,剩下的必然在外部。二是关键证据全来自 logcat 而不是 App 界面——W/BillingClient 那行定位了「锅在 Google Play」,preferred account via installer 那行定位了「是 installer 账号的地区」,两行就够了。三是对那些「很像但没被证实」的假设留个心眼,传播延迟和付款资料都演得很像,但都不是。

要说最值钱的一条,其实不是技术,是那句想通了的话:已经发了正式版,不等于中国区账号就能测。 这个弯绕过来,整件事就通了。

所以最后还是那句话:国行开发者测 Google Play 内购,专门备一个支持地区的 Google 账号,加进许可测试,单独拿来测——能帮你省下我这大半天。

kxnotes

发布了 77 篇文章

分享 Twitter

相关文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注