Google Play Billing Library - querySkuDetailsAsync occasionally returns BillingResponseCode.ERROR (6)
Asked Answered
W

1

17

I am using Google Play Billing Library 2.0.3 (implementation 'com.android.billingclient:billing:2.0.3') and it works fine for the most part, but for some users for the reasons unknown, querySkuDetailsAsync() method occasionally fails with BillingResponseCode.ERROR (6) error code...

Does anyone know anything about it? The reason of this error is not explained anywhere, other than it is a "Fatal error during the API action". Can anyone maybe tell why it might be happening?

edit: I am using the method as it is in the documentation (https://developer.android.com/google/play/billing/billing_library_overview):

public interface QuerySkuDetailsListener {
        void onSuccess(List<SkuDetails> skuDetailsList);

        void onErrorProductsHaveDifferentTypes();

        void onBillingClientError(int error_code);
    }

    public void querySkuDetails(final QuerySkuDetailsListener querySkuDetailsListener, final Product... products) {
        if (products.length < 1) {
            //ERROR_NO_PRODUCTS_TO_QUERY
            throw new NullPointerException();
        }

        String querySkuType = products[0].getSkuType();

        SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
        List<String> skuList = new ArrayList<>();
        for (Product product : products) {
            skuList.add(product.getSku());
            if (!product.getSkuType().equals(querySkuType)) {
                //ERROR_SKU_TYPE_CANT_BE_DIFFERENT
                querySkuDetailsListener.onErrorProductsHaveDifferentTypes();
                return;
            }
        }
        params.setSkusList(skuList).setType(querySkuType);

        billingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() {
            @Override
            public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
                    querySkuDetailsListener.onSuccess(skuDetailsList);
                }
                else {
                    //ERROR QUERYING SKU DETAILS
                    querySkuDetailsListener.onBillingClientError(billingResult.getResponseCode());
                }
            }
        });
    }

Then I use it like this:

        private void initiatePurchaseFlow(final Activity activity, @Nullable final Product oldProduct, final Product product, final JsonObject metadata, final InitiatePurchaseListener initiatePurchaseListener) {

                //Check if billingClient is ready
                if (billingClient.isReady()) {
                    querySkuDetails(new QuerySkuDetailsListener() {
                            @Override
                            public void onSuccess(List<SkuDetails> skuDetailsList) {
                                // Process the result.
                                BillingFlowParams.Builder flowParamsBuilder = BillingFlowParams.newBuilder();
                                flowParamsBuilder.setSkuDetails(skuDetailsList.get(0));
                                if (null != oldProduct) {
                                    flowParamsBuilder.setOldSku(oldProduct.getSku());
                                    flowParamsBuilder.setReplaceSkusProrationMode(BillingFlowParams.ProrationMode.IMMEDIATE_WITH_TIME_PRORATION);
                                }
                                BillingFlowParams flowParams = flowParamsBuilder.build();
                                int responseCode = billingClient.launchBillingFlow(activity, flowParams).getResponseCode();
                                if (responseCode == BillingClient.BillingResponseCode.OK) {
                                    //SUCCESS, OK
                                    ...Rest of the irrelevant code...
                                }
                                else {
                                    //ERROR_IN_LAUNCHING_BILLING_FLOW
                                    dispenseInitiatePurchaseFlowError(activity, initiatePurchaseListener, InitiatePurchaseErrorCodes.ERROR_IN_LAUNCHING_BILLING_FLOW, getBillingClientErrorDescription(responseCode));
                                }
                            }

                            @Override
                            public void onErrorProductsHaveDifferentTypes() {
                                //ERROR_IN_QUERYING_SKU_DETAILS_PRODUCTS_HAVE_DIFFERENT_TYPES
                                dispenseInitiatePurchaseFlowError(activity, initiatePurchaseListener, InitiatePurchaseErrorCodes.ERROR_IN_QUERYING_SKU_DETAILS_PRODUCTS_HAVE_DIFFERENT_TYPES, getBillingClientErrorDescription(0));
                            }

                            @Override
                            public void onBillingClientError(int error_code) {
                                //ERROR_IN_QUERYING_SKU_DETAILS
                                dispenseInitiatePurchaseFlowError(activity, initiatePurchaseListener, InitiatePurchaseErrorCodes.ERROR_IN_QUERYING_SKU_DETAILS, getBillingClientErrorDescription(error_code));
                            }
                        }, product);
                }
                else {
                    //ERROR_BILLING_CLIENT_IS_NOT_READY
                    //Trying to reconnect once          
                    billingClient.startConnection(new BillingClientStateListener() {
                        @Override
                        public void onBillingSetupFinished(BillingResult billingResult) {
                            if (billingResult.getResponseCode() == BillingResponse.OK) {
                                //launching again
                                initiatePurchaseFlow(activity, oldProduct, product, metadata, initiatePurchaseListener);
                            }
                            else {
                            // showing an error dialog
                            dispenseInitiatePurchaseFlowError(activity, initiatePurchaseListener, InitiatePurchaseErrorCodes.ERROR_BILLING_CLIENT_IS_NOT_READY, getBillingClientErrorDescription(billingResult.getResponseCode()));
                            }
                        }
                        @Override
                        public void onBillingServiceDisconnected() {
                            // showing an error dialog
                        }
                    });
                }
            }

edit: Could the users simply cheat? e.g. use a cracked google play store/other software that is messing with the purchases? Because I am not getting this error on any of the devices that I have, no matter what I do.

edit: So far I found out that onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> purchases) method of the billing client returns this error code (BillingResponseCode.ERROR) if you use lucky patcher to crack the application, but I wasn't getting any errors in the querySkuDetailsAsync()... Could this be related?

edit: Also I've created an issue in the google issuetracker, come support me there if you'd like. https://issuetracker.google.com/issues/139631105

Worser answered 19/8, 2019 at 4:41 Comment(12)
Where is your code?Coonhound
Also experiencing this issue. Seems to me that might be a newly introduced bug. For the time being, my "very dirty" workaround is that if received a 6 code, then perform a quick small retrial loop with querySkyDetailsAsync. So far seems to work, I still can see in the logs that sometimes the code 6 is received, but after a couple of retries it gets the right code. Lets hope the Android team solves it soon.Unfold
@PerracoLabs Thanks for the solution, that's an idea! xD I'll try that :P Also I've created an issue in the google issuetracker, come support me there if you'd like. issuetracker.google.com/issues/139631105Worser
By any chance, do the users have a different language / country that your application has sales on? To us, if we restrict the countries that we sell products on, the users from there cannot query the products. Occasionally it returns with an empty list while the response code is success, but sometimes it returns API error too. Maybe this is your issue, but since it's not a definitive solution I'm posting as a comment.Fancier
@Furkan Yurdakul yes, we do have users from different countries, but we don't have restrictions on the countries that the error is coming from...Worser
I see. Then, the best scenario would be retrying the connection at "onBillingServiceDisconnected" method 2 or 3 times. It will mostly reconnect if the issue is random.Fancier
@Furkan Yurdakul it's not related to the onBillingServiceDisconnected(the client is probably not disconnected)... At least not as far as it seems to be... It simply fails in querySkuDetailsAsync when the service is connected. I would certainly try the PerracoLabs' "dirty" solution, as it seems to be the most reasonable one.Worser
Google acknolege the bug and fixed in version 2.0.3. Check developer.android.com/google/play/billing/…Educator
@Educator you are talking about a different bugWorser
yap. Same behavior but different error. SorryEducator
Have you checked the debug message? developer.android.com/reference/com/android/billingclient/api/…Beeswax
For me with version 2.0.3 callback is not firedHippodrome
I
4

For future reference and some other people suffering from this (like me at IAB 3.0.1), someone at the issue tracker bug that OP opened posted a comment about how we should react if we receive this code

Error 6 (https://developer.android.com/reference/com/android/billingclient/api/BillingClient.BillingResponseCode#error) usually represents a transient error from our backend servers. This error code should trigger a retry which in most cases should succeed.

LINK: https://issuetracker.google.com/issues/139631105#comment11

example code:

@Override
public void onSkuDetailsResponse(@NonNull BillingResult billingResult, @Nullable List<SkuDetails> list) {
    Utils.log("BILLING: onSkuDetailsResponse(). Result (" + billingResult.getResponseCode() + ") List size: " + (list == null ? "null" : list.size()));
    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && list != null) {
        if (mSkuQueryErrorCounter > 0) mSkuQueryErrorCounter--;
        // process the SkuDetail list...
        }
    } else {
        // https://issuetracker.google.com/issues/139631105#comment11
        mSkuQueryErrorCounter++;
        if (mSkuQueryErrorCounter <= 2) scheduleUpdate(NOW);
        else if (mSkuQueryErrorCounter < 5) scheduleUpdate(TEN_SECONDS);
        else if (mSkuQueryErrorCounter < 10) scheduleUpdate(ONE_HOUR);
        else scheduleUpdate(ONE_DAY);
    }
}
Isolative answered 1/11, 2020 at 11:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.