In-app purchase in Android using Ionic 4

In this post, you’ll learn how to implement the famous (and infamous) In-App Purchase functionality in Android apps using Ionic 4. 

Complete code of this post can be found in the Github repository ionic-4-in-app-purchase (master branch)

What is In-app purchase, again ? 

In-App Purchase is the functionality that lets you purchase items from within your mobile apps. These are different from other payment gateways in the sense that the payment is taken care of by Google Play store or Apple App store itself. These one-time purchases are money in the bank for app developers, while the app store also takes its share.

Types of In-app purchase

There are four types of products / purchases available using In-app purchase method

  1. Consumable — Consumable allow the user to buy a certain item once. Often used in game apps, one-time in-app purchases can give you added functionality like extra characters, extra life, new powers etc. This purchase can be done again and again, e.g. You can buy more and more lives for your game character.
    E.g. Buying a life in Candy Crush ?
  2. Non-Consumable — This type of product can be bought only once, and it never expires. This can be used for something like a premium membership in the app.
    E.g. Getting a premium membership of an app for lifetime
  3. Non-renewable Subscriptions — Subscription is different from one time payment, it is supposed to run for a period of time and then it expires. Non-renewable Subscription expires after a set period of time and does not renew automatically.
    E.g. Premium membership of Netflix (Did they become auto-renewable ? I need to check my credit card bills)
  4. Auto-renewable Subscriptions — Auto-renewable Subscription expires after a set period of time and renews automatically. This is the most preferred choice for subscription based apps. But this option also faces the maximum scrutiny from app store reviews.
    E.g. Premium membership of Tinder

Why is In-app purchase so important ?

In-app purchases can give you major advantages in terms of sales. Following are few reason why In-app purchases are so important

  • Creative Control — While most of the payment gateways require you to have the payment UI a certain way, In-app purchases allows you to be as creative as you want in terms of UI and UX. Because everything is integrated inside the app itself, the payment becomes a part of the app, rather than a third-party service.
  • Freemium model — You allow the user to “try” the free version before investing even a few cents into the rest of your app and when they are convinced to upgrade no additional trip into the app store is required. This way your user engagement increases, and more users convert to paid ones.
  • Less user interaction, better UX — In-app purchase only takes a single tap. Sending the user to the App/Play Store to buy other products requires at least 2 or more taps. Even more if you integrate payment gateways in the app
  • Allows variety of options — As we discussed above, the four types of in-app purchase products can provide a wide variety of product and add-on experience for your users. Creating the same experience using a third-party payment gateway can be cumbersome for developers
  • Reliable Google and Apple ecosystem — Since the in-app purchase is offered by Google and Apple themselves, it can be assumed to be of highest quality. Any app containing in-app purchase is reviewed very closely before being allowed to published. So users can rest assured that these apps are least risky in terms of payment issues etc. 
  • Regularly updated — Since the in-app purchase is an App/Play store feature itself, it can be safely assumed that these functionalities will be regularly updated by the platforms as iOS and Android versions change. While the same cannot be said about third-party services, which struggle to update when a new version of Android / iOS is released in market

What is Ionic 4?

You probably already know about Ionic, but I’m putting it here just for the sake of beginners. Ionic is a complete open-source SDK for hybrid mobile app development created by Max Lynch, Ben Sperry and Adam Bradley of Drifty Co. in 2013. Ionic provides tools and services for developing hybrid mobile apps using Web technologies like CSS, HTML5, and Sass. Apps can be built with these Web technologies and then distributed through native app stores to be installed on devices by leveraging Cordova.

So, in other words — If you create Native apps in Android, you code in Java. If you create Native apps in iOS, you code in Obj-C or Swift. Both of these are powerful but complex languages. With Cordova (and Ionic) you can write a single piece of code for your app that can run on both iOS and Android (and windows!), that too with the simplicity of HTML, CSS, and JS.

Structure of the post

I will go ahead in step-by-step fashion so you can follow easily

  1. Setup your app in Android Play Store
  2. Create a basic Ionic 4 app 
  3. Upload the app on store
  4. Setup In-app purchasing testing on Play Store
  5. Setup the In-App purchase plugin
  6. Test the app on android device
  7. Keep in mind ! 
  8. Subscriptions and Receipt validations

Step 1 — Setup your app in Android Play Store

First of all, you will need to become an Android developer on Google Play. This requires a membership, worth USD25 , and it never expires (unlike Apple’s one year membership). Go to https://play.google.com/apps/publish. Without the membership, you’ll see this


Register for a Google Play Developer membership

Register for a Google Play Developer membership

After registration and fee payment, you’ll go to your dashboard that look like this. I have a list of apps made from before. 


Google Play Console dashboard

Google Play Console dashboard

Create a new app from the button on left-top. Fill the basic details as shown below and you’ll see your app created in the list of apps


Fill basic details of the app to create the app draft

Fill basic details of the app to create the app draft

Though, you’ll have to fill a lot of other details. See the gray checkbox in few of the options in the left hand panel ? Those all need to go green before you can publish your first app, or even an alpha/beta version for testing.


All the checkbox options need to go green (completed) before app can be tested or published

All the checkbox options need to go green (completed) before app can be tested or published

Get you Billing Key

For in-app purchase related operations in Android app, you will need Billing Key from Play Store. 

Go to your app page → Development Tools → Services & APIs, and pick your Billing key from there, and save it. We’ll use it later


Get your Billing key from Google Play console

Get your Billing key from Google Play console

2 — Create a basic Ionic 4 app

I have covered this topic in detail in this blog.

In short, the steps you need to take here are

  • Make sure you have node installed in the system (V10.15.3 at the time of this blog post)
  • Install ionic cli using npm (my Ionic version is 4.7.4 currently)
  • Create an Ionic app using ionic start

You can create a blank starter for the sake of this tutorial. On running ionic start ionic-4-in-app-purchase blank , node modules will be installed. Once the installation is done, run your app on browser using

$ ionic serve

The app will launch on browser. You can go to Inspect → Device Mode to see the code in a mobile layout. You can create a basic layout for In-app purchase, or you can just use a simple button for all actions, doesn’t matter. All the real action will happen when we build the app for Android. Here’s how my layout looks like (layout taken from Woocommerce Ionic Starter)


Basic layout for in-app purchase sample app

Basic layout for in-app purchase sample app

Step 3 - Upload the app on store

Create a release build of your app to upload it on Play Store. Don’t worry, you’ll only need to publish in Alpha or Beta testing. The app won’t go live for this. We’re uploading a build before anything else for two reasons

  • Play store does not allow to create in-app products through developer console until there is an APK uploaded
  • Once submitted, the APK can take up to one day to publish, even if it is in Beta or Alpha or Internal testing. 

Change App name and package name

Before creating a release APK, give a proper package name to the app in config.xml. By default, Ionic apps have package nameio.ionic.starter , but this is not unique for play store. So change it to something related to you, e.g. my app is com.enappd.ionicpurcahse . You can also change the display name in config.xml file


Change app name and package ID in config.xml

Change app name and package ID in config.xml

Create a release APK

Create an unsigned release APK simply by running 

$ ionic cordova build android --prod --release

Note: In some cases, developers have reported that creating an app with — prod flag results in in-app purchase not working. If you feel so, try creating the release with just --release flag. The resulting APK will be larger, but you can always update it with --prod later when in-app purchase works fine.

Sign your app for upload

To upload and publish an APK on Play store, you need to sign the app with a keystore signature. For that, you need a keystore file and jarsigner utility from JAVA (default in most systems)

  • Create the keystore
$ keytool -genkey -v -keystore release.keystore -alias ionicpurchase -storepass android -keypass android -keyalg RSA -validity 36500

where ionicpurchase is the alias, release.keystore is my keystore filename, and I’ve kept android password for both store and keystore password

  • Sign the APK using
$ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore KEYSTORE_FILE_LOCATION UNSIGNED_APK_LOCATION ionicpurchase

Replace KEYSTORE_FILE_LOCATION and UNSIGNED_APK_LOCATION with correct values

  • Zipalign your APK

Zip aligning is not a mandatory step, it just reduces the size of app and makes it faster for downloads (somehow). Change your directory to your android build tools directory e.g. for me it is …./Android/sdk/build-tools/28.0.3 and run

$ ./zipalign -v 4 /Users/abhijeetrathore/ionic-4-in-app-purchase/platforms/android/app/build/outputs/apk/release/app-release-unsigned.apk ~/Desktop/ionic-purchase.apk

Upload the app in Play store 

Go to your Play store dashboard, go to appdetails, fill in all information required to turn the gray checkboxes to green (as mentioned in Step 1) and then upload the APK to Internal testing track (and alpha testing track)


Add your APK to internal testing track, and alpha testing track

Add your APK to internal testing track, and alpha testing track

Once uploaded, proceed and publish the app to Internal testing and alpha testing. It will take few hours to a day for the app to publish. To check if the app is published, look for the published tag in the dropdown


Check if your app is published in Play Store

Check if your app is published in Play Store

Step 4 — Setup In-app purchasing testing on Play Store

Before starting in-app purchase implementation and testing the app, few things need to be done on Play store.

Create in-app purchase products

After a successful upload of APK with the in-app plugin, you can start adding in-app products in the Play store dashboard.

Go to app details in Play store → Store Presence → In-app products

Here, you’ll see three options

  • Managed Products — Fore consumables, non-consumables and non-renewing subscriptions
  • Subscriptions — For auto renewing subscriptions
  • Rewarded products — Rewarded products are earned by users in your app in return for viewing an ad.

We’ll create a CONSUMABLE as an example. So go to Managed Products and create a new product


Create a new managed product in play store console

Create a new managed product in play store console

Once you fill all details and save the product, it will appear in your managed products list


Managed products list in Google play console

Managed products list in Google play console

Note: Product ID is important as this will be used by the app to query Play store for validation of the product.

Create internal testers

You always want to test your app in Internal testing / Alpha test / Beta test track before publishing it to production. For Internal Testing and Alpha closed testing track, you’ll need to create Testers, and attach them to your Internal testing or Alpha testing track.

Create a Tester group by going to
App releases → Alpha testing → Manage . In the first card, create a new Tester group and select it once created


Create a new tester group for Alpha / Internal testing in Play Store console

Create a new tester group for Alpha / Internal testing in Play Store console

Select this tester group in both Internal testing and Alpha testing to enable testing.

Step 5— Setup the In-App purchase plugin in app

To implement In-app purchase functionality, we’ll use the plugin cordova-plugin-purchase . This is also available as Ionic Native. 

Install plugin

Install the plugin using

$ ionic cordova plugin add "https://github.com/j3k0/cordova-plugin-purchase.git#v9" --variable BILLING_KEY="<ANDROID_BILLING_KEY>"
$ npm install @ionic-native/in-app-purchase-2

Notice : You need to use ANDROID_BILLING_KEY from Step 1 when installing the plugin.

I am using the repository from Github directly, as this change is recently made and not available in npm

Once installed, include the module in your app.module.ts as well as in your page where you will implement in-app purchase

// app.module.ts
import { InAppPurchase2 } from '@ionic-native/in-app-purchase-2';...
providers: [ InAppPurchase2 ]
...

and in your cart.page.ts 

// cart.page.ts
import { InAppPurchase2 } from '@ionic-native/in-app-purchase-2';
...
constructor(public platform: Platform, private store: InAppPurchase2) {...}

Implement plugin methods

In-app purchase works broadly in four steps

  1. Check if platform and store is ready
  2. Register your product with product ID (we’ll create product in Play Store in Section 5). Register multiple products if you have.
  3. Register handles for a particular product’s purchase. These handles are event listeners, which will listen if the purchase was successful, failed etc.
  4. Purchase — If above steps go fine, you can start the purchase flow, which will be handled by Google’s native in-app purchase UI. This will ask for authentication on your phone before purchase

Let’s me first print out the cart.page.ts file here, and then we’ll discuss the methods


Let’s go through the code step-by-step

  1. Check for Platform ready — All store function will strictly run only inside Platform ready. If you don’t put your functions inside it, it can throw a plugin not installed error
  2. Register product — Registering a product basically validates the product with Play store if the product really exists, and if the type and ID of product is correct. Any purchase or refresh action should be performed only after registration
  3. Event handlers — There are several event handlers registered for different events of the purchase process. Let’s see the important ones
  • loaded: Called when product data is loaded from the store
  • updated: Called when any change occurred to a product
  • error: Called when an order failed. The `err` parameter is an IAPError object
  • approved: Called when a product order is approved
  • owned: Called when a non-consumable product or subscription is owned
  • cancelled: Called when a product order is cancelled by the user
  • refunded: Called when an order is refunded by the user
  • registered: Called when product has just been registered
  • valid: Called when the product details have been successfully loaded
  • invalid: Called when the product cannot be loaded from the store
  • initiated: Called when the purchase process has been initiated
  • finished: Called when the purchase process has completed.
  • expired: Called when validation find a subscription to be expired.

Each of these event can help you detect what the status of the purchase is. To understand the major events in the life-cycle, refer to the flow diagram below


In-app purchase flow

In-app purchase flow

4. Purchase product — The .order method starts the purchase process. This process will be successful only when the product is valid and successfully loaded on app start. On success, you’ll receive an object with product details. 

Once the transaction is approved, the product still isn’t owned: the store needs confirmation that the purchase was delivered before closing the transaction.

To confirm delivery, you’ll use the product.finish() method.

5. Restore purchase — In an app where you use in-app products, you need to check every time if the user has the ownership (has purchased) of the product or not. E.g. you may remove ads in the app based on whether the user has purchased a premium membership. 

This process is done by getting the details of the product after registering. If the user has purchased a particular product, the product will return owned: true in the details fetched by store.get('product_id') method. Accordingly you can take decision in the app.

Note: The product will return owned: true only for products other than CONSUMABLE . Consumable go back to valid state, and purchase can be verified by transaction details in product info

Step 6 — Test the app on android device

The major step, fingers crossed 🤞

First thing to understand —  An android app NEEDS to have a published version for proper functioning of in-app purchases. So you will HAVE to wait for first version to publish. After that, each new version gets updated in an hour or so. Better stick to Internal testing track, as it gets quicker updates than Alpha /Beta testing tracks.

Download as a Tester

Best way to test is to download the app from your opt-in URL once you are a tester. You can find the Opt-in url in your alpha testing / Internal testing page 



Download a test APK by running the opt-in url in browser

Remember, the opt-in URL will only allow those users to test who are added in the tester group. Once you click the opt-in url, you’ll go to a page that invites you to become a tester, look like following


Internal testing opt in screen for Google Play testers

Internal testing opt in screen for Google Play testers

Test the app

Once you download the APK, you can simply click the checkout button (in my UI) and that will call the checkout method of the cart.page.ts as mentioned in Step 5 above. The flow will look like follow



In-app purchase flow for Android app in internal testing

You need to have a payment method added with your Google account for this to work. I have used my credit card for this. 

How to test before APK gets approved

Since APK approval can take its own time, you can test a release APK with only --release flag. This will keep the console (debug) logs in your code, and will display this information in an Android Studio console. You can test limited functionality in Chrome inspect tool as well.

Since the app is not published in this case, you won’t be able to go through the purchase flow UI as shown in the images above, but you can receive the proper consoles of purchase success in Android Studio console. 

Making changes to app after first APK is published

Once you APK is published and you have a downloadable version, you need not upload every change as a new APK to store and wait for it to get published. For further changes and debugging, you can simply test a release APK with only --release flag, check the consoles in Android Studio, and upload the APK only once you are satisfied with the functionality. This saves a lot of time as you don’t have to upload every new version for every change. 

But it seems you still can’t make purchases with this APK. 

For this method to work, make sure you use the same package name in the release build you test on device as the package name give in Play store app

Detailed study of Store functions

We can now check how each method works in the flow. 

  • Register — When you register your product, and the product is successfully registered, you get a response like this from the plugin
[store.js] DEBUG: state: ionic_101 -> registered

Note : Even if you try to register a product not existing in Play console, it will return the same message. You will face error when trying to purchase such a product.

Just after register , you will receive only limited details of the product. Rest of the details will come when you refresh store



Product details received after register
  • Refresh — Refresh function refreshes the state of in-app products and fetches detailed information

Refresh function tells which product is valid and which is not

Refresh function tells which product is valid and which is not

> Refresh function fetches all the products from the store, and also reveals which registered product is valid and which is invalid, as shown in above console log.

> Refresh also fetches the purchase details that tells the app which products have been purchased. 

> In the app, this detailed information of products can be fetched by product.get() method. This information can be used to know the exact status of the product’s purchase. Following is the response of product.get('ionic_101) method after Refresh


Refresh function fetches the in-app product purchase details

> Calling the product.get() method for ionic_102 will return a valid: false in details.

  • Handlers — Registering the handlers in the app after purchase of a product triggers the approved event for me. In response, you get the same product info that we got from product.get('ionic_101') method, where we get the transaction info of the product as well. This can help us figure out the status of the product’s purchase.
    Also, the product info contains owned: falsebecause CONSUMABLES return to valid state after purchase, while others go to owned state
  • Purchase — Once a purchase is made successfully, you will get to know the success from the UI itself

Payment successful notification in Android in-app purchase

Payment successful notification in Android in-app purchase

But on the code level, there are few things happening →

  • Immediately after purchase (in the .then() of order() ), we receive the product data, but that is not the valid picture of the product status, so don’t use it for decision making. 
  • After this, updated() event is triggered, which provides following details of the product

Here, you can see the value of “transaction”: { “type”: “android-playstore” } . This shows that transaction was made. 

  • After this, the approved() event is triggered, which is a signal that the purchase is successful. The data received in this trigger is as follows

Notice, “state”: “initiated” has now changed to “state”: “approved” 

You can also see the transaction object containing the transaction info. This confirm that the purchase is made. You can save this information in your database for future usage. 

Un-finished Purchases

If your app wasn’t able to deliver the content, product.finish() won't be called.

Don’t worry, the approved event will be re-triggered the next time you call store.refresh(), which can very well be the next time the application starts. Pending transactions are persistent.

Few important points

  • When finished, a consumable product will get back to the VALID state, while other will enter the OWNED state. So don’t put a condition on owned: true for CONSUMABLE
  • Any error in the purchase process will bring a product back to the VALID state.
  •  During application startup, products may go instantly from REGISTERED to APPROVED or OWNED, for example if they are purchased non-consumables or non-expired subscriptions.
  • Non-Renewing Subscriptions are iOS products only. Please see the iOS Non Renewing Subscriptions documentation for a detailed explanation.

Step 7 — Keep in mind

In-app purchase is a difficult functionality to implement, especially because it’s difficult to get console logs in release builds. You can face several issues during the development. I faced a few during the blog development, and sharing them just in case you also face them

  1. Don’t try to debug the app in debug mode on Chrome inspect. You can do some functions here, but won’t be able to make purchases. This can lead you to believe that things are working, just the purchase isn’t working. Always user release build for this.
  2. You won’t see the Android in-app purchase default UI until the APK is published on one of the testing tracks. So wait for the APK to get published. This UI only appears in a release APK
  3. Don’t call the store functions only after Platform is ready 
  4. Don’t use the product description returned by register function, as it does not return all the details of the product. Only after refresh function, you will get the full description and valid state of products

Step 8 — Subscriptions and Receipt validations

All this trouble has been taken to test the in-app purchase consumables. Subscriptions work very similar way up to purchase, but a server receipt validation is mandatory for Subscriptions. 

When the receipt validator returns a store.PURCHASE_EXPIRED error code, the subscription will automatically loose its owned status.

Typically, you’ll enable and disable access to your content this way.

this.iap2.when("my_subcription").updated((product: IAPProduct) => {
if (product.owned)
// serve the app with subscription
else
// serve the app without subscription
});

For more details on Receipt validation, read thedocumentation provided by Ionic.

Conclusion

In this post we learnt how to implement the In-App purchase feature in Android apps. We also tested the app in and android device and understood what all the events and methods mean. 

It is a long a meticulous feature where a lot of things are required to be done correctly. But it’s a very stable feature, which once implemented correctly, will not cause you future troubles.

Complete code of this post can be found in the Github repository ionic-4-in-app-purchase (master branch)

Next Steps

Now that you have learned the implementation of In-app purchase in Ionic 4, you can also try

If you need a base to start your next Ionic 4 app, you can make your next awesome app using Ionic 4 Full App





Title
Subtitle
Kicker