1

I have implemented Google In-app-purchases v6+ library. The user is able to purchase non-consumable virtual item.

When the user re-installs the app or installs the app on the another device with the same GMAIL the app must recover purchased item. But it does not happen. The billingClient.queryPurchasesAsync(params: QueryPurchasesParams) returns empty list of purchased items.

How to obtain that purchased item?

The user must request Purchase that item again, observes the error You already own this item within the Google Billing BottomSheet and then re-run the app.

Only after that dancing with a fire the app retrieves purchased item within the billingClient.queryPurchasesAsync(params: QueryPurchasesParams) result.

How to obtain a list of purchases items within the queryPurchasesAsync(...) function without dancing with a fire?

The app is serverless.


Requesting the billingClient.queryPurchaseHistoryAsync(...) function before querying purchases did not help as well.

1 Answer 1

0

Google Play Billing Library 7

This is a pretty old question, but I haven't found an answer anywhere. Anyway, I think I found the solution myself.

The trick is to call billingClient.queryPurchaseHistoryAsync(...) even though it is deprecated, and then call acknowledgePurchase(...) with the purchaseToken from history.

If the purchase is refunded/canceled it will return ITEM_NOT_OWNED. If it is valid it will return OK and acknowledge. From then on, it will appear in queryPurchasesAsync as a regular purchase on that device.

    private void checkPurchase() {
        billingClient.queryPurchasesAsync(
                QueryPurchasesParams.newBuilder().setProductType(
                        BillingClient.ProductType.INAPP).build(),
                (billingResult, purchases) -> {
                    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                        for (Purchase purchase : purchases) {
                            String product = purchase.getProducts().get(0); // I have only one product
                            if (product != null && product.equals("myProduct") &&
                                    purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
                                // do whatever you do when the item is purchased
                                Log.d(TAG, "Found myProduct purchase: " + purchase.getOrderId());
                                return;
                            }
                        }
                        checkPurchaseHistory();
                    }
                });
    }

    private void checkPurchaseHistory() {
        billingClient.queryPurchaseHistoryAsync(
                QueryPurchaseHistoryParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build(), 
                (billingResult, purchases) -> {
                    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                        if (purchases != null && !purchases.isEmpty()) {
                            for (PurchaseHistoryRecord purchase : purchases) {
                                String product = purchase.getProducts().get(0); // I have only one product
                                if (product != null && product.equals("myProduct")) {
                                    Log.d(TAG, "Found purchase history: " + purchase.getPurchaseToken());
                                    // acknowledge will return OK if purchased on another device 
                                    // or ITEM_NOT_OWNED if purchase is refunded
                                    acknowledgePurchaseHistory(purchase.getPurchaseToken());
                                    return;
                                }
                            }
                        }
                        // do whatever you do if the item is not purchased
                    }
                });
    }

    private void acknowledgePurchaseHistory(String purchaseToken) {
        billingClient.acknowledgePurchase(AcknowledgePurchaseParams
                .newBuilder()
                .setPurchaseToken(purchaseToken)
                .build(), billingResult -> {
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_NOT_OWNED) {
                // do whatever you do if the item is not purchased
                Log.d(TAG, "Purchase acknowledge from history: ITEM_NOT_OWNED");
            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                // do whatever you do when the item is purchased
                Log.d(TAG, "Purchase acknowledged from history");
            }
        });
    }

If there is more than one product, the code can easily be adjusted.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.