Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you check if a user has bought an in-app purchase whenever he open the app?

Note: I'm talking about a one-time purchase that removes ads from the app.

Google says you should call BillingClient.queryPurchases() in your onResume() and onCreate() but I'm not sure how to do that.

First off here's the code for the actual in-app purchase, I just got it from a youtube tutorial and I don't even know if it's complete:

private void setupBillingClient(Context c) { //connect to google play
    billingClient = BillingClient.newBuilder(c)
            .enablePendingPurchases()
            .setListener(this)
            .build();
    billingClient.startConnection(new BillingClientStateListener() {
        @Override
        public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                //The BillingClient is setup successfully
                loadAllSkus();
            }
        }

        @Override
        public void onBillingServiceDisconnected() {
            //TODO: implement retry logic to handle lost connections to Google Play by calling startConnection() again
        }
    });
}

private void loadAllSkus() {
    if (billingClient.isReady()) { //first check if BillingClient is ready
        final SkuDetailsParams params = SkuDetailsParams.newBuilder()
                .setSkusList(skuList)
                .setType(BillingClient.SkuType.INAPP)
                .build();

        billingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
            @Override
            public void onSkuDetailsResponse(@NonNull final BillingResult billingResult, @Nullable List<SkuDetails> skuDetailsList) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    assert skuDetailsList != null;
                    for (Object skuDetailsObject : skuDetailsList) {
                        final SkuDetails skuDetails = (SkuDetails) skuDetailsObject;
                        if (skuDetails.getSku().equals(sku)) { //if it found the sku in play store you can start billing flow
                            Purchase.PurchasesResult result = billingClient.queryPurchases(BillingClient.SkuType.INAPP); //use play store cache, no network
                            List<Purchase> purchases = result.getPurchasesList();
                            boolean isOwned = false;
                            if (purchases != null) //first check if he has already purchased
                                for (Purchase purchase : purchases) {
                                    String thisSKU = purchase.getSku();
                                    if (thisSKU.equals(sku)) {
                                        isOwned = true;
                                        Toast.makeText(getContext(), "You are a premium user", Toast.LENGTH_LONG).show();//TODO: Delete this toast
                                        //TODO: Remove purchase options and ads here
                                        break;
                                    }
                                }

                            if (!isOwned) {
                                buyButton.setEnabled(true);
                                buyButton.setOnClickListener(new View.OnClickListener() {
                                    @Override
                                    public void onClick(View v) {
                                        BillingFlowParams billingFlowParams = BillingFlowParams
                                                .newBuilder()
                                                .setSkuDetails(skuDetails)
                                                .build();

                                        if (getActivity() != null)
                                            billingClient.launchBillingFlow(getActivity(), billingFlowParams);
                                        dismiss();
                                    }
                                });
                            }
                        }
                        /* else if (skuDetails.getSku().equals("something else")) { //add other products

                        }*/
                    }
                }
            }
        });
    } else Toast.makeText(getContext(), R.string.billing_not_ready, Toast.LENGTH_LONG).show();
}

@Override
public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<com.android.billingclient.api.Purchase> purchases) {
    int responseCode = billingResult.getResponseCode();

    if (responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
        for (Purchase purchase : purchases) {
            handlePurchase(purchase); //acknowledge the purchase
        }
    } else if (responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
        //already owned
    } else if (responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {

    }
}

private void handlePurchase(Purchase purchase) {
    if (purchase.getSku().equals(sku) && purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
        if (!purchase.isAcknowledged()) {
            AcknowledgePurchaseParams acknowledgePurchaseParams =
                    AcknowledgePurchaseParams.newBuilder()
                            .setPurchaseToken(purchase.getPurchaseToken())
                            .build();
            billingClient.acknowledgePurchase(acknowledgePurchaseParams, new AcknowledgePurchaseResponseListener() {
                @Override
                public void onAcknowledgePurchaseResponse(@NonNull BillingResult billingResult) {
                    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                        Toast.makeText(getContext(), "Purchase Acknowledged", Toast.LENGTH_LONG).show();//TODO: Delete this maybe?
                    }
                }
            });
        }
        Toast.makeText(getContext(), R.string.purchase_done, Toast.LENGTH_LONG).show();
    }
}

So apparently to call BillingClient.queryPurchases() I have to rewrite all of the above code in each and every activity?

like image 586
User104163 Avatar asked Oct 15 '25 14:10

User104163


1 Answers

The method BillingClient.queryPurchases() as you know returns a list of all the currently owned purchases items.

Replicating the same code again and again is always an incorrect approach / design. What you need to do is to encapsulate your code and make it as independent as possible from any Activity or any other component, so it can be reused.

For a better approach check the official billing samples from Google. You will see that all the billing logic is encapsulated so it can be easily reused.

Java: https://github.com/android/play-billing-samples/tree/main/ClassyTaxiJava

Kotlin: https://github.com/android/play-billing-samples/tree/main/ClassyTaxiAppKotlin

Some clarifications, if you find that the samples are about subscriptions and you need non-consumables, or vice versa. The billing library is the same for both, the difference is only the data to be queried, for example for subscriptions use BillingClient.SkuType.SUBS and for non-consumables BillingClient.SkuType.INAPP. But the rest, how to connect with billing, how to acknowledge, initiate the purchase flow, etc., is the exactly same for both. There are no separate APIs or different way to implement it, you use the same API and the same implementation just changing what is being queried.

like image 91
PerracoLabs Avatar answered Oct 17 '25 02:10

PerracoLabs



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!