Flutter内购完成时偶现BillingResponse.developerError及购买状态无效问题排查求助
我看了你遇到的问题——内购操作时偶尔返回BillingResponse.developerError,有时候又能正常成功,而且代码是完全遵循官方示例来的。结合你提供的代码细节,这个问题不一定是内部测试阶段导致的,更可能是流程时序、异步操作或者状态管理上的小问题,下面分几个方向给你分析:
1. 异步操作的竞态冲突
你在处理购买列表时用了forEach((PurchaseDetails purchaseDetails) async { ... }),但forEach不会等待内部的异步操作完成,这会导致多个购买记录的处理逻辑并行执行。比如可能出现两个操作同时尝试处理同一个购买记录,或者在completePurchase执行前,其他操作已经修改了购买状态,最终触发无效状态的错误。
解决方案:把forEach改成for-in循环,确保每个购买记录的处理是串行执行的:
for (final purchaseDetails in purchaseDetailsList) { if (purchaseDetails.status == PurchaseStatus.pending) { print('Pending..'); } else { // 你的后续处理逻辑,async/await会按顺序执行 } }
2. 重复调用completePurchase
如果同一个PurchaseDetails对象被多次调用completePurchase,第二次调用就会返回developerError——因为谷歌Play后台已经标记该购买为已完成状态。
排查点:
- 检查
_purchases列表是否存在重复添加购买记录的情况; - 在
_getPastPurchases处理历史购买时,是否已经对已完成的购买调用过completePurchase,后续又重复触发; - 可以给每个
PurchaseDetails增加一个自定义标记(比如bool isProcessed = false),确保只执行一次completePurchase。
3. 操作顺序与非消耗品的错误处理
你当前的逻辑是先执行deliverProduct、consumePurchase,再判断是否调用completePurchase。这里有两个潜在问题:
- Android平台上,消费或者交付逻辑如果耗时较长,可能导致谷歌Play后台的购买状态先发生变更,等到你调用
completePurchase时,状态已经不再是有效的可完成状态; - 你调用的是
buyNonConsumable购买非消耗品,但代码里却对它执行了consumePurchase——非消耗型商品是不需要消费的,这个不符合规范的操作很可能会触发状态异常!
建议调整顺序:先处理completePurchase,再执行交付逻辑(非消耗品直接去掉consumePurchase的调用):
if (purchaseDetails.pendingCompletePurchase) { var completePurchase = await InAppPurchaseConnection.instance .completePurchase(purchaseDetails); } // 再执行交付和其他逻辑 if (_isPurchased) { _purchases.add(purchaseDetails); await deliverProduct(); }
4. 内部测试环境的潜在问题
虽然不是必现问题的核心原因,但内部测试阶段确实有需要注意的配置:
- 确保测试账号已经添加到谷歌Play控制台的内部测试用户列表,且测试设备登录的是该账号;
- 内购商品必须发布到内部测试轨道(不能是草稿状态);
- 测试设备的谷歌Play服务要保持最新版本,旧版本可能存在兼容性bug。
5. 插件或依赖版本问题
尝试把in_app_purchase插件更新到最新版本,旧版本可能存在和最新谷歌Billing库适配的问题,导致偶发的状态错误。
先重点排查非消耗品调用consumePurchase和异步竞态这两个点,这是最容易引发这类偶现错误的原因,然后再逐步验证其他方向。
内容的提问来源于stack exchange,提问作者vlladislav45

