A simple way to integrate in-app purchase with the Google Play Billing Library into your app - Subscription

Working on Android In-App Purchases for Google Play billing library with the latest version 5.0.0+

A simple way to integrate in-app purchase with the Google Play Billing Library into your app - Subscription

In this article, I'm going to show you how to integrate In-App Purchase for Subscription of Google Play Billing version 5+ in 7 steps. I follow the official google docs, I'm not using any third-party library.

Pre-requisite

  • Google Play Console Account
  • Published App on Play Store
  • Tester Device with GMS
Configure Your Testing device by adding the Gmail account to internal testing testers 
and License testing (Watch the YouTube video for clarity: https://youtu.be/j6wWVMj-fi8 )


Setup the in-app purchase subscription product in the Google Play Console account
I have already created mine which are 
Product ID: sub_premium

The following methods (These are the methods you need for the IAP System to work, you can copy and paste)

void establishConnection(){}
void showProducts(){}
void launchPurchaseFlow(){}
void verifySubPayment(Purchase purchases){}
void checkSubscription(){}

Step 0: //Add the Google Play Billing Library dependency
Step 1: //Initialize a BillingClient with PurchasesUpdatedListener
Step 2: //Establish a connection to Google Play
Step 3: //Show products available to buy
Step 4: //Launch the purchase flow
Step 5: //Processing purchases / Verify Payment
Step 6: //Handling pending transactions
Step 7: //Check the subscriptions on SplashScreenActivity

Step 0: //Add the Google Play Billing Library dependency

//Add the Google Play Billing Library dependency to your app's build.gradle file as shown:

dependencies {
    def billingVersion = "5.0.0"
    implementation "com.android.billingclient:billing:$billingVersion"
}

And Open Manifest File and add this permission

Step 1: //Initialize a BillingClient with PurchasesUpdatedListener

  //Initialize a BillingClient with PurchasesUpdatedListener onCreate method

    billingClient = BillingClient.newBuilder(this)
                .enablePendingPurchases()
                .setListener(
                        new PurchasesUpdatedListener() {
                            @Override
                            public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List list) {
                               if(billingResult.getResponseCode()==BillingClient.BillingResponseCode.OK && list !=null) {
                                   for (Purchase purchase: list){
                                       verifySubPurchase(purchase);
                                   }
                               }
                            }
                        }
                ).build();

        //start the connection after initializing the billing client
        establishConnection();
                

Step 2: //Establish a connection to Google Play

 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.
                    showProducts();
                }
            }

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

Step 3: //Show products available to buy

@SuppressLint("SetTextI18n")
    void showProducts() {

        ImmutableList productList = ImmutableList.of(
                //Product 1 = index is 0
                QueryProductDetailsParams.Product.newBuilder()
                .setProductId("sub_premium")
                .setProductType(BillingClient.ProductType.SUBS)
                .build(),

                //Product 2 = index is 1
                QueryProductDetailsParams.Product.newBuilder()
                .setProductId("test_id_shar")
                .setProductType(BillingClient.ProductType.SUBS)
                .build()

        );

        QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()
                .setProductList(productList)
                .build();

        billingClient.queryProductDetailsAsync(
                params,
                (billingResult, productDetailsList) -> {
                    // Process the result
                    for (ProductDetails productDetails : productDetailsList) {
                        if (productDetails.getProductId().equals("sub_premium")) {
                            List subDetails = productDetails.getSubscriptionOfferDetails();
                            assert subDetails != null;
                            Log.d("testOffer",subDetails.get(0).getOfferToken());
                            txt_price.setText(subDetails.get(0).getPricingPhases().getPricingPhaseList().get(0).getFormattedPrice()+" Per Month");
                            txt_price.setOnClickListener(view -> {
                                launchPurchaseFlow(productDetails);
                            });
                        }

                        if (productDetails.getProductId().equals("test_id_shar")) {
                            List subDetails = productDetails.getSubscriptionOfferDetails();
                            assert subDetails != null;
                            Log.d("testOffer",subDetails.get(1).getOfferToken());
                            offer_btn.setText(subDetails.get(1).getPricingPhases().getPricingPhaseList().get(0).getFormattedPrice()+" Per Month");
                            offer_btn.setOnClickListener(view -> {
                                launchPurchaseFlow(productDetails);
                            });
                        }
                    }
                }
        );

    }
   

Step 4: //Launch the purchase flow

    void launchPurchaseFlow(ProductDetails productDetails) {
        assert productDetails.getSubscriptionOfferDetails() != null;
        ImmutableList productDetailsParamsList =
                ImmutableList.of(
                        BillingFlowParams.ProductDetailsParams.newBuilder()
                                .setProductDetails(productDetails)
                                .setOfferToken(productDetails.getSubscriptionOfferDetails().get(0).getOfferToken())
                                .build()
                );
        BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
                .setProductDetailsParamsList(productDetailsParamsList)
                .build();
        BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
    }
    

Step 5: //Processing purchases / Verify Payment

 
    void verifySubPurchase(Purchase purchases) {

        AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams
                .newBuilder()
                .setPurchaseToken(purchases.getPurchaseToken())
                .build();

        billingClient.acknowledgePurchase(acknowledgePurchaseParams, billingResult -> {
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                //user prefs to set premium
                Toast.makeText(StoreActivity.this, "You are a premium user now", Toast.LENGTH_SHORT).show();
                //Setting premium to 1
                // 1 - premium
                // 0 - no premium
                prefs.setPremium(1);
            }
        });

        Log.d(TAG, "Purchase Token: " + purchases.getPurchaseToken());
        Log.d(TAG, "Purchase Time: " + purchases.getPurchaseTime());
        Log.d(TAG, "Purchase OrderID: " + purchases.getOrderId());
    }
    

Step 6: //Handling pending transactions

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

    }

Step 7: //Check Subscription (This code goes to your Splash screen)

void checkSubscription(){

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

        }

        @Override
        public void onBillingSetupFinished(@NonNull BillingResult billingResult) {

            if(billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK){
                finalBillingClient.queryPurchasesAsync(
                        QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.SUBS).build(), (billingResult1, list) -> {
                    if (billingResult1.getResponseCode() == BillingClient.BillingResponseCode.OK){
                         Log.d("testOffer",list.size() +" size");
                         if(list.size()>0){
                             prefs.setPremium(1); // set 1 to activate premium feature
                             int i = 0;
                             for (Purchase purchase: list){
                                 //Here you can manage each product, if you have multiple subscription
                                 Log.d("testOffer",purchase.getOriginalJson()); // Get to see the order information
                                 Log.d("testOffer", " index" + i);
                                 i++;
                             }
                         }else {
                             prefs.setPremium(0); // set 0 to de-activate premium feature
                         }
                    }
                });

            }

        }
    });
}

GitHub Repo: https://github.com/wdtheprovider/in-app-purchases-subscription

Files