A simple way to integrate in-app purchase with the Google Play Billing Library into your app - Subscription
Working on Android In-App Purchases for Google Play billing library with the latest version 5.0.0+
In this article, I'm going to show you how to integrate In-App Purchase for Subscription of Google Play Billing version 5+ in 7 steps. I follow the official google docs, I'm not using any third-party library.
Pre-requisite
- Google Play Console Account
- Published App on Play Store
- Tester Device with GMS
Configure Your Testing device by adding the Gmail account to internal testing testers
and License testing (Watch the YouTube video for clarity: https://youtu.be/j6wWVMj-fi8 )
Setup the in-app purchase subscription product in the Google Play Console account
I have already created mine which are
Product ID: sub_premium
The following methods (These are the methods you need for the IAP System to work, you can copy and paste)
void establishConnection(){}
void showProducts(){}
void launchPurchaseFlow(){}
void verifySubPayment(Purchase purchases){}
void checkSubscription(){}
Step 0: //Add the Google Play Billing Library dependency
Step 1: //Initialize a BillingClient with PurchasesUpdatedListener
Step 2: //Establish a connection to Google Play
Step 3: //Show products available to buy
Step 4: //Launch the purchase flow
Step 5: //Processing purchases / Verify Payment
Step 6: //Handling pending transactions
Step 7: //Check the subscriptions on SplashScreenActivity
Step 0: //Add the Google Play Billing Library dependency
//Add the Google Play Billing Library dependency to your app's build.gradle file as shown:
dependencies {
def billingVersion = "5.0.0"
implementation "com.android.billingclient:billing:$billingVersion"
}
And Open Manifest File and add this permission
Step 1: //Initialize a BillingClient with PurchasesUpdatedListener
//Initialize a BillingClient with PurchasesUpdatedListener onCreate method
billingClient = BillingClient.newBuilder(this)
.enablePendingPurchases()
.setListener(
new PurchasesUpdatedListener() {
@Override
public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List list) {
if(billingResult.getResponseCode()==BillingClient.BillingResponseCode.OK && list !=null) {
for (Purchase purchase: list){
verifySubPurchase(purchase);
}
}
}
}
).build();
//start the connection after initializing the billing client
establishConnection();
Step 2: //Establish a connection to Google Play
void establishConnection() {
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// The BillingClient is ready. You can query purchases here.
showProducts();
}
}
@Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
establishConnection();
}
});
}
Step 3: //Show products available to buy
@SuppressLint("SetTextI18n")
void showProducts() {
ImmutableList productList = ImmutableList.of(
//Product 1 = index is 0
QueryProductDetailsParams.Product.newBuilder()
.setProductId("sub_premium")
.setProductType(BillingClient.ProductType.SUBS)
.build(),
//Product 2 = index is 1
QueryProductDetailsParams.Product.newBuilder()
.setProductId("test_id_shar")
.setProductType(BillingClient.ProductType.SUBS)
.build()
);
QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()
.setProductList(productList)
.build();
billingClient.queryProductDetailsAsync(
params,
(billingResult, productDetailsList) -> {
// Process the result
for (ProductDetails productDetails : productDetailsList) {
if (productDetails.getProductId().equals("sub_premium")) {
List subDetails = productDetails.getSubscriptionOfferDetails();
assert subDetails != null;
Log.d("testOffer",subDetails.get(0).getOfferToken());
txt_price.setText(subDetails.get(0).getPricingPhases().getPricingPhaseList().get(0).getFormattedPrice()+" Per Month");
txt_price.setOnClickListener(view -> {
launchPurchaseFlow(productDetails);
});
}
if (productDetails.getProductId().equals("test_id_shar")) {
List subDetails = productDetails.getSubscriptionOfferDetails();
assert subDetails != null;
Log.d("testOffer",subDetails.get(1).getOfferToken());
offer_btn.setText(subDetails.get(1).getPricingPhases().getPricingPhaseList().get(0).getFormattedPrice()+" Per Month");
offer_btn.setOnClickListener(view -> {
launchPurchaseFlow(productDetails);
});
}
}
}
);
}
Step 4: //Launch the purchase flow
void launchPurchaseFlow(ProductDetails productDetails) {
assert productDetails.getSubscriptionOfferDetails() != null;
ImmutableList productDetailsParamsList =
ImmutableList.of(
BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.setOfferToken(productDetails.getSubscriptionOfferDetails().get(0).getOfferToken())
.build()
);
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.build();
BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
}
Step 5: //Processing purchases / Verify Payment
void verifySubPurchase(Purchase purchases) {
AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams
.newBuilder()
.setPurchaseToken(purchases.getPurchaseToken())
.build();
billingClient.acknowledgePurchase(acknowledgePurchaseParams, billingResult -> {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
//user prefs to set premium
Toast.makeText(StoreActivity.this, "You are a premium user now", Toast.LENGTH_SHORT).show();
//Setting premium to 1
// 1 - premium
// 0 - no premium
prefs.setPremium(1);
}
});
Log.d(TAG, "Purchase Token: " + purchases.getPurchaseToken());
Log.d(TAG, "Purchase Time: " + purchases.getPurchaseTime());
Log.d(TAG, "Purchase OrderID: " + purchases.getOrderId());
}
Step 6: //Handling pending transactions
protected void onResume() {
super.onResume();
billingClient.queryPurchasesAsync(
QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.SUBS).build(),
(billingResult, list) -> {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
for (Purchase purchase : list) {
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED && !purchase.isAcknowledged()) {
verifySubPurchase(purchase);
}
}
}
}
);
}
Step 7: //Check Subscription (This code goes to your Splash screen)
void checkSubscription(){
billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener((billingResult, list) -> {}).build();
final BillingClient finalBillingClient = billingClient;
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingServiceDisconnected() {
}
@Override
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
if(billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK){
finalBillingClient.queryPurchasesAsync(
QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.SUBS).build(), (billingResult1, list) -> {
if (billingResult1.getResponseCode() == BillingClient.BillingResponseCode.OK){
Log.d("testOffer",list.size() +" size");
if(list.size()>0){
prefs.setPremium(1); // set 1 to activate premium feature
int i = 0;
for (Purchase purchase: list){
//Here you can manage each product, if you have multiple subscription
Log.d("testOffer",purchase.getOriginalJson()); // Get to see the order information
Log.d("testOffer", " index" + i);
i++;
}
}else {
prefs.setPremium(0); // set 0 to de-activate premium feature
}
}
});
}
}
});
}
GitHub Repo: https://github.com/wdtheprovider/in-app-purchases-subscription