Editor’s note: This post was updated on 21 September 2023 to remove mention of the now-deprecated expo-in-app-purchases
library. It is now recommended that you use react-native-iap
or react-native-purchases
, both of which we discuss in this post.
Implementing in-app subscription purchases (IASP) is one of the key ways you can earn money from your mobile application. With IAP, you can easily monetize your app’s features, and content, and even offer digital products for sale.
React Native offers different libraries built on top of both Apple’s StoreKit framework and the Google Play Billing Library, which helps simplify IAP integration in mobile apps. In this article, we’ll explore a selection of some of the best IAP libraries for implementing in-app subscriptions in your React Native application.
Jump ahead:
When deciding on which IAP library to use in your application, there are several factors you need to consider, including the following:
react-native-iap is a popular IAP library developed and maintained by dooboolab. It supports in-app subscription implementation for the App Store, Play Store, and Amazon Appstore.
react-native-iap provides a set of Hooks and exposes a set of APIs for processing product purchases and subscriptions, listening for completed and failed transactions, and fetching your product listing.
However, react-native-iap does not support receipt validation, so you must verify transactions on your backend.
Prior to integrating the react-native-iap
library, you need to configure and create your product catalog with unique identifiers in the respective stores; react-native-iap uses these IDs to fetch your product listing.
You can install react-native-iap through npm:
npm install react-native-iap
Afterward, follow the installation instructions outlined in the documentation for both Android and iOS to complete the configuration process.
Example usage:
import React, { useEffect, useState } from 'react'; import { StyleSheet, Text, View, Platform, Pressable } from 'react-native'; import { initConnection, endConnection, finishTransaction, flushFailedPurchasesCachedAsPendingAndroid, purchaseUpdatedListener, purchaseErrorListener, getProducts, requestPurchase, } from 'react-native-iap' const App = () => { const [products, setProducts] = useState([]); useEffect(() => { const initializeConnection = async () => { try { await initConnection(); if (Platform.OS === "android") { await flushFailedPurchasesCachedAsPendingAndroid(); } } catch (error) { console.error("An error occurred", error.message); } } const purchaseUpdate = purchaseUpdatedListener( async (purchase) => { const receipt = purchase.transactionReceipt; if (receipt) { try { await finishTransaction({ purchase, isConsumable: true }); } catch (error) { console.error("An error occurred", error.message); } } }); const purchaseError = purchaseErrorListener((error) => console.error('Purchase error', error.message)); initializeConnection(); purchaseUpdate(); purchaseError(); fetchProducts(); return () => { endConnection(); purchaseUpdate.remove(); purchaseError.remove(); } }, []); const fetchProducts = async () => { try { const products = await getProducts({ skus: Platform.select({ ios: ['com.rniap.product10', 'com.rniap.product20'], android: ['com.rniap.product100', 'com.rniap.product200'], }) }); setProducts(products); } catch (error) { console.error("Error occurred while fetching products", error.message); } }; const makePurchase = async (sku) => { try { requestPurchase({ sku }) } catch (error) { console.error("Error making purchase", error.message); } } return ( <View style={styles.container}> { products.map((product) => ( <View key={product.productId} style={styles.row}> <Text>{product.title}</Text> <Pressable onPress={() => makePurchase(product.productId)}>Buy Product</Pressable></View> )) } </View> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', padding: 10 }, row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', }, text: { fontSize: 16, marginEnd: 20 } }); export default App;
This code snippet sets up the necessary connections and listeners for in-app purchases, fetches products from the store, and allows the user to make purchases by pressing a button for each product displayed on the screen.
The initializeConnection
function initializes the in-app purchase connection using the initConnection
function. If the platform is Android, it also flushes any failed purchases that are cached as pending using flushFailedPurchasesCachedAsPendingAndroid
.
The purchaseUpdate
function is a listener that is triggered when a purchase is updated. It calls the finishTransaction
function to finish the transaction after a successful purchase. The purchaseError
function is a listener that is triggered when a purchase error occurs.
The react-native-adapty
library allows you to integrate in-app subscriptions into your React Native applications seamlessly. It provides a comprehensive set of features you‘d typically need to manage subscriptions, such as A/B testing, free trials, refunds, renewals, upgrades, downgrades, one-time purchases, and lifetime subscriptions. react-native-adapty can be used in both Expo-managed and native React Native workflows.
In addition, it offers an intuitive dashboard where you can track performance metrics, view users’ payment history, customize UI elements, and manage product listings without releasing new versions of your application.
To begin using react-native-adapty, you need to create an Adapty account and then register your application, after which you will get your public SDK key for use in your application.
You can install the react-native-adapty SDK using Yarn:
yarn add react-native-adapty
After installation, follow the instructions described in the documentation to complete installation for both Android and iOS.
Example usage:
import React, { useEffect, useState } from 'react'; import { StyleSheet, Text, View, Pressable } from 'react-native'; import { adapty } from 'react-native-adapty'; const App = () => { const [products, setProducts] = useState([]); useEffect(() => { const initialize = async () => { try { await adapty.activate("PUBLIC_SDK_KEY", { customerUserId: "USER_ID", logLevel: 'ERROR', }); } catch (error) { console.error("An error occurred while activating adapty", error.message); } } initialize(); fetchPaywall("paywall_id"); }, []); //pawywallId is the id specified when creating a paywall in the adapty dashboard const fetchPaywall = async (paywallId) => { try { const paywallResult = await adapty.getPaywall(paywallId); const productResult = await adapty.getPaywallProducts(paywallResult); setProducts(productResult); } catch (error) { console.error("Error occured while fetching paywall", error.message); } }; const makePurchase = async (product) => { try { await adapty.makePurchase(product) } catch (error) { console.error("Error making purchase", error.message); } } return ( <View style={styles.container}> { products.map((product) => ( <View key={product.vendorProductId} style={styles.row}> <Text>{product.localizedTitle}</Text> <Pressable onPress={() => makePurchase(product)}>Buy Product</Pressable></View> )) } </View> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', padding: 10 }, row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', }, text: { fontSize: 16, marginEnd: 20 } }); export default App;
Use the adapty.makePurchase
method enables in-app purchases. The fetchPaywall
function is an asynchronous function that fetches the paywall using the adapty.getPaywall
method and then fetches the paywall products using the adapty.getPaywallProducts
method.
The initialize
function is an asynchronous function that activates the Adapty SDK using the adapty.activate
method. It takes a public SDK key, customer user ID, and log level as parameters.
To use this code, you need to have the necessary dependencies installed, including the Adapty SDK. Additionally, you need to replace:
"PUBLIC_SDK_KEY"
with your actual Adapty SDK public key"USER_ID"
with the customer user ID"paywall_id"
with the ID of the paywall you want to fetchreact-native-purchases is a user-friendly IAP library developed and maintained by RevenueCat. It is a wrapper around the StoreKit framework, Google Play Billing Library, and the RevenueCat backend. However, if you’re using an Expo-managed workflow, you will need to create a development build as well.
It has built-in support for receipt validation, subscription status tracking, analytics metrics, and webhooks, which you can use to set up notifications for specific purchase events. In addition, you can remotely configure your product listings from your dashboard, view users’ transaction history, and easily integrate with third-party analytic tools.
To get started with integrating react-native-purchases in your application, you first need to sign up for a RevenueCat account and also set up a new project, and then proceed to add your application, after which you will obtain an API key for use in your project.
You can install the react-native-purchases SDK through npm:
npm install react-native-purchases
After installation, you also have to perform some additional configuration for iOS. Additionally, remember to also include the “Billing” permission in your AndroidManifest.xml
file.
<uses-permission android:name="com.android.vending.BILLING" />
Example usage:
import React, { useEffect, useState } from 'react'; import { StyleSheet, Text, View, Pressable } from 'react-native'; import Purchases from 'react-native-purchases'; const App = () => { const [packages, setPackages] = useState([]); useEffect(() => { Purchases.setLogLevel(Purchases.LOG_LEVEL.ERROR); const initialize = () => { Purchases.configure({ apiKey: "API_KEY", appUserID: null, observerMode: false, useAmazon: false }); } initialize(); fetchPackages(); }, []); const fetchPackages = async () => { try { const offerings = await Purchases.getOfferings() if(offerings.current !== null) { setPackages(offerings.current.availablePackages) } } catch (error) { console.error("Error occured while fetching packages", error.message); } }; const makePurchase = async (pkg) => { try { const {customerInfo} = await Purchases.purchasePackage(pkg); if(typeof customerInfo.entitlements.active["ENTITLEMENT_ID"] !== 'undefined') { //do something } } catch (error) { if(!error.userCancelled) { console.error("Error making purchase", error.message); } } } return ( <View style={styles.container}> { packages.map((pkg) => ( <View key={pkg.identifier} style={styles.row}> <Text>{pkg.title}</Text> <Pressable onPress={() => makePurchase(pkg)}>Buy Product</Pressable></View> )) } </View> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', padding: 10 }, row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', }, text: { fontSize: 16, marginEnd: 20 } }); export default App;
This code sets up a React Native app with a purchases integration for in-app purchases. It initializes purchases, fetches the available packages, and provides a UI to display and purchase those packages.
A call to Purchases.purchasePackage
initiates the purchase process. Purchases.getOfferings
fetches the offerings. The initialize
function configures purchases with the API key, appUserID
, observerMode
, and useAmazon
properties using Purchases.configure
. If an error occurs during configuration, it is logged to the console.
react-native-qonversion is another notable IAP library that allows you to swiftly incorporate in-app subscriptions into your application, eliminating the need to set up a backend for receipt validation — this is all handled by Qonversion.
You can also send users personalized push notifications triggered by purchase events, discover optimal pricing options via A/B tests, view detailed subscription analytics, share subscription data with your favorite analytics tool, handle both App Store and Play Store purchases, and so on.
To begin integrating react-native-qonversion, you need to create an account and proceed to create a project and register your application. After that, you can get your project key for use in your application. Also, don’t forget to create your IAP products on App Store Connect and the Google Play console!
The react-native-qonversion library can be installed through npm:
npm install react-native-qonversion
However, if you’re developing with Expo, you need to create a development build as well.
Example usage:
import React, { useEffect, useState } from 'react'; import { StyleSheet, Text, View, Pressable } from 'react-native'; import Qonversion, {QonversionConfigBuilder, LaunchMode, Entitlement} from 'react-native-qonversion' const config = new QonversionConfigBuilder( 'PROJECT_KEY', LaunchMode.SUBSCRIPTION_MANAGEMENT ).build(); const App = () => { const [products, setProducts] = useState([]); useEffect(() => { const initialize = () => { Qonversion.initialize(config); } initialize(); fetchProducts(); }, []); const fetchProducts = async () => { try { const offerings = await Qonversion.getSharedInstance().offerings(); if(offerings.main != null && offerings.main.products.length > 0) { setProducts(offerings.main.products); } } catch (error) { console.error("Error occured while fetching products", error.message); } }; const makePurchase = async (product) => { try { const entitlements = await Qonversion.getSharedInstance().purchaseProduct(product); if(entitlements !== null && entitlements['premium'].isActive) { //do something } } catch (error) { if(!error.userCanceled) { console.error("Error making purchase", error.message); } } } return ( <View style={styles.container}> { products.map((product) => ( <View key={product.qonversionID} style={styles.row}> <Text>{product.qonversionID}</Text> <Pressable onPress={() => makePurchase(product)}>Buy Product</Pressable></View> )) } </View> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', padding: 10 }, row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', }, text: { fontSize: 16, marginEnd: 20 } }); export default App;
This code sets up a React Native app with Qonversion integration for subscription management. It initializes the Qonversion SDK, fetches the available products, and provides a UI to display and purchase those products.
The QonversionConfigBuilder
object is created using the 'PROJECT_KEY'
and 'LaunchMode.SUBSCRIPTION_MANAGEMENT'
values. This configures the Qonversion
SDK with the appropriate project key and sets the launch mode to subscription management.
A call to Qonversion.getSharedInstance().purchaseProduct(product)
initiates the purchase process. Qonversion.getSharedInstance().offerings()
fetches the product offerings.
The following table shows an overview of the features of each of the highlighted IAP libraries:
Library | react-native-iap | react-native-adapty | react-native-purchases | react-native-qonversion |
---|---|---|---|---|
Stores supported | Apple App Store, Google Play Store, Amazon Appstore | Apple App Store and Google Play Store | Apple App Store, Google Play Store, and Amazon Appstore | Apple App Store and Google Play Store |
Receipt validation | No | Yes | Yes | Yes |
Supports A/B tests | No | Yes | Yes | Yes |
No-code paywall | No | Yes | No | No |
Dynamic paywall | No | Yes | Yes | Yes |
Third-party integrations | No | Yes | Yes | Yes |
Pricing | Free | Free up to $10,000 monthly tracked revenue (MTR) | Free up to $10,000 MTR | Free up to $1,000 MTR |
In-app purchase is an important method of app monetization. When incorporating IAP in React Native applications, several libraries stand out for their robust features, flexibility, and cross-platform support. These libraries simplify the process of integrating IAP functionality and offer various capabilities such as purchasing products, verifying receipts, listing products, managing subscriptions, and so on.
By harnessing the features provided by these libraries, you can streamline the integration of in-app purchases in your React Native application, enabling effective app monetization and enhancing the overall user experience.
LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.
LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.
Start proactively monitoring your React Native apps — try LogRocket for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
2 Replies to "Best React Native in-app subscription libraries"
Expo InAppPurchases – A library for accepting in-app purchases.
Deprecated: This module is no longer maintained and will be removed in a future SDK release. Development was paused in June 2022 and the package was deprecated in August 2023. We recommend one of the following, both of which are compatible with EAS Build and development builds:
• react-native-iap, which provides an interface to client-side Google Play Billing and StoreKit API’s
• react-native-purchases, which also integrates with RevenueCat’s services for server-side receipt validation and more.
Thank you for letting us know! We have updated the post to remove expo-in-app-purchases and added an editor’s note to reflect this change. We already cover both alternatives you mention in the post.