Code Example

Android Google InApp Purchase Snippets

Learn Google InApp purchase using this simple demo example and snippets.

1: Google InApp Purchase Snippets

Here are some simple snippets:

How to establish the connection with play library. It will be used to notify that setup is complete and the billing client is ready. You can query whatever you want:

    private void startConnection() {
        mBillingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                int billingResponseCode = billingResult.getResponseCode();
                Log.d(TAG, "onBillingSetupFinished: " + billingResult.getResponseCode());
                if (billingResponseCode == BillingClient.BillingResponseCode.OK) {
                    getPurchasedItems();
                    getSKUDetails(skuList);
                }
            }

            @Override
            public void onBillingServiceDisconnected() {
                Log.d(TAG, "onBillingServiceDisconnected: ");
            }
        });
    }

How to Get purchases details for all the items bought within your app:

    public void getPurchasedItems() {
        Purchase.PurchasesResult purchasesResult = mBillingClient.queryPurchases(BillingClient.SkuType.INAPP);
        if (IAPHelperListener != null)
            IAPHelperListener.onPurchasehistoryResponse(purchasesResult.getPurchasesList());
    }

How to Perform a network query to get SKU details and return the result asynchronously:

    public void getSKUDetails(List<String> skuList) {
        final HashMap<String, SkuDetails> skuDetailsHashMap = new HashMap<>();
        SkuDetailsParams skuParams = SkuDetailsParams.newBuilder().setType(BillingClient.SkuType.INAPP).setSkusList(skuList).build();
        mBillingClient.querySkuDetailsAsync(skuParams, new SkuDetailsResponseListener() {
            @Override
            public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
                    for (SkuDetails skuDetails : skuDetailsList) {
                        skuDetailsHashMap.put(skuDetails.getSku(), skuDetails);
                    }
                    if (IAPHelperListener != null)
                        IAPHelperListener.onSkuListResponse(skuDetailsHashMap);
                }
            }
        });

How to verify a purchase:

    private boolean isSignatureValid(Purchase purchase) {
        return Security.verifyPurchase(Security.BASE_64_ENCODED_PUBLIC_KEY, purchase.getOriginalJson(), purchase.getSignature());
    }
``

How to release <code>BillingClient reference resources:

```java
    public void endConnection() {
        if (mBillingClient != null && mBillingClient.isReady()) {
            mBillingClient.endConnection();
            mBillingClient = null;
        }
    }

This example will comprise the following files:

  • IAPHelper.java
  • MainActivity.java
  • Security.java

Step 1: Create Project

  1. Open your AndroidStudio IDE.
  2. Go to File-->New-->Project to create a new project.

Step 2: Add Dependencies

In your app/build.gradle add dependencies as shown below:

apply plugin: 'com.android.application'

android {
    //..
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'com.android.billingclient:billing:2.1.0'
    implementation 'com.jakewharton:butterknife:10.2.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
    //..
}

Step 3: Design Layouts

*(a). activity_main.xml

Create a file named activity_main.xml and design it as follows:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/ivCoins"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:src="@drawable/coins"
        android:layout_marginTop="20dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

    <TextView
        android:id="@+id/tvTotalCoins"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="Total Coins : "
        android:textSize="20sp"
        android:textColor="@color/colorPrimaryDark"
        android:textStyle="bold|italic"
        app:layout_constraintTop_toBottomOf="@+id/ivCoins"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"/>

    <TextView
        android:id="@+id/tvTotalCoinsNum"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="0"
        android:textSize="20sp"
        android:textColor="@color/colorPrimaryDark"
        android:textStyle="bold|italic"
        app:layout_constraintTop_toBottomOf="@+id/ivCoins"
        app:layout_constraintStart_toEndOf="@+id/tvTotalCoins"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/colorPrimaryDark"
        android:layout_margin="10dp"
        app:layout_constraintTop_toBottomOf="@+id/tvTotalCoins"/>

    <TextView
        android:id="@+id/tvConsumable"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="@string/consumable"
        android:textSize="15sp"
        android:textColor="@android:color/black"
        app:layout_constraintTop_toBottomOf="@+id/tvTotalCoins"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

    <LinearLayout
        android:id="@+id/llBuyCoin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:background="@color/colorPrimary"
        android:layout_margin="20dp"
        android:gravity="center"
        app:layout_constraintTop_toBottomOf="@+id/tvConsumable"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/ivBuyCoin"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:src="@drawable/get_coin"/>

        <TextView
            android:id="@+id/tvBuyCoin"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:text="@string/buy_coin"
            android:textSize="20sp"
            android:textColor="@android:color/white"/>

    </LinearLayout>

    <TextView
        android:id="@+id/tvNonConsumable"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="@string/non_consumable"
        android:textSize="15sp"
        android:textColor="@android:color/black"
        app:layout_constraintTop_toBottomOf="@+id/llBuyCoin"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

    <LinearLayout
        android:id="@+id/llUnlockRing"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:background="@color/colorPrimary"
        android:layout_margin="20dp"
        android:gravity="center"
        app:layout_constraintTop_toBottomOf="@+id/tvNonConsumable"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/llUnlockNecklace"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/ivUnlockRing"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:src="@drawable/ring"/>

        <TextView
            android:id="@+id/tvUnlockRing"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/unlock_ring"
            android:layout_marginTop="4dp"
            android:textSize="15sp"
            android:textColor="@android:color/white"/>

        <TextView
            android:id="@+id/tvRingBought"
            android:visibility="gone"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/bought"
            android:layout_marginTop="4dp"
            android:textSize="20sp"
            android:textColor="@android:color/white"/>

    </LinearLayout>

    <LinearLayout
        android:id="@+id/llUnlockNecklace"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:background="@color/colorPrimary"
        android:layout_marginTop="20dp"
        android:gravity="center"
        app:layout_constraintTop_toBottomOf="@+id/tvNonConsumable"
        app:layout_constraintStart_toEndOf="@+id/llUnlockRing"
        app:layout_constraintEnd_toStartOf="@+id/llUnlockCrown"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/ivUnlockNecklace"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:src="@drawable/necklace"/>

        <TextView
            android:id="@+id/tvUnlockNecklace"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/unlock_necklace"
            android:layout_marginTop="4dp"
            android:textSize="15sp"
            android:textColor="@android:color/white"/>

        <TextView
            android:id="@+id/tvNecklaceBought"
            android:visibility="gone"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/bought"
            android:layout_marginTop="4dp"
            android:textSize="20sp"
            android:textColor="@android:color/white"/>

    </LinearLayout>

    <LinearLayout
        android:id="@+id/llUnlockCrown"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:background="@color/colorPrimary"
        android:layout_margin="20dp"
        android:gravity="center"
        app:layout_constraintTop_toBottomOf="@+id/tvNonConsumable"
        app:layout_constraintStart_toEndOf="@+id/llUnlockNecklace"
        app:layout_constraintEnd_toEndOf="parent"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/ivUnlockCrown"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:src="@drawable/crown"/>

        <TextView
            android:id="@+id/tvUnlockCrown"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/unlock_crown"
            android:layout_marginTop="4dp"
            android:textSize="15sp"
            android:textColor="@android:color/white"/>

        <TextView
            android:id="@+id/tvCrownBought"
            android:visibility="gone"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/bought"
            android:layout_marginTop="4dp"
            android:textSize="20sp"
            android:textColor="@android:color/white"/>

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

Step 4: Write Code

Write Code as follows:

*(a). IAPHelper.java

Create a file named IAPHelper.java

Here is the full code

package com.demo.iap;

import android.app.Activity;
import android.content.Context;
import android.util.Log;

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.ConsumeParams;
import com.android.billingclient.api.ConsumeResponseListener;
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 java.util.HashMap;
import java.util.List;

public class IAPHelper {

    private String TAG = IAPHelper.class.getSimpleName();

    private Context context;
    private BillingClient mBillingClient;
    private IAPHelperListener IAPHelperListener;
    private List<String> skuList;

    /**
     * To instantiate the object
     *  @param context           It will be used to get an application context to bind to the in-app billing service.
     * @param IAPHelperListener Your listener to get the response for your query.
     * @param skuList
     */
    public IAPHelper(Context context, IAPHelperListener IAPHelperListener, List<String> skuList) {
        this.context = context;
        this.IAPHelperListener = IAPHelperListener;
        this.skuList = skuList;
        this.mBillingClient = BillingClient.newBuilder(context)
                .enablePendingPurchases()
                .setListener(getPurchaseUpdatedListener())
                .build();
        if (!mBillingClient.isReady()) {
            Log.d(TAG, "BillingClient: Start connection...");
            startConnection();
        }
    }

    /**
     * To establish the connection with play library
     * It will be used to notify that setup is complete and the billing
     * client is ready. You can query whatever you want.
     */
    private void startConnection() {
        mBillingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                int billingResponseCode = billingResult.getResponseCode();
                Log.d(TAG, "onBillingSetupFinished: " + billingResult.getResponseCode());
                if (billingResponseCode == BillingClient.BillingResponseCode.OK) {
                    getPurchasedItems();
                    getSKUDetails(skuList);
                }
            }

            @Override
            public void onBillingServiceDisconnected() {
                Log.d(TAG, "onBillingServiceDisconnected: ");
            }
        });
    }

    /**
     * Get purchases details for all the items bought within your app.
     */
    public void getPurchasedItems() {
        Purchase.PurchasesResult purchasesResult = mBillingClient.queryPurchases(BillingClient.SkuType.INAPP);
        if (IAPHelperListener != null)
            IAPHelperListener.onPurchasehistoryResponse(purchasesResult.getPurchasesList());
    }

    /**
     * Perform a network query to get SKU details and return the result asynchronously.
     */
    public void getSKUDetails(List<String> skuList) {
        final HashMap<String, SkuDetails> skuDetailsHashMap = new HashMap<>();
        SkuDetailsParams skuParams = SkuDetailsParams.newBuilder().setType(BillingClient.SkuType.INAPP).setSkusList(skuList).build();
        mBillingClient.querySkuDetailsAsync(skuParams, new SkuDetailsResponseListener() {
            @Override
            public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
                    for (SkuDetails skuDetails : skuDetailsList) {
                        skuDetailsHashMap.put(skuDetails.getSku(), skuDetails);
                    }
                    if (IAPHelperListener != null)
                        IAPHelperListener.onSkuListResponse(skuDetailsHashMap);
                }
            }
        });
    }

    /**
     * Initiate the billing flow for an in-app purchase or subscription.
     *
     * @param skuDetails skudetails of the product to be purchased
     *                   Developer console.
     */
    public void launchBillingFLow(final SkuDetails skuDetails) {
        if(mBillingClient.isReady()){
            BillingFlowParams mBillingFlowParams = BillingFlowParams.newBuilder()
                    .setSkuDetails(skuDetails)
                    .build();
            mBillingClient.launchBillingFlow((Activity) context, mBillingFlowParams);
        }
    }

    /**
     * Your listener to get the response for purchase updates which happen when, the user buys
     * something within the app or by initiating a purchase from Google Play Store.
     */
    private PurchasesUpdatedListener getPurchaseUpdatedListener() {
        return (billingResult, purchases) -> {
            int responseCode = billingResult.getResponseCode();
            if (responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
                //here when purchase completed
                for (Purchase purchase : purchases) {
                    //I have named sku in such a way that I get sku name as "type_name" for ex: "nc_ring"
                    //For non consumable I will acknowledge purchase
                    //For consumable I will consume purchase
                    String type = purchase.getSku().split("_")[0];
                    if(type.equals("nc"))
                        acknowledgePurchase(purchase);
                    else
                        consumePurchase(purchase);
                }
            } else if (responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
                // Handle an error caused by a user cancelling the purchase flow.
                Log.d(TAG, "user cancelled");
            } else if (responseCode == BillingClient.BillingResponseCode.SERVICE_DISCONNECTED) {
                Log.d(TAG , "service disconnected");
                startConnection();
            }
        };
    }

    public void acknowledgePurchase(Purchase purchase) {
        if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED
                && isSignatureValid(purchase)) {

            //This is for Consumable product
            AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build();
            mBillingClient.acknowledgePurchase(acknowledgePurchaseParams, new AcknowledgePurchaseResponseListener() {
                @Override
                public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
                    Log.d("purchase", "Purchase Acknowledged");
                }
            });

            if (IAPHelperListener != null)
                IAPHelperListener.onPurchaseCompleted(purchase);
        }
    }

    public void consumePurchase(Purchase purchase) {
        if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED
                && isSignatureValid(purchase)) {

            //This is for Consumable product
            ConsumeParams consumeParams = ConsumeParams.newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build();
            mBillingClient.consumeAsync(consumeParams, new ConsumeResponseListener() {
                @Override
                public void onConsumeResponse(BillingResult billingResult, String s) {
                    Log.d("purchase", "Purchase Consumed");
                }
            });

            if (IAPHelperListener != null)
                IAPHelperListener.onPurchaseCompleted(purchase);
        }
    }

    private boolean isSignatureValid(Purchase purchase) {
        return Security.verifyPurchase(Security.BASE_64_ENCODED_PUBLIC_KEY, purchase.getOriginalJson(), purchase.getSignature());
    }

    /**
     * Call this method once you are done with this BillingClient reference.
     */
    public void endConnection() {
        if (mBillingClient != null && mBillingClient.isReady()) {
            mBillingClient.endConnection();
            mBillingClient = null;
        }
    }

    /**
     * Listener interface for handling the various responses of the Purchase helper util
     */
    public interface IAPHelperListener {
        void onSkuListResponse(HashMap<String, SkuDetails> skuDetailsHashMap);
        void onPurchasehistoryResponse(List<Purchase> purchasedItem);
        void onPurchaseCompleted(Purchase purchase);
    }

}

*(b). MainActivity.java

Create a file named MainActivity.java

Here is the full code

package com.demo.iap;

import androidx.appcompat.app.AppCompatActivity;

import android.Manifest;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.ConsumeParams;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.SkuDetails;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class MainActivity extends AppCompatActivity implements IAPHelper.IAPHelperListener {

    private String TAG = MainActivity.class.getSimpleName();

    IAPHelper iapHelper;
    HashMap<String, SkuDetails> skuDetailsHashMap = new HashMap<>();
    //For non_consumable tag "nc" is used at start
    final String RING = "nc_ring";
    final String NECKLACE = "nc_necklace";
    final String CROWN = "nc_crown";
    final String COIN = "coin";
    final String TEST = "android.test.purchased"; //This id can be used for testing purpose
    private List<String> skuList = Arrays.asList(COIN, RING, NECKLACE, CROWN, TEST);

    private SharedPreferences pref;
    private String SETTINGS = "saved_settings";
    private String SETTINGS_COINS = "saved_coins";

    @BindView(R.id.tvTotalCoinsNum)
    TextView tvtotalCoin;

    private Integer totalCoins;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        iapHelper = new IAPHelper(this, this, skuList);

        //Get previous coins from shared pref data
        pref = getSharedPreferences(SETTINGS, 0);
        totalCoins = pref.getInt(SETTINGS_COINS, 0);
        tvtotalCoin.setText(String.format(Locale.getDefault(), "%d", totalCoins));
    }

    @OnClick(R.id.llBuyCoin)
    public void buyCoin(){
        launch(TEST);
    }

    @OnClick(R.id.llUnlockRing)
    public void unlockRing(){
        launch(RING);
    }

    @OnClick(R.id.llUnlockNecklace)
    public void unlockNecklace(){
        launch(NECKLACE);
    }

    @OnClick(R.id.llUnlockCrown)
    public void unlockCrown(){
        launch(CROWN);
    }

    private void launch(String sku){
        if(!skuDetailsHashMap.isEmpty())
            iapHelper.launchBillingFLow(skuDetailsHashMap.get(sku));
    }

    @Override
    public void onSkuListResponse(HashMap<String, SkuDetails> skuDetails) {
        skuDetailsHashMap = skuDetails;
    }

    @Override
    public void onPurchasehistoryResponse(List<Purchase> purchasedItems) {
        if (purchasedItems != null) {
            for (Purchase purchase : purchasedItems) {
                //Update UI and backend according to purchased items if required
                // Like in this project I am updating UI for purchased items
                String sku = purchase.getSku();
                switch (sku) {
                    case RING:
                        findViewById(R.id.tvRingBought).setVisibility(View.VISIBLE);
                        break;
                    case NECKLACE:
                        findViewById(R.id.tvNecklaceBought).setVisibility(View.VISIBLE);
                        break;
                    case CROWN:
                        findViewById(R.id.tvCrownBought).setVisibility(View.VISIBLE);
                        break;
                }
            }
        }
    }

    @Override
    public void onPurchaseCompleted(Purchase purchase) {
        Toast.makeText(getApplicationContext(), "Purchase Successful", Toast.LENGTH_SHORT).show();
        updatePurchase(purchase);
    }

    private void updatePurchase(Purchase purchase){
        String sku = purchase.getSku();
        switch (sku) {
            case RING:
                findViewById(R.id.tvRingBought).setVisibility(View.VISIBLE);
                break;
            case NECKLACE:
                findViewById(R.id.tvNecklaceBought).setVisibility(View.VISIBLE);
                break;
            case CROWN:
                findViewById(R.id.tvCrownBought).setVisibility(View.VISIBLE);
                break;
            case TEST:
                totalCoins += 25;
                pref.edit().putInt(SETTINGS_COINS, totalCoins).apply();
                tvtotalCoin.setText(String.format(Locale.getDefault(), "%d", totalCoins));
                break;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (iapHelper != null)
            iapHelper.endConnection();
    }
}

*(c). Security.java

Create a file named Security.java

Here is the full code

/* Copyright (c) 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.demo.iap;

import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;

import com.android.billingclient.api.Purchase;

import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

/**
 * Security-related methods. For a secure implementation, all of this code
 * should be implemented on a server that communicates with the
 * application on the device. For the sake of simplicity and clarity of this
 * example, this code is included here and is executed on the device. If you
 * must verify the purchases on the phone, you should obfuscate this code to
 * make it harder for an attacker to replace the code with stubs that treat all
 * purchases as verified.
 */
public class Security {
    private static final String TAG = Security.class.getSimpleName();

    private static final String KEY_FACTORY_ALGORITHM = "RSA";
    private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";

    //This you will get from Services & API in your application console
    public static String BASE_64_ENCODED_PUBLIC_KEY = "your_public_key_that you_get_from_google_play_console";

    /**
     * Verifies that the data was signed with the given signature, and returns
     * the verified purchase. The data is in JSON format and signed
     * with a private key. The data also contains the {@link Purchase.PurchaseState}
     * and product ID of the purchase.
     * @param base64PublicKey the base64-encoded public key to use for verifying.
     * @param signedData the signed JSON string (signed, not encrypted)
     * @param signature the signature for the data, signed with the private key
     */
    public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {
        if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) ||
                TextUtils.isEmpty(signature)) {
            Log.e(TAG, "Purchase verification failed: missing data.");
            return false;
        }

        PublicKey key = Security.generatePublicKey(base64PublicKey);
        return Security.verify(key, signedData, signature);
    }

    /**
     * Generates a PublicKey instance from a string containing the
     * Base64-encoded public key.
     *
     * @param encodedPublicKey Base64-encoded public key
     * @throws IllegalArgumentException if encodedPublicKey is invalid
     */
    private static PublicKey generatePublicKey(String encodedPublicKey) {
        try {
            byte[] decodedKey = Base64.decode(encodedPublicKey, Base64.DEFAULT);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
            return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (InvalidKeySpecException e) {
            Log.e(TAG, "Invalid key specification.");
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * Verifies that the signature from the server matches the computed
     * signature on the data.  Returns true if the data is correctly signed.
     *
     * @param publicKey public key associated with the developer account
     * @param signedData signed data from server
     * @param signature server signature
     * @return true if the data and signature match
     */
    private static boolean verify(PublicKey publicKey, String signedData, String signature) {
        byte[] signatureBytes;
        try {
            signatureBytes = Base64.decode(signature, Base64.DEFAULT);
        } catch (IllegalArgumentException e) {
            Log.e(TAG, "Base64 decoding failed.");
            return false;
        }
        try {
            Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
            sig.initVerify(publicKey);
            sig.update(signedData.getBytes());
            if (!sig.verify(signatureBytes)) {
                Log.e(TAG, "Signature verification failed.");
                return false;
            }
            return true;
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "NoSuchAlgorithmException.");
        } catch (InvalidKeyException e) {
            Log.e(TAG, "Invalid key specification.");
        } catch (SignatureException e) {
            Log.e(TAG, "Signature exception.");
        }
        return false;
    }
}

Run

Simply copy the source code into your Android Project,Build and Run.

Reference

Download code here.
Follow code author here.