How to configure and code Android in-app purchases in React Native apps

Tasos Maroudas
Aug 22, 2018 · 7 min read
Image for post
Image for post
Image 1: Preparing for an in-app purchase

In-app purchases! A way, among others, to make money from your mobile application. Probably not the cheapest option out there, in terms of 3rd party commission, but it stands out as a native option for your apps.

Let’s get started

In order to make payments work there is a 2 step process we need to follow:

Let’s move on and see how to configure an in-app product of one time charge.

Open the Google Play Console

As we login with our account to Google Play Console, we choose the application we want to configure and from the vertical left menu we navigate to Store presence -> In-app products:

Image for post
Image for post
Image 2: In-app products screen for an app in Google Play dev’s console

In this app we have setup 3 products. Each product consists of a set of properties that we first need to configure. In order to setup the new product, we click the top right button called Create Managed Product. A new screen opens that allows us to add the new product as shown below:

Image for post
Image for post
Image 3a: Mandatory properties to be configured for an Android in-app product — part a
Image for post
Image for post
Image 3b: Mandatory properties to be configured for an Android in-app product — part b

As you can see the mandatory fields include: name, id, description, status and price:

  • We need to have a unique product id, so that Google knows which product we are referring to from within our app.
  • Status means if the product is eligible to be purchased or not. Inactive ones cannot be purchased. We turn this to active as soon as we start testing our products and before the app goes live.
  • The price is the net value (without VAT) that we want to charge. The basic price is set to the currency of the locale that your account is configured to. In our example we show a UK account and a net value of 2.87 GBP. On top of this price, Google will automatically add the country’s VAT, which is 20% for UK. If your app is available in other countries as well, you can choose whether to sell or not your products there and Google will automatically perform the conversions/calculations needed. If we take Finland as an example the net value of the product is converted to EUR (3.22 EUR), while the country’s VAT of 24% (0.77 EUR) is added automatically; the final price of the product becomes 3.99 EUR as shown in image 3b. From this amount, Google deducts 30% transaction fees and in some countries it deducts the VAT automatically as well.

And that’s it! We save our first product and we are ready to code it within our app!

Let’s code this product!

We are going to be using react-native-billing library. Installation steps are few and easy to follow, so you should be up and running with it very fast.

Retrieve the product’s price

The first thing we need to do within our app is to get the prices of our newly created product. Price is retrieved from Google Play and through an API call, as we just configured it over the previous steps. We can retrieve this information within componentDidMount method as shown below:

async componentDidMount() {
try {
// make sure the service is close before opening it
await InAppBilling.close();
await InAppBilling.open();
// product with Google Play id: gems.pack.500
const details = await InAppBilling.getProductDetails('gems.pack.500');
this.gemsPack.priceText = details.priceText;
} catch (error) {
// debug in device with the help of Alert component
} finally {
await InAppBilling.close();
}
}

Now let’s elaborate on what we see above:

First we need to open the connection with Google Play and when we are finished, we should close it. As an extra level of safety we should close the connection before each new call to Google Play, just to make sure we will not be stuck to a previous session.

Then we use the product id we created before, in order to identify the product we are requesting information for. Within the response, we get the product’s price in a form of text (i.e. ‘3.99 EUR’).

In case we encounter an error here, we will need to debug in a real device. Emulator does not work for real products.

Finally, pay attention to the fact that your screen will first render and the prices will return asynchronously on a second time. That being said, you either need to show a loading component until the API call returns or you need to retrieve the prices earlier in your app and pass them as props to the “Shop” screen.

The actual purchase

Now in order to purchase the gems package (or any other product), we need to execute the following function when the user clicks the “purchase” button:

buyGoogleProduct = async id => {
const response = await new Promise((resolve, reject) => {
let repurchaseTries = 0;
const maxRepurchaseTries = 2;
const buyInAppProduct = async () => {
try {
const purchase = await InAppBilling.purchase(id);
resolve(purchase);
} catch (error) {
if (error.message === 'Purchase or subscribe failed with error: 102') {
if (repurchaseTries >= maxRepurchaseTries) {
reject(new Error(`Failed to purchase ${id} after ${maxRepurchaseTries} retries.`));
} else {
repurchaseTries += 1;
buyInAppProduct();
}
} else if (error.message === 'Purchase or subscribe failed with error: 6') {
// Communicate to the user that the payment was declined
} else if (error.message === 'Purchase or subscribe failed with error: 1') {
// Communicate to the user that the payment was cancelled
}
}
};
buyInAppProduct();
});
let result;
if (response.purchaseState === 'PurchasedSuccessfully') {
result = response;
} else {
result = Promise.reject(new Error(response.purchaseState));
}
return result;
}

Here we create a custom promise, which we use to perform the real purchase and also handle all the known — and unknown — errors.

The actual purchase takes place in line:

const purchase = await InAppBilling.purchase(id);

and that is the moment where the user sees the native modal:

Image for post
Image for post
Image 4: Native Google purchase modal in Greek language

The rest of the code takes care of the error situations:

Create your custom APIs

In order to make use of the aforementioned method, you should create your own custom API call(s) and encapsulate it inside them. This needs to happen for 2 reasons:

Let’s see an example:

buyGems = async () => {
try {
// make sure the service is close before opening it
await InAppBilling.close();
await InAppBilling.open();
// perform the purchase
const purchase = await this.buyGoogleProduct('gems.pack.500');
// Parse Google Play response data and update BE for payment
// validation
const args = {
productName: 'PACK',
receiptData: purchase.receiptData.replace(/"/g, "'"),
receiptSignature: purchase.receiptSignature
};
const response = await buyGemsCustomAPI(args);
// update user gems in-app state
this.props.saveUser({gems: response.data.userBuyGems.gems});
// consume Android store product
await InAppBilling.consumePurchase(this.state.productId);

} catch (error) {
// If an error that is not handled occurs show a generic message
} finally {
// close connection
await InAppBilling.close();
}
}

We first invoke the buyGoogleProduct method by supplying the proper product id and then we invoke our own custom API method buyGemsCustomAPI. As arguments to this method, we provide an internal productName, along with the receiptData and the receiptSignature as received from API call response. The latter 2 values are needed for payment validation in our back end server.

Finally, we consume the purchase for the specific product type, so that we allow the user to buy again gems instantly (i.e. for monthly recurring service we should not consume the purchase until the month is over in order not to allow the user to re-purchase something that he has already paid for).

How do my sales look?

In order to see how many sales you are performing, open Google Play Console and before you choose an app, navigate from the vertical menu bar the “order management” option:

Image for post
Image for post
Image 5: Order management screen in Google Play Console

What do you think?

What do you think about this article and the proposed solution? Have you tried any other library for Android in-app purchases in RN apps? Feel free to offer your perspective and ideas to the comments section below.

If you enjoyed this article, feel free to hit that clap button 👏 to help others find it.

About me

Hi there, I’m Tasos; a software engineer that loves web and currently works a lot with React Native and React. I’m the co-founder of Coded Lines software agency where we undertake end-to-end mobile & web projects with emphasis to in-app marketing. If it sounds what you are looking for, please contact me here: tasos.maroudas@codedlines.com. Thanks for stopping by :)

___________________________________________________________________

Thanks

George Karboulonis for proof reading

React Native Training

Stories and tutorials for developers interested in React…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store