Android - 生产中的 Google Play 结算库错误

Ale*_*tru 6 android in-app-purchase in-app-billing firebase

我在 Google Play 商店发布了一个 Android 应用程序,上周我发布了一个新的更新,只是为了解决一些小问题。从我在 Play 商店添加更新版本的那天开始,我可以在 Firebase Crashlytics 上看到有人尝试购买应用功能时出现问题。

在我发布生产中的更新版本之前,我在 Alpha 测试中添加了该应用程序,以便我可以确保 InAppPurchase 工作,并且确实如此。

当其他人尝试购买应用程序功能时,我可以看到抛出了此致命异常:

Fatal Exception: java.lang.IllegalArgumentException: SKU cannot be null.
   at com.android.billingclient.api.BillingFlowParams$Builder.build(com.android.billingclient:billing@@3.0.0:23)
Run Code Online (Sandbox Code Playgroud)

SKU 在我的“托管产品”列表中仍然有效。

这是我用来初始化计费客户端的代码(在一个片段中):

        billingClient = BillingClient.newBuilder(getActivity())
            .enablePendingPurchases()
            .setListener(purchasesUpdatedListener)
            .build();
Run Code Online (Sandbox Code Playgroud)

这是我用来启动连接的代码:

billingClient.startConnection(new BillingClientStateListener() {
        @Override
        public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
            Log.d(TAG, "Connection finished");
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                // The BillingClient is ready. You can query purchases here.
                List<String> skuList = new ArrayList<>();
                skuList.add("unlock_keyboard");
                SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
                params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
                billingClient.querySkuDetailsAsync(params.build(),
                        new SkuDetailsResponseListener() {
                            @Override
                            public void onSkuDetailsResponse(@NonNull BillingResult billingResult,
                                                             List<SkuDetails> skuDetailsList) {
                                // Process the result.
                                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
                                    for (Object skuDetailsObject : skuDetailsList) {
                                        skuDetails = (SkuDetails) skuDetailsObject;
                                        sku = skuDetails.getSku();


                                    }
                                    Log.d(TAG, "i got response");
                                    Log.d(TAG, String.valueOf(billingResult.getResponseCode()));
                                    Log.d(TAG, billingResult.getDebugMessage());
                                }
                            }
                        });
            }
        }
Run Code Online (Sandbox Code Playgroud)

这是我用来处理购买的代码:

PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
        @Override
        public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> list) {
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && list != null) {
                for (Purchase purchase : list) {
                    handlePurchase(purchase);
                    Log.d(TAG, "Purchase completed" + billingResult.getResponseCode());
                }
            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
                Log.d(TAG, "User Canceled" + billingResult.getResponseCode());
            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
                if ("unlock_keyboard".equals(sku)) {
                    KeyboardAlreadyPurchasedConfirmation();
                }
                Log.d(TAG, "Item Already owned" + billingResult.getResponseCode());
            }
        }
    };
Run Code Online (Sandbox Code Playgroud)

为了启动计费流程,用户必须单击对话框中的按钮。这是代码:

        builder.setPositiveButton(
            getString(R.string.purchase_keyboard),
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {

                    sku = "unlock_keyboard";
                    BillingFlowParams flowParams = BillingFlowParams.newBuilder()
                            .setSkuDetails(skuDetails)
                            .build();
                    billingClient.launchBillingFlow(Objects.requireNonNull(getActivity()), flowParams);

                }

            });
Run Code Online (Sandbox Code Playgroud)

在我的应用程序的以前版本中,这种情况从未发生过,它只是在新更新后才开始。我只需要知道是什么导致了这个问题,是我的代码有问题还是只是 Google Play 服务的问题?我必须指出这是在不同 Android 版本的不同设备上发生的。

非常感谢。

Ale*_*tru 1

我还没有解决这个问题,但我找到了一种方法来减少此库升级产生的错误数量。

我所做的是将 Google Billing Library 从版本3.0.1降级到版本2.1.0,尽管我在 Firebase 中仍然遇到一些错误(SKU 为 null),但大多数用户现在可以购买这些产品。

另外,我实现了一个方法,每当首次打开 Activity 时无法启动 Google Billing 库连接时就会调用该方法,因此更准确地说,这是重新启动计费连接方法。

如果您遇到同样的问题,我建议您至少现在尝试相同的操作,因为 Google Billing 库似乎仍然存在一些需要修复的问题。

1. 在 build.gradle(app) 中添加以下行:

implementation 'com.android.billingclient:billing:2.1.0'
Run Code Online (Sandbox Code Playgroud)

2 . 在 AndroidManifest.xml 文件中添加 BILLING 权限,因为该库的旧版本仍然需要它:

<uses-permission android:name="com.android.vending.BILLING" />
Run Code Online (Sandbox Code Playgroud)

3 . 创建重启计费连接方法:

public void restartBillingConnection() {
    billingClient = BillingClient.newBuilder(Objects.requireNonNull(getActivity())).enablePendingPurchases().setListener(ChooseOptionsFragment.this).build();

    billingClient.startConnection(new BillingClientStateListener() {@Override
    public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
        Log.d(TAG, "Connection finished");
        if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
            List < String > skuList = new ArrayList < >();
            skuList.add(ITEM_SKU_AD_REMOVAL);
            SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
            params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
            billingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() {@Override
            public void onSkuDetailsResponse(@NonNull BillingResult billingResult, List < SkuDetails > skuDetailsList) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
                    for (Object skuDetailsObject: skuDetailsList) {
                        skuDetails = (SkuDetails) skuDetailsObject;
                        sku = skuDetails.getSku();
                        String price = skuDetails.getPrice();
                        if (ITEM_SKU_AD_REMOVAL.equals(sku)) {
                            skuPrice = price;
                            BillingFlowParams flowParams = BillingFlowParams.newBuilder().setSkuDetails(skuDetails).build();
                            billingClient.launchBillingFlow(Objects.requireNonNull(Objects.requireNonNull(getActivity())), flowParams);
                        }
                        else {
                            Log.d(TAG, "Sku is null");
                        }

                    }
                    Log.d(TAG, "i got response");
                    Log.d(TAG, String.valueOf(billingResult.getResponseCode()));
                    Log.d(TAG, billingResult.getDebugMessage());
                }
                else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ERROR) {
                    Toast.makeText(getActivity(), "Error in completing the purchase!", Toast.LENGTH_SHORT).show();
                }
            }
            });
        }

        else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.SERVICE_TIMEOUT) {
            Toast.makeText(getActivity(), "Service timeout!", Toast.LENGTH_SHORT).show();
        }
        else {
            Toast.makeText(getActivity(), "Failed to connect to the billing client!", Toast.LENGTH_SHORT).show();
        }

    }@Override
    public void onBillingServiceDisconnected() {
        restartBillingConnection();
    }
    });
}
Run Code Online (Sandbox Code Playgroud)

4. 确保在 Google Billing 服务断开连接时调用此方法:

@Override
    public void onBillingServiceDisconnected() {
        restartBillingConnection();
    }
Run Code Online (Sandbox Code Playgroud)

希望这个解决方案能够帮助您解决目前的问题。如果您有其他方法可以完全修复它,请在这篇文章中留下答案。