Encountering Issues with Handling Purchases in Google Play Billing Library

116 Views Asked by At

I have been working on integrating Google Play Billing for the past two days. Yesterday, I successfully launched the billing flow; however, to my surprise, I encountered an issue where the testing card section was not visible. Instead, I was presented with actual payment options. In a rush, I proceeded to make a purchase, hoping that this might be a new testing method and the money would not be deducted. Unfortunately, the payment went through.

Note :- I had uploaded the app bundle to Internal Testing, and the app was already published in production long before I added Google Play Billing.

However, I made a mistake by not implementing shared preferences to handle the successful purchase, which was intended to remove ads from the app. Therefore, the ads persisted in the app, and I couldn't observe the changes resulting from the successful purchase.

After realizing the oversight, I added the necessary shared preferences.

But upon testing with another ID, I still encountered the issue of real payment options appearing instead of the testing environment. So i reducing the price of the product and made another purchase to validate the shared preference implementation, But i am still unable to remove the ads.

It seems that the shared preference was never being called in the handlePurchase() and restorePurchase() methods.

This is the code i have implemented , please take the code kindly , im new to this and dont want to get rage on few of my intial questions

HelpActivity.java

`public class HelpActivity extends AppCompatActivity {
    private BillingClient billingClient;
    private final PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
        @Override
        public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                if (purchases.size() > 0) {
                    for (Purchase purchase : purchases) {
                        handlePurchase(purchase);
                    }
                } else {
                    Toast.makeText(HelpActivity.this, "Unable to handle purchase", Toast.LENGTH_SHORT).show();
                }

            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
                // Handle an error caused by a user cancelling the purchase flow.
                Toast.makeText(HelpActivity.this, "Purchase Canceled", Toast.LENGTH_SHORT).show();
                //Note!!!! only this toast message is being shown in this entre process 
            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED ) {
                if (purchases.size() > 0){
                    for (Purchase purchase : purchases) {
                        handlePurchase(purchase);
                    }
                    Toast.makeText(HelpActivity.this, "You already own the item", Toast.LENGTH_SHORT).show();
                }else {
                    Toast.makeText(HelpActivity.this, "Unable to handle purchase(Item Owned)", Toast.LENGTH_SHORT).show();
                }

            }
        }
    };
    private MaterialButton removeAdsBtn;
    private MaterialButton restorePurchaseBtn;
    private ProductDetails productDetails;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_help_avtivity);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); // Set vertical orientation
        Window window = this.getWindow();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            window.setStatusBarColor(this.getResources().getColor(R.color.black));
        }
        restorePurchaseBtn = findViewById(R.id.restoreAdsBtn);
        removeAdsBtn = findViewById(R.id.removeAdsBtn);
        // Initialize BillingClient

        setupBillingClient();
        restorePurchaseBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                restorePurchases();
            }
        });
        removeAdsBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Call the method to initiate the billing flow
                initiateBillingFlow();
            }
        });
    }


    private void handlePurchase(Purchase purchase) {

        if (!purchase.isAcknowledged()) {
            billingClient.acknowledgePurchase(AcknowledgePurchaseParams
                    .newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build(), billingResult -> {
                if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
                    // Grant the user access to remove ads
                    // Save the purchase details to SharedPreferences to remember the purchase
                    Toast.makeText(HelpActivity.this, "Thank you for the purchase", Toast.LENGTH_SHORT).show();
                    Toast.makeText(HelpActivity.this, "Restarting the app, Please wait ...", Toast.LENGTH_SHORT).show();
                    SharedPreferences preferences = getSharedPreferences("adremoveSP", MODE_PRIVATE);
                    SharedPreferences.Editor editor = preferences.edit();
                    editor.putBoolean("ads_removed", true);
                    editor.apply();
                    Intent intent = new Intent(HelpActivity.this, MainActivity.class);
                    startActivity(intent);
                }
            });
        }

    }

    public void setupBillingClient() {
        billingClient = BillingClient.newBuilder(this).setListener(purchasesUpdatedListener).enablePendingPurchases().build();


        establishConnection();
    }

    void establishConnection() {

        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    // The BillingClient is ready. You can query purchases here.
                    QueryPurchase();
                }
            }

            @Override
            public void onBillingServiceDisconnected() {
                // Try to restart the connection on the next request to
                // Google Play by calling the startConnection() method.
                establishConnection();
            }
        });
    }

    public void QueryPurchase() {
        QueryProductDetailsParams queryProductDetailsParams = QueryProductDetailsParams.newBuilder().setProductList(ImmutableList.of(QueryProductDetailsParams.Product.newBuilder().setProductId("removeads_rickroll").setProductType(BillingClient.ProductType.INAPP).build())).build();

        billingClient.queryProductDetailsAsync(queryProductDetailsParams, new ProductDetailsResponseListener() {
            public void onProductDetailsResponse(BillingResult billingResult, List<ProductDetails> productDetailsList) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    // Check if the productDetailsList is not empty
                    if (productDetailsList != null && !productDetailsList.isEmpty()) {
                       

                        productDetails = productDetailsList.get(0);
                        

                    } else {
                       
                    }
                } else {
                    
                }
            }
        });
    }

    public void initiateBillingFlow() {
        if (productDetails != null) {
            ImmutableList<BillingFlowParams.ProductDetailsParams> productDetailsParamsList =
                    ImmutableList.of(
                            BillingFlowParams.ProductDetailsParams.newBuilder()
                                    .setProductDetails(productDetails)
                                    .build()
                    );
            BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
                    .setProductDetailsParamsList(productDetailsParamsList)
                    .build();

            BillingResult billingResult = billingClient.launchBillingFlow(HelpActivity.this, billingFlowParams);
        }
    }

    public void restorePurchases() {
        billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener((billingResult, list) -> {
        }).build();
        final BillingClient finalBillingClient = billingClient;
        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingServiceDisconnected() {
                establishConnection();
            }

            @Override
            public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    finalBillingClient.queryPurchasesAsync(
                            QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build(), (billingResult1, list) -> {
                                if (billingResult1.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                                    if (list.size() > 0) {

                                        Toast.makeText(HelpActivity.this, "Successfully restored", Toast.LENGTH_SHORT).show();

                                        SharedPreferences preferences = getSharedPreferences("adremoveSP", MODE_PRIVATE);
                                        SharedPreferences.Editor editor = preferences.edit();
                                        editor.putBoolean("ads_removed", true);
                                        editor.apply();
                                        Intent intent = new Intent(HelpActivity.this, MainActivity.class);
                                        startActivity(intent);
                                    } else {

                                        Toast.makeText(HelpActivity.this, "Oops, No purchase found.", Toast.LENGTH_SHORT).show();
                                        SharedPreferences preferences = getSharedPreferences("adremoveSP", MODE_PRIVATE);
                                        SharedPreferences.Editor editor = preferences.edit();
                                        editor.putBoolean("ads_removed", false);
                                        editor.apply();


                                    }
                                }
                            });
                }
            }
        });
    }

    protected void onResume() {
        super.onResume();
        billingClient.queryPurchasesAsync(
                QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build(),
                (billingResult, list) -> {
                    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                        for (Purchase purchase : list) {
                            if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED && !purchase.isAcknowledged()) {
                                handlePurchase(purchase);
                            }
                        }
                    }
                }
        );
    }

}`

I understand that I'm including the entire file code, but it's necessary as it comprises 99% of the Google Play Billing implementation.

Another point I'd like to highlight is that when I click the 'Okay' button in the billing flow where the purchase has already been made, the app crashes after clicking the 'Okay' button twice. I'm unsure how to identify the cause of the problem since it doesn't run from the studio itself.

0

There are 0 best solutions below