React-Native-IAP: One package to rule them all π§ββοΈ
Anisha Malde
Posted on February 20, 2023
React Native developers often face the problem of adding native support for multiple operating systems, which takes time and effort. What if there was an open source project to handle In-App purchasing (IAP) dependencies across iOS, Google PlayStore, and Amazon Appstore for you? Hello react-native-iap!
I recently had this challenge while integrating Amazon IAP in a React Native app. After researching the top open source projects for purchasing, react-native-iap
enabled IAP functionality without the complexity of operating system specific APIs β truly one (react native) package to rule them all! π§ββοΈβ¨
For this article, I am going to walk you through the project and then Iβll explain how I launched their sample with the Amazon IAP App Tester.
β¬οΈ The sample app
The first thing I did was clone the Github repository for react-native-iap
as it contained all the working examples for Amazon Appstore, Google Play, and Apple Appstore.
git clone https://github.com/dooboolab/react-native-iap
Next, I navigated to the IapExample folder:
From within the folder, I used these commands to start up in the specific platform:
yarn ios
yarn android:play
yarn android:amazon
Note: If building an app with Java, make sure your Java version is compatible with the Kotlin version used by the app - (i.e.: Java 17)
πͺ The useIAP hook for all the stores
Exploring the sample I saw it uses their new useIAP
hook that handles purchases for all the different stores. This was great as the useIAP
hook allows you to access a number of operating system agnostic variables and functions allowing you to use the reuse the same logic across platforms.
const {
connected,
getSubscriptions,
getProducts,
products,
subscriptions,
currentPurchaseError,
initConnectionError,
finishTransaction,
currentPurchase,
purchaseHistory,
availablePurchases,
} = useIAP();
-
connected
is a boolean variable that checks whether the native IAP module is able to interact with react. -
getProducts
&getSubscriptions
are the functions that update the products and subscriptions variables when fetched. -
currentPurchaseError
&initConnectionError
are the errors returned from failed purchases & failed connection respectively. -
currentPurchase
returns the current purchase. -
finishTransaction
is the function you call to βconsumeβ purchases.- For consumables, once an item is consumed, it will be removed from
getAvailablePurchases()
and you should record the purchase into your database before callingfinishTransaction()
. - For non-consumables, purchases need to be acknowledged on Android, or they will be automatically refunded after a few days. This method acknowledges a purchase when you have delivered it to your user.
- For consumables, once an item is consumed, it will be removed from
-
purchaseHistory
returns an array of all the previous purchases -
availablePurchases
returns an array of all the available items that can be purchased There are two other methods that are important, however they are not available through the hook and will need to be imported from the package: ```javascript
import {requestPurchase, requestSubscription} from 'react-native-iap';
* `requestPurchase` & `requestSubscription` are the functions that handle the purchase of the products/subscriptions.
Taking the example of scenario where a user is making a purchase of a subscription, I mapped how I could utilise the variables and functions:
#### Retrieve the available subscriptions
The first step would be to retrieve the available subscriptions and present them to a user. I can easily do this by checking I am `connected` and then calling the `getSubscriptions` function. This will automatically update the subscriptions variable with the `subscriptions`:
```javascript
const {connected, subscriptions, getSubscriptions} = useIAP();
useEffect(() => {
if (connected) {
getSubscriptions({skus: [
'com.amazon.sample.iap.subscription.mymagazine.month',
'com.amazon.sample.iap.subscription.mymagazine.quarter',
]});
}
}, [getSubscriptions]);
Handle the purchase request
Next to handle a purchase request e.g. when a purchaseSubscription
button is pressed, I can make my call of requestSubscription
:
requestSubscription = async (sku: Sku) => {
try {
await requestSubscription({sku});
} catch (error) {
//Handle error
}
};
Handle errors
Next, I can wrap my currentPurchaseError
in a useEffect
and listen to check if any error occurred and then handle it accordingly:
const {currentPurchaseError} = useIAP();
useEffect(() => {
...
}, [currentPurchaseError]);
Check current purchase
Finally, asynchronously, I can create a function to check the status and execute finishTransaction
:
const {currentPurchase, finishTransaction} = useIAP();
useEffect(() => {
const checkCurrentPurchase = async () => {
try {
if (currentPurchase?.productId) {
await finishTransaction({
purchase: currentPurchase,
isConsumable: true,
});
}
} catch (error) {
//Handle error
}
};
checkCurrentPurchase();
}, [currentPurchase, finishTransaction]);
π§ͺ Test with the Amazon App Tester
The sample looked great so it was now time to unit test IAP functionality by simulating a production environment with the Amazon IAP App Tester in three steps.
Step 1: Download the App Tester
If you haven't already got it, you can download App Tester from the Amazon Appstore, onto the same Android device as your app. The App Tester simulates the production environment and you configure it with IAP items.
Step 2: Create the amazon.sdktester.json
The example app already contains the JSON file with the IAP Items used in the demo and you can find it in IAPExample/android/app/amazon.sdktester.json
{
"com.amazon.sample.iap.subscription.mymagazine.month": {
"description":"Monthly Subscription to My Magazine",
"title":"My Magazine",
"itemType":"SUBSCRIPTION",
"price":5.0,
"subscriptionParent":"com.amazon.sample.iap.subscription.mymagazine"
},
"com.amazon.sample.iap.subscription.mymagazine.quarter": {
"description":"Quarterly Subscription to My Magazine",
"title":"My Magazine",
"itemType":"SUBSCRIPTION",
"price":12.0,
"subscriptionParent":"com.amazon.sample.iap.subscription.mymagazine"
}
}
However, you could also create your own IAP items here. Once you have created your in-app items, you can download a JSON-formatted data file containing all of the data for your items like the one above.
Step 3: Push the amazon.sdktester.json
file to your device
Next, if you haven't already, connect your Amazon device with your developer environment using the Android Debug Bridge (adb). This is to enable you to copy the JSON file to the /sdcard/
folder on your device file system by running the following command line:
$ adb push [_Your_JSON_File_Folder_]/amazon.sdktester.json /sdcard/amazon.sdktester.json
Note: If your device is running an Android OS version older than 4.0, copy the file to /mnt/sdcard/
.
Once that is set-up you will see the In-App items on the App Tester:
Finally, running the Amazon version of the example app with the command yarn android:amazon
you can test purchases using Amazon IAP:
Implementing with Expo
One thing to note if you are using Expo Go to build your app, when you are testing, make sure to change the build type to debug
in your eas.json
to ensure it is compatible with the test version of Amazon IAP. This will enable you to test with Fast Refresh too. The release(:app:assembleRelease)
version can only be tested when live testing with the Appstore.
"build": {
"preview": {
"android": {
"buildType": "apk",
"gradleCommand": ":app:assembleDebug"
}
},
Big kudos to lead maintainer @andresesfm and the all the contributions from community developers for react-native-iap! Consider showing your support to their open source project on OpenCollective. Iβll definitely be using their package to implement Amazon IAP in my React Native App!
To stay up to date with Amazon Appstore developer updates on the following platforms:
π£ Follow @AmazonAppDev on Twitter
πΊ Subscribe to our Youtube channel
π§ Sign up for the Developer Newsletter
Posted on February 20, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.