In-App purchase Google Play Billing - Play Billing Library

Setup Google Play developer account

Google Play console use we can manage our products with Google Play Billing.
To sell our products with Google Play we need GooglePaymentcenter  profile account
Then we need to link this account with Google Play developer account.

You can check how to Link GooglePlay developer account

InApp purchase Licence API

 

Enable Google Play Billing

Once we are ready with developer account we must publish our app with including Google Play Billing dependencies.
This step is used to enable Google Play Billing features in the Google Play Console

Add Google Play Billing dependencies

add below dependencies in app levlel build.gradle file

dependencies {
    def billing_version = "3.0.0"

    implementation 'com.android.billingclient:billing:$billing_version'//Android
    implementation 'com.android.billingclient:billing-ktx:$billing_version'// If using Kotlin
}

 

After adding this, just create a bundle/apk and publish it in Googleplay Console in Alpha/beta/Testing track mode

Create Products

We can add our products for two ways

  1. Create and configure one-time products (Here)
  2. Create and configure subscription (Here)

After adding product now come to application and add your logic to show products and display subscription plans

Create BillingClient object

mBillingClient = BillingClient.newBuilder(this).setListener(new PurchasesUpdatedListener() {
            @Override
            public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> purchases) {

                
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
                        && purchases != null) {
                    for (Purchase purchase : purchases) {
                    //Here all your purchased products. Handle your logic here.
                       
                    }
                } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
                    // Handle an error caused by a user cancelling the purchase flow.
                } else {
                    // Handle any other error codes.
                }
            }


        }).enablePendingPurchases()
                .build();

 

Start Billing connection to get data

mBillingClient.startConnection(new BillingClientStateListener() {


            @Override
            public void onBillingSetupFinished(@NonNull BillingResult billingResponseCode) {
                Log.v("ResposneCode","ResposneCode "+billingResponseCode.getResponseCode());
                if (billingResponseCode.getResponseCode() == BillingClient.BillingResponseCode.OK) {

                }
            }

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

 

Get all Subscription/Products list from Merchant account and display it.
To fetch products we need to request with its product ids

final List<String> skuList = new ArrayList<>();

        skuList.add("weekly"); // SKU Id
        skuList.add("monthly");  // SKU Id
        skuList.add("3months");  // SKU Id
        skuList.add("6months");  // SKU Id
        SkuDetailsParams params = SkuDetailsParams.newBuilder()
                .setSkusList(skuList)
                .setType(BillingClient.SkuType.SUBS)
                .build();
        mBillingClient.querySkuDetailsAsync(params,
                new SkuDetailsResponseListener() {
                    @Override
                    public void onSkuDetailsResponse(BillingResult billingResult,
                                                     List<SkuDetails> skuDetailsList)
                    {
                    
                    //Return all products list
                       
                        if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
                            Log.v("ResposneCode","ResposneCode13 "+skuDetailsList.size());
                           

                        }
                    }
                });
        

 

Launch Billing FlowSubscrib Plan

To subscribe specific plan/Products we need to launch Billing flow

BillingFlowParams.Builder builder = BillingFlowParams.newBuilder()
                                    .setSkuDetails(skuListData.get(pos));
                            mBillingClient.launchBillingFlow(_this, builder.build());

 

We can get status of subscribed product under PurchasesUpdatedListener(), there we need to     save access Key of the products/subscription plan. This key we need to save at your app level/server side

 

Check Subscription Status

If we subscribe any product/subscription, to verify it status we need it access Key.

To check status we need to use acknowledgePurchase() query 

AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener=new AcknowledgePurchaseResponseListener() {
            @Override
            public void onAcknowledgePurchaseResponse(@NonNull BillingResult billingResult) {

                Log.v("Onresposne","Onresposne purchased "+billingResult.getResponseCode());
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                  //Subscription will activate status
                }else{
                   
                }
            }
        };
 AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                        .setPurchaseToken(key)
                        .build();
        mBillingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);

 

Example Code

Activity.java

package com.rrtutors.inapppurchase;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.android.billingclient.api.AcknowledgePurchaseParams;
import com.android.billingclient.api.AcknowledgePurchaseResponseListener;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;

import java.util.ArrayList;
import java.util.List;

public class MySubScription extends AppCompatActivity {

    Button btn_sub;
    TextView txt_des;
    String[]subTypeList;
    Dialog alertDialog1;

    BillingClient   mBillingClient;
    Activity _this;


    SharedPreferences sh;

    int pos=0;
    List<SkuDetails>skuListData;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_sub_scription);
        btn_sub=findViewById(R.id.btn_sub);
        txt_des=findViewById(R.id.txt_des);
        btn_sub.setEnabled(false);

        openConnection();
        _this=this;
        sh=getSharedPreferences("mysub",MODE_PRIVATE);
        btn_sub.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if(subTypeList==null||subTypeList.length<=0)
                {
                    return;
                }
                AlertDialog.Builder builder = new AlertDialog.Builder(MySubScription.this);

                builder.setTitle("Select Your Choice");

                builder.setSingleChoiceItems(subTypeList, -1, new DialogInterface.OnClickListener() {

                    public void onClick(DialogInterface dialog, int item) {

                        pos=item;

                    }
                });
                builder.setPositiveButton("Continue", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        alertDialog1.dismiss();
                        if(skuListData!=null&&skuListData.size()>pos)
                        {
                            BillingFlowParams.Builder builder = BillingFlowParams.newBuilder()
                                    .setSkuDetails(skuListData.get(pos));
                            mBillingClient.launchBillingFlow(_this, builder.build());
                        }
                    }
                });
                builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        alertDialog1.dismiss();
                    }
                });
                alertDialog1 = builder.create();
                alertDialog1.show();
            }
        });
    }

    private void openConnection()
    {
        mBillingClient = BillingClient.newBuilder(this).setListener(new PurchasesUpdatedListener() {
            @Override
            public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> purchases) {

                Log.v("dfdsf","gggggg "+billingResult.getResponseCode());
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
                        && purchases != null) {
                    for (Purchase purchase : purchases) {


                        sh.edit().putString("key",purchase.getPurchaseToken()).commit();
                    }
                } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
                    // Handle an error caused by a user cancelling the purchase flow.
                } else {
                    // Handle any other error codes.
                }
            }


        }).enablePendingPurchases()
                .build();

        mBillingClient.startConnection(new BillingClientStateListener() {


            @Override
            public void onBillingSetupFinished(@NonNull BillingResult billingResponseCode) {
                Log.v("ResposneCode","ResposneCode "+billingResponseCode.getResponseCode());
                if (billingResponseCode.getResponseCode() == BillingClient.BillingResponseCode.OK) {


                    // String key=getSharedPreferences("mysub",MODE_PRIVATE).getString("key","");
                    // Log.v("Onresposne","Onresposne purchased key "+key);
                    btn_sub.setEnabled(true);
                    checkStatus();
                    showList();


                }
            }

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

    private void showList()
    {


/**
 * To purchase an Subscription
 */

        final List<String> skuList = new ArrayList<>();

        skuList.add("weekly"); // SKU Id
        skuList.add("monthly");  // SKU Id
        skuList.add("3months");  // SKU Id
        skuList.add("6months");  // SKU Id
        SkuDetailsParams params = SkuDetailsParams.newBuilder()
                .setSkusList(skuList)
                .setType(BillingClient.SkuType.SUBS)
                .build();
        mBillingClient.querySkuDetailsAsync(params,
                new SkuDetailsResponseListener() {
                    @Override
                    public void onSkuDetailsResponse(BillingResult billingResult,
                                                     List<SkuDetails> skuDetailsList)
                    {
                        Log.v("ResposneCode","ResposneCode11 "+billingResult.getResponseCode());
                        Log.v("ResposneCode","ResposneCode12 "+skuDetailsList);
                        if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
                            Log.v("ResposneCode","ResposneCode13 "+skuDetailsList.size());
                            Product product;
                            skuListData=skuDetailsList;
                            subTypeList=new String[skuListData.size()];
                           for (int k=0;k<skuDetailsList.size();k++)
                               subTypeList[k]=(skuDetailsList.get(k).getTitle()+"\n Price "+skuDetailsList.get(k).getPrice()+"\n").replaceAll("(InAppSubscription)"," Sub ");

                        }
                    }
                });


    }

    private void checkStatus()
    {

        AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener=new AcknowledgePurchaseResponseListener() {
            @Override
            public void onAcknowledgePurchaseResponse(@NonNull BillingResult billingResult) {

                Log.v("Onresposne","Onresposne purchased "+billingResult.getResponseCode());
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                   btn_sub.setVisibility(View.GONE);
                    txt_des.setText("You already Subscribed, You can access all Content.");
                }else{
                    btn_sub.setVisibility(View.VISIBLE);
                    txt_des.setText("To Access all content please subscribe.");
                }
            }
        };
        String key=getSharedPreferences("mysub",MODE_PRIVATE).getString("key","");
        if(key.isEmpty())
        {
            return;
        }
        AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                        .setPurchaseToken(key)
                        .build();
        mBillingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);

    }
}

 

xml file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:background="@color/colorPrimary"
    tools:context="com.rrtutors.inapppurchase.MySubScription">

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_margin="10dp"
        android:layout_gravity="center"
        app:cardCornerRadius="10dp"
        app:cardElevation="10dp"
        android:layout_height="wrap_content">
        <LinearLayout
            android:layout_width="match_parent"
            android:orientation="vertical"
            android:layout_margin="10dp"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/txt_des"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="18sp"
                android:text="To Access all content please subscribe"
                />
            <Button
                android:id="@+id/btn_sub"
                android:layout_width="wrap_content"
                android:layout_gravity="right"
                android:text="Subscribe"
                android:background="#07DB73"
                android:layout_margin="10dp"
                android:textColor="#FFF"

                android:layout_height="wrap_content" />
        </LinearLayout>
    </androidx.cardview.widget.CardView>
</LinearLayout>

 

Output

Inapp purchase

inapp purchase

inapp purchase