How to make In App Purchase in Android Using Google Play Billing Library
Please Subscribe Youtube| Like Facebook | Follow Twitter
Video
Introduction
In this article we will learn how to integrate and make in app purchase of Single Non-consumable one-time products using google play billing library.
It is recommended to read current article page first for understanding basic concepts of Google Play Billing.

Tips
Article | Java | Kotlin |
For Single Non-consumable one-time product | link | link |
For Multiple Non-consumable one-time product | link | link |
For Single Consumable one-time product | link | link |
For Multiple Consumable one-time product | link | link |
For Single In App Subscriptions | link | link |
For Multiple In App Subscriptions | link | link |
In-app product types
Google Play Billing can be used to sell the following types of in-app products:
1) One-time products: An in-app product requiring a single, non-recurring charge to the user’s form of payment. Additional game levels, premium loot boxes, and media files are examples of one-time products. The Google Play Console refers to one-time products as managed products, and the Google Play Billing library calls them “INAPP”.
Google Play Billing supports the following types of one-time products:
- Non-consumable one-time products are products that provide a permanent effect, such as a premium upgrade. To avoid users from repurchasing these products, you shouldn’t indicate them as being consumed.
- Consumable one-time products are products that provide temporary benefits and can be repurchased, such as additional in-game currency or extra game lives. To make a consumable one-time product available for purchase again, you need to send a consumption request to Google Play.
2) Subscriptions: An in-app product requiring a recurring charge to the user’s form of payment. Online service is example of subscription. The Google Play Billing Library calls these “SUBS”.
Our Example

In this post we will demonstrate how to purchase Non-consumable one-time products. For example purpose we will use “purchase” as Non-consumable one-time product which will allow user to purchase it one time only. You can use this according to your need such as to remove ads or upgrade premium status etc.
In our example we have a textview which shows purchase status and a purchase button which allow user to purchase item. When item is bought purchase status will be changed to “purchased” and purchase button will be removed and we store purchase value in user preference.
On every app start we will check purchase status of item from Google Play Store Cache and reflect necessary changes accordingly because if user already purchased item previously and re-installs the app or switch to another device or refunded the purchased item, therefore we will store updated purchase status value in user preference.
Google Play Store Cache works best on both offline as well as online. When there is no internet Cache will contain last updated purchase status of item. When internet become available Play Store Cache will be updated automatically with latest purchase status of item. Google Play Store Cache updated roughly every 24 hours or immediately on every purchase.
Requirements:
- Android Project/App to which In App Purchase Item to be added
- Google play console account
Note: In order to create in app product items in Google Console or perform real test of in app purchase item in your app, you first need to Integrate Google Play Library and add billing permission in your project and then upload that apk on Google play console and publish it any track (Internal test track (recommended), Closed track, Open track, Production track). Therefore we will perform these actions first.
Steps
Follow below steps
1 Integrate Google Play Library in your project
2 Create an application and fill Store listing in Google play console
3 Upload and publish your app
4 Create product item “purchase” One-Time Managed Product in Google play console
5 Code in app purchase flow logic.
6 Run and Test the application
1 Integrate Google Play Billing Library in your project.
First add Google Play Billing Library dependency at app level of your build.gradle file.
implementation 'com.android.billingclient:billing:3.0.1'
In manifest file add below permission
<uses-permission android:name="com.android.vending.BILLING" />
2 Create an application and fill Store listing in Google play console
First go to your Google play console and create an application


After creating App fill necessary Store listing

3 Upload and publish your app
After necessary Store listing is filled go to app release and open internal test track and click on create release.


Upload your apk, fill necessary info then review and publish/release your app in internal test track.

4 Create product item “purchase” One-Time Managed Product in Google play console
After releasing app in internal test go to Store presence -> In-app products -> Managed products

Click on create managed product

Enter product id “purchase”, title, description and click on active.

Add price details for demo purpose we have entered rs 500 PKR


Then click on save

After saving product item will be created

5 Code in app purchase flow logic.
First make sure you have added Google Play Billing Library dependency and billing permission, which was described in step 1.
Then Add textview Purchase Status and Purchase button in activity_main.xml Layout.
<?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"
tools:context="com.programtown.example.MainActivity">
<TextView
android:id="@+id/purchase_status"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
/>
<Button
android:id="@+id/purchase_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Purchase"
android:onClick="purchase"
/>
</LinearLayout>

In order to make In-App purchase our MainActivity needs to implements PurchasesUpdatedListener interface and override onPurchasesUpdated() method which will be called every time to get notifications for purchases updates. Both purchases initiated by your app and the ones initiated outside of your app will be reported here.
We also need to create BillingClient Object and set listener to it so that we can communicate with Google Play Billing Service.
public class MainActivity extends AppCompatActivity implements PurchasesUpdatedListener {
private BillingClient billingClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
billingClient = BillingClient.newBuilder(this)
.enablePendingPurchases().setListener(this).build();
}
@Override
public void onPurchasesUpdated(BillingResult billingResult, @Nullable
List<Purchase> purchases) {
//we will code purchase result logic later
}
}
On purchase button click we will initiate purchase flow
//initiate purchase on button click
public void purchase(View view) {
//check if service is already connected
if (billingClient.isReady()) {
initiatePurchase();
}
//else reconnect service
else{
billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build();
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
initiatePurchase();
} else {
Toast.makeText(getApplicationContext(),"Error "+billingResult.getDebugMessage(),Toast.LENGTH_SHORT).show();
}
}
@Override
public void onBillingServiceDisconnected() {
}
});
}
}
In initiatePurchase() method we have included product id “purchase”, which was added in Google Play Console inside in app products, specified product type as INAPP. Then we have called querySkuDetailsAsync() method which will query our product from Play console. After querying we will call launchBillingFlow() method which will display purchase dialog. Result of purchase dialog will be reported inside onPurchasesUpdated() method which we will code next.
private void initiatePurchase() {
List<String> skuList = new ArrayList<>();
skuList.add(PRODUCT_ID);
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(INAPP);
billingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(BillingResult billingResult,
List<SkuDetails> skuDetailsList) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
if (skuDetailsList != null && skuDetailsList.size() > 0) {
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetailsList.get(0))
.build();
billingClient.launchBillingFlow(MainActivity.this, flowParams);
}
else{
//try to add item/product id "purchase" inside managed product in google play console
Toast.makeText(getApplicationContext(),"Purchase Item not Found",Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(getApplicationContext(),
" Error "+billingResult.getDebugMessage(), Toast.LENGTH_SHORT).show();
}
}
});
}
In onPurchasesUpdated() method we will check purchase result and handle it accordingly.
@Override
public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> purchases) {
//if item newly purchased
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
handlePurchases(purchases);
}
//if item already purchased then check and reflect changes
else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
Purchase.PurchasesResult queryAlreadyPurchasesResult = billingClient.queryPurchases(INAPP);
List<Purchase> alreadyPurchases = queryAlreadyPurchasesResult.getPurchasesList();
if(alreadyPurchases!=null){
handlePurchases(alreadyPurchases);
}
}
//if purchase cancelled
else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
Toast.makeText(getApplicationContext(),"Purchase Canceled",Toast.LENGTH_SHORT).show();
}
// Handle any other error msgs
else {
Toast.makeText(getApplicationContext(),"Error "+billingResult.getDebugMessage(),Toast.LENGTH_SHORT).show();
}
}
In handlePurchases() method we will handle, verify and acknowledge purchase. After the purchase, acknowledgement is necessary because failure to properly acknowledge purchase will result in purchase being refunded. After the acknowledgement we will save purchase value in preference and restart activity to make necessary changes e.g. change purchase status, hide purchase button.
This method also toast user to complete transaction if purchase status is pending. In case of unspecified purchase state we will change purchase status to “Not Purchased” and store updated value in preference.
void handlePurchases(List<Purchase> purchases) {
for(Purchase purchase:purchases) {
//if item is purchased
if (PRODUCT_ID.equals(purchase.getSku()) && purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED)
{
if (!verifyValidSignature(purchase.getOriginalJson(), purchase.getSignature())) {
// Invalid purchase
// show error to user
Toast.makeText(getApplicationContext(), "Error : Invalid Purchase", Toast.LENGTH_SHORT).show();
return;
}
// else purchase is valid
//if item is purchased and not acknowledged
if (!purchase.isAcknowledged()) {
AcknowledgePurchaseParams acknowledgePurchaseParams =
AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
billingClient.acknowledgePurchase(acknowledgePurchaseParams, ackPurchase);
}
//else item is purchased and also acknowledged
else {
// Grant entitlement to the user on item purchase
// restart activity
if(!getPurchaseValueFromPref()){
savePurchaseValueToPref(true);
Toast.makeText(getApplicationContext(), "Item Purchased", Toast.LENGTH_SHORT).show();
this.recreate();
}
}
}
//if purchase is pending
else if( PRODUCT_ID.equals(purchase.getSku()) && purchase.getPurchaseState() == Purchase.PurchaseState.PENDING)
{
Toast.makeText(getApplicationContext(),
"Purchase is Pending. Please complete Transaction", Toast.LENGTH_SHORT).show();
}
//if purchase is unknown
else if(PRODUCT_ID.equals(purchase.getSku()) && purchase.getPurchaseState() == Purchase.PurchaseState.UNSPECIFIED_STATE)
{
savePurchaseValueToPref(false);
purchaseStatus.setText("Purchase Status : Not Purchased");
purchaseButton.setVisibility(View.VISIBLE);
Toast.makeText(getApplicationContext(), "Purchase Status Unknown", Toast.LENGTH_SHORT).show();
}
}
}
AcknowledgePurchaseResponseListener ackPurchase = new AcknowledgePurchaseResponseListener() {
@Override
public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
if(billingResult.getResponseCode()==BillingClient.BillingResponseCode.OK){
//if purchase is acknowledged
// Grant entitlement to the user. and restart activity
savePurchaseValueToPref(true);
Toast.makeText(getApplicationContext(), "Item Purchased", Toast.LENGTH_SHORT).show();
MainActivity.this.recreate();
}
}
};
For verification we have used client side signature verification, which will be performed with in app (less secure). However if you can afford server then you should do verification of purchase through your server (more secure).
In client side verification you need to add your developer’s public key of app.
For old Play Console Design To get key go to Developer Console > Select your app > Development Tools > Services & APIs.

For new Play Console Design To get key go to Developer Console > Select your app > Monetize > Monetization setup

Then paste that key to String variable base64key in below method.
/**
* Verifies that the purchase was signed correctly for this developer's public key.
* <p>Note: It's strongly recommended to perform such check on your backend since hackers can
* replace this method with "constant true" if they decompile/rebuild your app.
* </p>
*/
private boolean verifyValidSignature(String signedData, String signature) {
try {
// To get key go to Developer Console > Select your app > Development Tools > Services & APIs.
String base64Key = "Add Your Key Here"
return Security.verifyPurchase(base64Key, signedData, signature);
} catch (IOException e) {
return false;
}
}
Code of Security.java is at whole code section
Up to that point our purchase logic is complete. Now On every app start we will check purchase status of item from Google Play Store Cache using getPurchasesList() method and reflect necessary changes accordingly because if user already purchased item previously and reinstalls the app or switch to another device or refunded the purchased item therefore we should store updated purchase status in user preference. After querying we will call handlePurchases() method which will make necessary changes accordingly.
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
if(billingResult.getResponseCode()==BillingClient.BillingResponseCode.OK){
Purchase.PurchasesResult queryPurchase = billingClient.queryPurchases(INAPP);
List<Purchase> queryPurchases = queryPurchase.getPurchasesList();
if(queryPurchases!=null && queryPurchases.size()>0){
handlePurchases(queryPurchases);
}
//if purchase list is empty that means item is not purchased
//Or purchase is refunded or canceled
else{
savePurchaseValueToPref(false);
}
}
}
@Override
public void onBillingServiceDisconnected() {
}
});
//item Purchased
if(getPurchaseValueFromPref()){
purchaseButton.setVisibility(View.GONE);
purchaseStatus.setText("Purchase Status : Purchased");
}
//item not Purchased
else{
purchaseButton.setVisibility(View.VISIBLE);
purchaseStatus.setText("Purchase Status : Not Purchased");
}
Whole Code
App level build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.programtown.example"
minSdkVersion 17
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
implementation 'com.android.billingclient:billing:3.0.1'
}
Manifest File
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.programtown.example">
<uses-permission android:name="com.android.vending.BILLING" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="com.programtown.example.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
activity_main.xml
<?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"
tools:context="com.programtown.example.MainActivity">
<TextView
android:id="@+id/purchase_status"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
/>
<Button
android:id="@+id/purchase_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Purchase"
android:onClick="purchase"
/>
</LinearLayout>
MainActivity.java
package com.programtown.example;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.content.SharedPreferences;
import androidx.annotation.Nullable;
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 static com.android.billingclient.api.BillingClient.SkuType.INAPP;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements PurchasesUpdatedListener {
public static final String PREF_FILE= "MyPref";
public static final String PURCHASE_KEY= "purchase";
public static final String PRODUCT_ID= "purchase";
TextView purchaseStatus;
Button purchaseButton;
private BillingClient billingClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
purchaseStatus=(TextView) findViewById(R.id.purchase_status);
purchaseButton=(Button) findViewById(R.id.purchase_button);
// Establish connection to billing client
//check purchase status from google play store cache
//to check if item already Purchased previously or refunded
billingClient = BillingClient.newBuilder(this)
.enablePendingPurchases().setListener(this).build();
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
if(billingResult.getResponseCode()==BillingClient.BillingResponseCode.OK){
Purchase.PurchasesResult queryPurchase = billingClient.queryPurchases(INAPP);
List<Purchase> queryPurchases = queryPurchase.getPurchasesList();
if(queryPurchases!=null && queryPurchases.size()>0){
handlePurchases(queryPurchases);
}
//if purchase list is empty that means item is not purchased
//Or purchase is refunded or canceled
else{
savePurchaseValueToPref(false);
}
}
}
@Override
public void onBillingServiceDisconnected() {
}
});
//item Purchased
if(getPurchaseValueFromPref()){
purchaseButton.setVisibility(View.GONE);
purchaseStatus.setText("Purchase Status : Purchased");
}
//item not Purchased
else{
purchaseButton.setVisibility(View.VISIBLE);
purchaseStatus.setText("Purchase Status : Not Purchased");
}
}
private SharedPreferences getPreferenceObject() {
return getApplicationContext().getSharedPreferences(PREF_FILE, 0);
}
private SharedPreferences.Editor getPreferenceEditObject() {
SharedPreferences pref = getApplicationContext().getSharedPreferences(PREF_FILE, 0);
return pref.edit();
}
private boolean getPurchaseValueFromPref(){
return getPreferenceObject().getBoolean( PURCHASE_KEY,false);
}
private void savePurchaseValueToPref(boolean value){
getPreferenceEditObject().putBoolean(PURCHASE_KEY,value).commit();
}
//initiate purchase on button click
public void purchase(View view) {
//check if service is already connected
if (billingClient.isReady()) {
initiatePurchase();
}
//else reconnect service
else{
billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build();
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
initiatePurchase();
} else {
Toast.makeText(getApplicationContext(),"Error "+billingResult.getDebugMessage(),Toast.LENGTH_SHORT).show();
}
}
@Override
public void onBillingServiceDisconnected() {
}
});
}
}
private void initiatePurchase() {
List<String> skuList = new ArrayList<>();
skuList.add(PRODUCT_ID);
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(INAPP);
billingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(BillingResult billingResult,
List<SkuDetails> skuDetailsList) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
if (skuDetailsList != null && skuDetailsList.size() > 0) {
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetailsList.get(0))
.build();
billingClient.launchBillingFlow(MainActivity.this, flowParams);
}
else{
//try to add item/product id "purchase" inside managed product in google play console
Toast.makeText(getApplicationContext(),"Purchase Item not Found",Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(getApplicationContext(),
" Error "+billingResult.getDebugMessage(), Toast.LENGTH_SHORT).show();
}
}
});
}
@Override
public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> purchases) {
//if item newly purchased
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
handlePurchases(purchases);
}
//if item already purchased then check and reflect changes
else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
Purchase.PurchasesResult queryAlreadyPurchasesResult = billingClient.queryPurchases(INAPP);
List<Purchase> alreadyPurchases = queryAlreadyPurchasesResult.getPurchasesList();
if(alreadyPurchases!=null){
handlePurchases(alreadyPurchases);
}
}
//if purchase cancelled
else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
Toast.makeText(getApplicationContext(),"Purchase Canceled",Toast.LENGTH_SHORT).show();
}
// Handle any other error msgs
else {
Toast.makeText(getApplicationContext(),"Error "+billingResult.getDebugMessage(),Toast.LENGTH_SHORT).show();
}
}
void handlePurchases(List<Purchase> purchases) {
for(Purchase purchase:purchases) {
//if item is purchased
if (PRODUCT_ID.equals(purchase.getSku()) && purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED)
{
if (!verifyValidSignature(purchase.getOriginalJson(), purchase.getSignature())) {
// Invalid purchase
// show error to user
Toast.makeText(getApplicationContext(), "Error : Invalid Purchase", Toast.LENGTH_SHORT).show();
return;
}
// else purchase is valid
//if item is purchased and not acknowledged
if (!purchase.isAcknowledged()) {
AcknowledgePurchaseParams acknowledgePurchaseParams =
AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
billingClient.acknowledgePurchase(acknowledgePurchaseParams, ackPurchase);
}
//else item is purchased and also acknowledged
else {
// Grant entitlement to the user on item purchase
// restart activity
if(!getPurchaseValueFromPref()){
savePurchaseValueToPref(true);
Toast.makeText(getApplicationContext(), "Item Purchased", Toast.LENGTH_SHORT).show();
this.recreate();
}
}
}
//if purchase is pending
else if( PRODUCT_ID.equals(purchase.getSku()) && purchase.getPurchaseState() == Purchase.PurchaseState.PENDING)
{
Toast.makeText(getApplicationContext(),
"Purchase is Pending. Please complete Transaction", Toast.LENGTH_SHORT).show();
}
//if purchase is unknown
else if(PRODUCT_ID.equals(purchase.getSku()) && purchase.getPurchaseState() == Purchase.PurchaseState.UNSPECIFIED_STATE)
{
savePurchaseValueToPref(false);
purchaseStatus.setText("Purchase Status : Not Purchased");
purchaseButton.setVisibility(View.VISIBLE);
Toast.makeText(getApplicationContext(), "Purchase Status Unknown", Toast.LENGTH_SHORT).show();
}
}
}
AcknowledgePurchaseResponseListener ackPurchase = new AcknowledgePurchaseResponseListener() {
@Override
public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
if(billingResult.getResponseCode()==BillingClient.BillingResponseCode.OK){
//if purchase is acknowledged
// Grant entitlement to the user. and restart activity
savePurchaseValueToPref(true);
Toast.makeText(getApplicationContext(), "Item Purchased", Toast.LENGTH_SHORT).show();
MainActivity.this.recreate();
}
}
};
/**
* Verifies that the purchase was signed correctly for this developer's public key.
* <p>Note: It's strongly recommended to perform such check on your backend since hackers can
* replace this method with "constant true" if they decompile/rebuild your app.
* </p>
*/
private boolean verifyValidSignature(String signedData, String signature) {
try {
// To get key go to Developer Console > Select your app > Development Tools > Services & APIs.
String base64Key = "Add Your Key Here";
return Security.verifyPurchase(base64Key, signedData, signature);
} catch (IOException e) {
return false;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if(billingClient!=null){
billingClient.endConnection();
}
}
}
Security.java
/*
* 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.programtown.example;
import android.text.TextUtils;
import android.util.Base64;
import java.io.IOException;
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.
*/
public class Security {
private static final String TAG = "IABUtil/Security";
private static final String KEY_FACTORY_ALGORITHM = "RSA";
private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
/**
* Verifies that the data was signed with the given signature, and returns the verified
* 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
* @throws IOException if encoding algorithm is not supported or key specification
* is invalid
*/
public static boolean verifyPurchase(String base64PublicKey, String signedData,
String signature) throws IOException {
if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey)
|| TextUtils.isEmpty(signature)) {
//Purchase verification failed: missing data
return false;
}
PublicKey key = generatePublicKey(base64PublicKey);
return verify(key, signedData, signature);
}
/**
* Generates a PublicKey instance from a string containing the Base64-encoded public key.
*
* @param encodedPublicKey Base64-encoded public key
* @throws IOException if encoding algorithm is not supported or key specification
* is invalid
*/
public static PublicKey generatePublicKey(String encodedPublicKey) throws IOException {
try {
byte[] decodedKey = Base64.decode(encodedPublicKey, Base64.DEFAULT);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
} catch (NoSuchAlgorithmException e) {
// "RSA" is guaranteed to be available.
throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
String msg = "Invalid key specification: " + e;
throw new IOException(msg);
}
}
/**
* 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
*/
public static boolean verify(PublicKey publicKey, String signedData, String signature) {
byte[] signatureBytes;
try {
signatureBytes = Base64.decode(signature, Base64.DEFAULT);
} catch (IllegalArgumentException e) {
//Base64 decoding failed
return false;
}
try {
Signature signatureAlgorithm = Signature.getInstance(SIGNATURE_ALGORITHM);
signatureAlgorithm.initVerify(publicKey);
signatureAlgorithm.update(signedData.getBytes());
if (!signatureAlgorithm.verify(signatureBytes)) {
//Signature verification failed
return false;
}
return true;
} catch (NoSuchAlgorithmException e) {
// "RSA" is guaranteed to be available
throw new RuntimeException(e);
} catch (InvalidKeyException e) {
//Invalid key specification
} catch (SignatureException e) {
//Signature exception
}
return false;
}
}
6 Run and Test the application
Now Run your app and test in app purchase by using your gmail account that is associated with your Google Console developer account. Because by default, the only test account registered is the one that’s associated with your developer account. You can register additional test accounts by using the Google Play Console.





Conclusion
So in this article we have learnt how to integrate and make in app purchase of one time product. If you liked the article then please share this page and article. Thanks.
NOTE:
Need Support?
References:
https://developer.android.com/google/play/billing/billing_overview
https://developer.android.com/google/play/billing/billing_onetime
Please Subscribe Youtube| Like Facebook | Follow Twitter
Hello, I have been using this code for two years. Now I got an email that I need to use library 5.0.0. The problem is that I can’t use the old code getPurchasesList queryPurchase PurchasesResult getSku I can’t use them. Will it be a problem if I stick with the old version 3? Thanks.
@Ivanov
yes you need to update it to latest version v5 because version 3 old now and not supported
You can get it from here but it is paid
https://programtown.com/android-google-in-app-purchase-subscription-download-example-source-code-google-play-billing-library-5-0-java-kotlin/
Hello, I have been using this code for two years. Now I got an email that I need to use library 5.0.0. The problem is that I can’t use the old code getPurchasesList queryPurchase PurchasesResult getSku I can’t use them. Will it be a problem if I stick with the old version 3? Thanks
yes you need to update it to latest version v5 because version 3 old now and not supported
You can get it from here but it is paid
https://programtown.com/android-google-in-app-purchase-subscription-download-example-source-code-google-play-billing-library-5-0-java-kotlin/
Firstly, thank you for this article that really help me on setup Google Billing.
During test, I successfully buy the product for user-1 , and try to switch the google account to user-2. When run the application first time, the apps check and return status that item is purchased. I just switch to user-2, so the expected status should be “not purchase”.
In debug @ handlePurchases(),
the purchase.getPurchaseState() == 1 ,
but purchase.getOriginalJson(); key purchaseState == 0
Can you highlight what is happened here? and how to fix the issue?
Thank you
can you rerun the app again?
first time when user 1 purchase the item
then preference will be set to true after successful purchase.
After switching to user 2 google play will return no purchase.
So after that false will be stored in preference.
Therefore when you will run app again 3rd time then purchase status for user 2 will be
not purchased.
thanks
Thanks for this.
Given the present Billing Library V4, some parts of the code appears deprecated such as queryPurchases. Please we’ll be glad if you can edit this to reflect the present library.
Thanks
I have not done yet for version 4.
you can book order for personal support and implementation with latest library via Fiver.
https://www.fiverr.com/share/NEW6eN
Thanks
Dear, Thanks for your tutorial, But i’m facing a issue. after subscription successful not open premium content. I think issue is with shared preferences. Can you help me to solve this issue ?
you can book order for personal support and implementation with latest library via Fiver.
https://www.fiverr.com/share/NEW6eN
Thanks
How can we check for already purchased item ?. I need this code for restore functionality. By the way your code is awesome, I am searching from last month for billing library 4.0.0 and finally got your tutorial.
here it is
//if item already purchased then check and reflect changes
else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
And
I have not done yet for version 4.
you can book order for personal support and implementation with latest library via Fiver.
https://www.fiverr.com/share/NEW6eN
Thanks
Hey your code is extremely good, i have question about when i can add data on backend as well after purchase the item, in old code i add when “IabHelper.OnConsumeFinishedListener” in these method i make function like these
“addOrder(price, “inapp”, “paid”, purchase.getOrderId(), “”, currency_code);”
so in new code where can i add these line for add data like these after item purchase
Thank you!
here i think you can get purchase infoe inside purchase object
if ( purchase.getSkus().contains(PRODUCT_ID) && purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED)
//purchased object
if (!verifyValidSignature(purchase.getOriginalJson(), purchase.getSignature())) {
// Invalid purchase
// show error to user
Toast.makeText(getApplicationContext(), “Error : Invalid Purchase”, Toast.LENGTH_SHORT).show();
return;
}
Purchase is shown successful but verification side problem.
my Code stops here Giving Toast “Error : Invalid Purchase”, can you help me out this.
please see keys for signature in developer console
please see keys for signature in developer console
However you can book order for personal support and implementation with latest library via Fiver.
https://www.fiverr.com/share/NEW6eN
Thanks
Hi.
Did you find any solution
Hey, thank you very much. I was wondering if we can use Preferences DataStore instead of SharedPrefences for storing purchases?
yes you can.
Thanks, This guidance is really helpful.
I’ve got a question.
Using your method, I got 10 Order, but only one of them successfull. I got these messages for the unseccssfull payment “There was an issue charging the customer’s payment method ~ Cancelling”.
Any suggesting, Thanks before
Ps: Sorry if my english is bad
it means customer/user was unable to process payment. it is issue on customer side related to payment methods.
thanks
Hello: I changed the test device android 6.0 to android 8.0 device, it can work normally, if anyone encounters the same problem, you can refer to it ! Thank you !
Sorry, I guess my question was replied in your April 12 message. Thanks.
Thanks for the tutorial, which provides a step-to-step guidance for implementation of in-app purchase. It is really helpful. I have a question regarding testing and debugging. I am currently working with Android studio. Can I test it with android studio, or I have to do it in Google Play Console. If it must be the latter, do I have to run the code in signed release APK? Thanks.
Yes you can test it with android studio. But first you have to release signed apk in internal test track in play console with billing permission added in manifest.
thanks
Hello, thank for your tutorial, It work for me.
But I still have a problem,
I do a project with many activity and ad, I hope when I buy product, ad will disappear.
so ,I have to get record form billingclient,
In other page ,how can I get billingclient record?
or I have to set a global SharedPreferences to get value?
thanks.
yes through SharedPreferences .
On every app start get record from billingclient and save/update in SharedPreferences then in every activtity check status from SharedPreferences.
thanks
thanks
Hello
Great tutorial. I had been trying for months to integrate inApp purchases and couldn’t get it to work. The Android documentation was driving me crazy. great job!
Let’s see, I followed step by step the tutorial and testing in the emulator with the sku “android.test.purchased” everything works great, consumable and non-consumable purchases. So I decided to upload to private the apk signed with my id’s and my key to be able to do a test.
I have the different products configured and activated, I have the apk signed and published in private, I have a list of testers and one of the emails is my own developer email.
I downloaded the apk from the store and it’s been a week since then, and I always get the same message “Error the item you requested is not for sale”.
I don’t know what I might be missing. I would appreciate your help
Regards
**Apologies for my English
You need to make sure:
* Make sure to upload the signed APK to developer console.
* Make sure to install the signed APK on your device not launch the app in the debugger.
* Make sure to create a test account in your developer console.
* Setup you testing account
* Make sure to sign in your device with your test account.
* In a case of closed alpha/beta testing, make sure you have added your test account to selected testers group, you can do this on the page of management your alpha/beta version.
* In a case of closed alpha/beta testing, make sure your testing account have accepted participation in testing of this application via special invite link
* Make sure to create in app billing in your developer console and finally activate the item from the console!!! (this is the one that got me after fully following google’s tutorial)
* Make sure to set VersionCode and VersionName in the manifest to be the same as the version in the developer console (Alpha, Beta or Production. Drafts does not work anymore).
* Make sure product id in play console and product id in your code is same and matched.
* Make sure to clear data and cache of your playstore app on your phone to see if fixed. If not then wait for one day for item to be available
reference: https://stackoverflow.com/questions/13117081/the-item-you-requested-is-not-available-for-purchase
Hello
thanks for your answer, I have gone through everything you comment and it is apparently all ok, but I keep getting null in “onPurchasesUpdated” in the list. If I run the code with the test id (android.test.purchased) everything goes perfectly, but when I put my IDs I always get the same, null the message of “this article is not for sale”
don’t know what else I can do. I have configured the id in my console and they are active, the apk is published in the test channel. I have installed from Google Play with the qr that the console gives me …
I would appreciate any other indication
Thank you
I have installed from Google Play with the qr that the console gives me ? You can use app from android studio directly by clicking on run in android studio and test your app in app purchase.
and make sure version code of your published signed apk is same as currently used in source code for testing app.
furthermore billing permission added.
And finally you are using same gmail account that is used for your play console account.
thanks
Hey thanks for this great tutorial!
One question…
In onBillingSetupFinished when I call billingClient.queryPurchases I always get null. Anything else works just fine. I can purchase and the text shows purchased. After the next start of the App queryPurchases gives me null value and I have to purchase again. But then I get an error showing “you already own this product”. This happens on my developer devices. Any advice what I can look for?
or may be there is issue with acknowledgment of purchase. please review your code.
Thank you so much for this IAP tutorials!
But I have a problem with this approach.. getPurchasesList always returns null.
When I first purchase a product everything works fine. But when onCreate is being called the next time after the purchase getPurchasesList returns null even if the purchase was successful. Then I try to purchase again and get “you already own this item”-Error.
When does getPurchasesList return NULL?
if product is consumable then after purchase it will be consumed and purchase will be empty/null and in case of non consumable you need to make sure you have acknowledged it properly.
please check your developer’s public key of app is correct.
And getPurchasesList returns null when you have purchased consumable product or you have not bought anything yet.
hi, when using lucky patcher the purchase getting cracked, so could you please update this tutorial for verify in server.
I do not have tried server side verification.
To prevent purchase/subscription from hacking we can use following measure
1. perform client side verification which is mentioned in video.
2. perform server side verification, see info here https://developer.android.com/google/play/billing/security
However above two step still not guaranteed for 100% security because hacker can reverse engineer your apk and bypass verification check then redistribute it, unless your app is depend on online service where only authorized users get your content by performing authentication check at your own backend server.
Moreover you can also more secure your apk from reverse engineering by performing obfuscation on your apk (allows you to make it difficult for hackers to modify/crack your apk).
see here
https://programtown.com/how-to-shrink-obfuscate-and-optimize-android-apk/
thank you
Thanks so much it helps a lot!
I was wondering, how do you get the price set online so that I can display it on the purchase button?
yes look in to SkuDetails object and getPrice() method
https://developer.android.com/reference/com/android/billingclient/api/SkuDetails
Please I keep getting this response email
(This test purchase was cancelled because it was not acknowledged. You should ensure all purchases are acknowledged, so they are not subject to refunds)
please any help or I’m doing something wrong
Pls I have seen my error thanks great Tuta
Great And please subscribe our youtube channel
Thanks
Sir it is working on 30 sdk or not
it will work. please comment if any issue arises.
thanks
hi, i’m experiencing the same. Assist
sir, you are great man, finally i can do it, thank you very much,
welcome 🙂
I have a problems with part of code (when app starts) which checks the purchase status.
I always get (even after properly buying and acknowledging purchase) DEVELOPER_ERROR whenever calling:
Purchase.PurchasesResult alreadyPurchasedResult = billingClient.queryPurchases(item.getSkuId());
Why?
DEVELOPER_ERROR
Invalid arguments provided to the API. This error can also indicate that the application was not correctly signed or properly set up for In-app Billing in Google Play, or does not have the necessary permissions in its manifest.
Also check this link
And this link
Actually I solved it – it was fault on my side. As a parameter of queryPurchases should NOT be skuID as I was using, but BillingClient.SkuType.INAPP/SUBS. Such a rookie mistake, but took me couple hours to figure out 🙂
great !
Hello,
I was trying this article . But I am getting error:
“Purchase Item not Found”
It looks like that onSkuDetailsResponse always returns an empty list although I have created a product ID named ‘purchase’ in the play console.
Could you please help me fix this?
Below are different ways of fixing it.
Please try to clear data and cache of your playstore app on your phone to see if fixed
If not then wait for one day for item to be available
Check Here For info
https://stackoverflow.com/questions/62320895/android-google-pay-billing-response-4-item-unavailable
Furthermore make sure app is uploaded on playstore in any track with billing permission added and product id in your Play console is same as product id used in your app project.
Thanks
I am getting “Purchase Item not Found”
No error message appears, I press the button and nothing happens. The application is published in Google Play and tested with a real phone.
I fixed the problem, I had not entered the key Base64.
But now something else happened, I created a new product ID, and the price window does not open
What error message you are receiving?
Please make sure product id in playstore and in your app is same.
Furthermore
Please test with real phone and add billing permission in manifest and upload signed apk with new version code in playstore in test track.
And then wait when your app is published in test track then test it.
please check below link for further troubleshoot.
https://stackoverflow.com/questions/11068686/this-version-of-the-application-is-not-configured-for-billing-through-google-pla
Thanks
No error message appears, I press the button and nothing happens. The application is published in Google Play and tested with a real phone
Please make sure your click event working.
Review your code. If there was something wrong with in app purchase code then there must be error message in toast.
I did everything as described and it works, but after 5 minutes the test purchase is canceled and I don’t know why. “This test purchase was canceled because it was not confirmed”.
Are you selecting test card always decline
Option during purchase dailog?
See here
https://developer.android.com/images/google/play/billing/test-payment-methods.png
I did everything as described and it works, but after 5 minutes the test purchase is canceled and I don’t know why.
“This test purchase was canceled because it was not confirmed”.
void handlePurchases(List purchases) {
for(Purchase purchase:purchases) {
//if item is purchased
if (PRODUCT_ID.equals(purchase.getSku()) && purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED)
{
if (!verifyValidSignature(purchase.getOriginalJson(), purchase.getSignature())) {
// Invalid purchase
// show error to user
Toast.makeText(getApplicationContext(), “Error : Invalid Purchase”, Toast.LENGTH_SHORT).show();
return;
}
// else purchase is valid
//if item is purchased and not acknowledged
if (!purchase.isAcknowledged()) {
AcknowledgePurchaseParams acknowledgePurchaseParams =
AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
billingClient.acknowledgePurchase(acknowledgePurchaseParams, ackPurchase);
}
//else item is purchased and also acknowledged
else {
// Grant entitlement to the user on item purchase
// restart activity
if(!getPurchaseValueFromPref()){
savePurchaseValueToPref(true);
Toast.makeText(getApplicationContext(), “Item Purchased”, Toast.LENGTH_SHORT).show();
this.recreate();
}
}
}
Hello Sir,
I have a problem. When I clicked Purchase button , it says ” This version of the application is not configured for billing through Google Play”.
I followed instructions and applied everything you did.
What should I do to fix this? Can you help me? Thanks.
Hi
Please test with real phone and add billing permission in manifest and upload signed apk with new version code in playstore in test track.
And then wait when your app is published in test track then test it.
please check below link for further troubleshoot.
Click here
Thanks
Thank you for your help. I fixed the problem by doing internal test with signed APK.
Great and welcome and please share this site and subscribe our Youtube channel.
Hey I have another problem. I want to add a few more non-consumable purchase items to my activity. Which code sections should I duplicate and change?
By the way, I found this website via your youtube video, you are very helpful.
Thanks you
For multple non consumable items
Follow this article
https://programtown.com/how-to-make-multiple-in-app-purchase-in-android-using-google-play-billing-library/
Hello, first thank you very much for the tutorial, it helped me a lot, I was racking my brain with code that I was building from scratch and when I saw this tutorial I managed to see my errors. I have a question about consumable products, I changed my code to the one below, but when I test it, it shows that I already bought the product, what can it be? Kind regards.
void handlePurchases(List purchases) {
for(Purchase purchase:purchases) {
ConsumeParams consumeParams =
ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
billingClient.consumeAsync(consumeParams, listener);
}
}
Thank you
You are reading article of Single Non-consumable one-time product.(that allows user to purchase product only once)
For Single Consumable one-time product (that allows user to purchase single product again ) (Follow this link)
For Multiple Consumable one-time product (that allows user to purchase multiple products again ) (Follow this link)
When product is bought for consumable product then you need to consume it after purchasing it so that product can be bought again.
You can look up above tutorials if any help needed then feel free to ask.
Further more in your code. You are consuming every product regardless of its status i.e purchased, pending, unspecified.
You need to consume only those product which has been purchased. Then you have to consume it after purchase. My mentioned
links clearly explain how to perform purchase of single and multiple Consumable products.
thanks
Thank you very much, I did your tutorial and it worked. I am working dynamically. Taking items from an api and the user consuming in real time.
Great
Please share this article and website
Thanks
Hi ProgramTown for your very useful tutorial,actually this is only broadly description based tutorial for this purpose.
You posted for non consumable and subscription based tutorial as i learned both using acknowlegePurchase.
Where i can find out similar tutorial for consumable item because after using consumeAsyc i found out purchase cancelled email from Google play for not doing acknowlegde. So it makes me confused what to do.
And current version is 3.0.0 what i am using.
So Just change AcknowledgePurchaseResponseListener to ConsumeResponseListener for consuming as well as
acknowledging one time consumable product. Reply if you have any issues?
Thank you Sir for your reply.
You used AcknowledgePurchaseListener once after checking purchase acknowleged or not. So here the replacement
if (!purchase.isAcknowledged()) {
ConsumeParams consumeParams = ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
ConsumeResponseListener listener = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// Toast.makeText(PurchaseWindow.this,”Successfully purchased and Acknowleged”,Toast.LENGTH_LONG).show();
}
}
};
billingClient.consumeAsync(consumeParams, listener);
}
Please correct me if i did any wrong.
i put ConsumeResponselistener within handlepurchase() as i followed google doc.
I also wanna know which purchase item pass this toast and customer may get OrderId too,it may handy when multiple items are in pending and acknowledged one by one,
ConsumeResponseListener listener = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
Toast.makeText(PurchaseWindow.this,"Successful And Acknowledged : "+purchase.getSku()+"\n"+purchase.getOrderId(),Toast.LENGTH_LONG).show();
}
}
};
Hi! Great tutorial!
Do I have to run AcknowledgePurchaseResponseListener and ConsumeResponseListener both for consummables?
I’m having trouble with BillingHelper. It keeps showing me in error and prompting me to create a class
In this article google play billing library version ‘com.android.billingclient:billing:2.2.0’
is used which includes BillingHelper Class.
In latest library BillingHelper Class is removed thats why you are facing issue.
BillingHelper Class (import com.android.billingclient.util.BillingHelper)
is only used in Security.java class.
Which is simply printing warning log using
i.e BillingHelper.logWarn(TAG, “Purchase verification failed: missing data.”);
So simply Either remove
import com.android.billingclient.util.BillingHelper
and BillingHelper.logWarn() method calls from Security.java class in your project
OR use google play billing library version ‘com.android.billingclient:billing:2.2.0’
Thank you for the advice. It helped me out.
Welcome
Hello there, it’s me again. I followed your tutorial and it has worked perfectly, but now I want to add more one time products. I have 31 of them, so how do I got about it using this tutorial?
Code:
Thank you for the response. Will I have to replace all the code form this tutuorial or just add onto it?
Thank you for the code. I’ve implemented the billing, but I encountered a few issues.
I keep getting an error:
incompatible types: Object cannot be converted to Purchase
for(Purchase p:queryPurchases){
It’s given me errors on 2 of these lines.
savePurchaseItemValueToPref(purchaseItemIDs.get(index),true);
What did I do wrong?
Fixed my problems. The only issue I have left is this.
Raw use of parameterized class ‘ArrayList’
Oh, do I also have to add a file for the product IDs or is should a just use the ones I put in the code?
Sorry If I’m asking too many questions. I’m new to in app billing.
Sorry for this again, but for this:
public static final String PRODUCT_ID= “purchase”;
Should I leave it as “purchase” or do I add more product IDs?
Note: I had to create a database for my images after the last time we emailed, because they would’ve significantly increased app size.
I understand. Well my products(images) are stored in a Firebase Realtime database server, but I got them to show on a recyclerview. I’ve already setup an OnClick event for each item, so I don’t need to make 31 buttons right?
Also from what I understand about the public void purchase1(View view) method, I’ll have to make 31 one of those methods right?
Thank you again. The reason I’m using a database is because I don’t know how to right the code to download images from app drawable, so that users can download the image once purchased. Plus my app is currently 10MB in size and the images are 27Mb in size altogether, but I might change my strategy again, because your suggestion sounds a lot easier.
Well I think I’ve asked every question I could’ve for now. If I have issues I’ll come back, but I’ll also tell you when this finally works.
Thank you for your help once again
Yes. That’s why I did it originally, but I’ve been struggling to implement this with in-app billing. I think I might go with storing the images in the drawable for now, because I do want to earn a living, but I’ll switch to the database later on when my user downloads increase.
Oh I also wanted to ask how to turn this testing in app billing into a live in app billing?
Simple.
Well I’m going to try my code now and see what happens.
Thank you
Hey there. I just wanted to tell you that I’ve found a better to sell my images. Now I’m telling you this, because I wanted to make sure that what I do what give me or my users issues.
So now instead of making a recyclerview where users can individually purchase items with a button click.
I’ve decided to turn the recyclerview into a list of packages that includes multiple images. When they click this package it take them to an activity that displays the images and there that’s where they can buy those images.
Now I was thinking that I would create Product IDs with the package name, that the purchase is actually for the package and not the image itself and once they purchase the package a download button appears and they are able to download them.
I’m planning on using your original tutorial and just make multiple activities for those packages. It would be an issue, because I won’t have too many packages.
I’m confident it’ll work, but I just want your feedback on things that I should or should not do, because it would give me and users a problem.
thats ok
Oh I also wanted to ask if I need to make a Security class for all activities or just use one?
Hey man. I’m suddenly having trouble with my in app billing. I published my app on an Internal testing track and tried to test it, but I keep getting a “Item not found” dialog, when I check the Pre-launch report devices, the billing client works perfectly.
I want to be able to test purchase, because a different button is supposed to show up when they complete purchases, but I can’t know if this works, because the billing flow doesn’t appear. Plus for some reason the testers don’t buy the products even though it’s a test.
Thank you
The billing works. Thank you, but now I have another problem.
I have 4 in-app products which can be purchased in 4 different activities. Now when I purchase one product from one activity the other 3 three show that they’ve also been purchased.
check product ids in your google play console and in your 4 activities that they are same or not.
Oh. I understand now.
So I should also add the IDs after here
private void initiatePurchase() {
List skuList = new ArrayList();
skuList.add(“PRODUCT_ID”);
and what do I replace the “PRODUCT_ID” string with, because now I’m receiving this:
Cannot resolve symbol ‘PRODUCT_ID’
Thank you. Once again sorry for asking too many questions
Thank You Brother!
Welcome dear
One more doubt is that can I test the purchase in an internal test track? I have read that purchases test can be done only on closed/open test tracks. Please let me know this as well. TIA
Yes you can test in app purchase item in internal test track. To test in app purchase item in your app, you first need to Integrate Google Play Library and add billing permission in your app and then upload that apk on Google play console and publish it any track (Internal test track (recommended), Closed track, Open track, Production track). I have already mentioned it at Note section after Requirements heading.
Got it. Thank you
Hi, a wonderful post. Thank you. Is it the same code for the subscription as well? Should I just make INAPP to SUBS?
Thank you, this code is for inapp purchase and I will post article for subscription soon.
That would be really helpful to me. Can i know how soon it will be posted?
Hopefully in a day, I will mail you once posted.
Article regarding In App Purchase Subscription is posted here.
https://programtown.com/how-to-make-in-app-purchase-subscription-in-android-using-google-play-billing-library/
Thanks, This guidance is really helpful.
I’ve got a question.
What is the parameter ‘View view’ for in the definition of
method purchase…”public void purchase(View view)”
It looks no need to pass that argument.
Is there any reason to use it?
I’ve just found that purchaseButton in .xml file has the attribute of ‘android:onClick=”purchase”‘.
Sorry for my rash question.
no problem @Seon , please let me know if there is any other query.
Thanks for the step by step guidance.
In section 5 you write to include the Billing library and permission. If your guide is followed step by step the reader would already added it in section 1. So no need to do it again.
I would also refer where you have that “Security.java” class from. I assume it’s from the Android billing sample project here: https://github.com/android/play-billing-samples/blob/551a178e52baf60cc6e1f9cb6f40767b8453655a/TrivialDrive/app/src/main/java/com/example/android/trivialdrivesample/util/Security.java
Thanks for pointing out.
I have repeated in section 5 because some times users directly jump to code section, when they are looking for solution quickly . Now I have created anchor link as remainder if they have skipped section 1.
For “Security.java” class, license info is included with in class file. By the way now I have added source url in reference section as well.