react-native-iap
This is a react-native link library project for in app purchase for both android and ios platforms. The goal for this project is to have similar experience between the two platforms for in-app-purchase. Basically android platform has more functions for in-app-purchase and is not our specific interests for this project.
We are willing to share same in-app-purchase experience for both android and ios platform and will continuously merge methods which are standing alone.
Android iap is implemented with iap version 3 which is currently recent.
Playstore & Itunnesconnect configuration
Migration Guide
2.0.0-alpha1
has released. Not much difference. There were some parameters supports and changes to distinguish the differences in platform at one sight. Please follow the readme what you get in returned variables when calling getItems
and when purchasing through buyProduct
or buySubscription
.
Difference between 0.3.*
and 1.0.0
has only one method renaming refreshItems
to consumeAllItems
.
To migrate 0.2.*
to 0.3.*
, You can follow below guide.
0.2.* | 0.3.* | 1.* |
---|
prepareAndroid | prepare | prepare |
getItems | getProducts | getProducts |
getSubscribeItems | getSubscriptions | getSubscriptions |
getPurchasedItemsAndroid | getPurchaseHistory | getPurchaseHistory |
`` | getAvailablePurchases | getAvailablePurchases |
buySubscribeItem | buySubscription | buySubscription |
buyItem | buyProduct | buyProduct |
consumeItemAndroid | consumePurchase | consumePurchase |
refreshAllItems | Not Available | consumeAllItems |
refreshPurchaseItemsAndroid | Not Available | Not Available |
From above method changes, getProducts
gets itemSkus
as parameter in different way then as used in getItems
. In getItems
you had to put parameter as
const itemSkus = {
ios: [
'point_1000',
],
android: [
'point_1000',
],
};
But now you should do like below which will just pass single array instead of object.
const itemSkus = Platform.select({
ios: [
'point_1000',
],
android: [
'point_1000',
],
});
Also, note that this is our last migration for renaming method names without any deprecation warning. Thank you for your understanding.
Methods
Func | Param | Return | Description |
---|
prepare | | Promise<void> | Prepare IAP module. Must be called on Android before any other purchase flow methods. In ios, it will simply call canMakePayments method and return value. |
getProducts | string[] Product IDs/skus | Promise<Product[]> | Get a list of products (consumable and non-consumable items, but not subscriptions). Note: On iOS versions earlier than 11.2 this method will return subscriptions if they are included in your list of SKUs. This is because we cannot differentiate between IAP products and subscriptions prior to 11.2. |
getSubscriptions | string[] Subscription IDs/skus | Promise<Subscription[]> | Get a list of subscriptions. Note: On iOS this method has the same output as getProducts . Because iOS does not differentiate between IAP products and subscriptions. |
getPurchaseHistory | | Promise<Purchase[]> | Gets an invetory of purchases made by the user regardless of consumption status (where possible) |
getAvailablePurchases | | Promise<Purchase[]> | Get all purchases made by the user (either non-consumable, or haven't been consumed yet) |
buySubscription | string Subscription ID/sku, string Old Subscription ID/sku (on Android) | Promise<Purchase> | Create (buy) a subscription to a sku. For upgrading/downgrading subscription on Android pass second parameter with current subscription ID, on iOS this is handled automatically by store. |
buyProduct | string Product ID/sku | Promise<Purchase> | Buy a product |
buyProductWithoutFinishTransaction | string Product ID/sku | Promise<Purchase> | Buy a product without finish transaction call (iOS only) |
finishTransaction | void | void | Send finishTransaction call to Apple IAP server. Call this function after receipt validation process |
consumeProduct | string Purchase token | Promise<void> | Consume a product (on Android.) No-op on iOS. |
endConnection | | Promise<void> | End billing connection (on Android.) No-op on iOS. |
consumeAllItems | | Promise<void> | Consume all items in android so they are able to buy again (on Android.) No-op on iOS. |
validateReceiptIos | object receiptBody, boolean isTest, number RNVersion | object or boolean result | validate receipt for ios. |
validateReceiptAndroid | string packageName, string productId, string productToken, string accessToken, boolean isSubscription, number RNVersion | object or boolean result | validate receipt for android. |
Npm repo
https://www.npmjs.com/package/react-native-iap
Git repo
https://github.com/dooboolab/react-native-iap
Getting started
$ npm install react-native-iap --save
Mostly automatic installation
$ react-native link react-native-iap
Note for Ejected iOS Apps:
The above command will add the following to your Podfile
:
pod 'RNIap', :path => '../node_modules/react-native-iap'
You should remove this before running pod install
and follow the manual installation instructions below.
Manual installation
iOS
- In XCode, in the project navigator, right click
Libraries
➜ Add Files to [your project's name]
- Go to
node_modules
➜ react-native-iap
and add RNIap.xcodeproj
- In XCode, in the project navigator, select your project. Add
libRNIap.a
to your project's Build Phases
➜ Link Binary With Libraries
- Run your project (
Cmd+R
)<
Android
- Open up
android/app/src/main/java/[...]/MainApplication.java
- Add
import com.dooboolab.RNIap.RNIapPackage;
to the imports at the top of the file - Add
new RNIapPackage()
to the list returned by the getPackages()
method
- Append the following lines to
android/settings.gradle
:
include ':react-native-iap'
project(':react-native-iap').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-iap/android')
- Insert the following lines inside the dependencies block in
android/app/build.gradle
:
compile project(':react-native-iap')
- Add the following to the
<permission>
block in android/app/src/main/AndroidManifest.xml
:
<uses-permission android:name="com.android.vending.BILLING" />
Usage
You can look in the RNIapExample folder to try the example. Below is basic implementation which is also provided in RNIapExample project.
Prepare IAP, In App Billing.
First thing you should do is to define your items for iOS and android separately like defined below.
import * as RNIap from 'react-native-iap';
const itemSkus = Platform.select({
ios: [
'com.example.coins100'
],
android: [
'com.example.coins100'
]
});
Next, call the prepare function (ios it's not needed, but android it is. No need to check platform though since nothing will happen in ios:
async function() {
try {
await RNIap.prepare();
} catch(err) {
console.warn(err);
}
}
Get Valid Items
Once you called prepare(), call getProducts(). Both are async funcs. You can do it in componentDidMount(), or other area as appropriate for you app. Since a user may first start your app with a bad internet connection, then later have an internet connection, making preparing/getting items more than once may be a good idea. Like if the user has no IAPs available when the app first starts, you may want to check again when the user enters the your IAP store.
async componentDidMount() {
try {
await RNIap.prepare();
const products = await RNIap.getProducts(itemSkus);
this.setState({ products });
} catch(err) {
console.warn(err);
}
}
Each item is a JavaScript object containing these keys:
| iOS | Android | Comment |
---|
price | ✓ | ✓ | Will return localizedPrice on Android (default) or a string price (eg. 1.99 ) (iOS) |
productId | ✓ | ✓ | Returns a string needed to purchase the item later |
currency | ✓ | ✓ | Returns the currency code |
localizedPrice | ✓ | ✓ | Use localizedPrice if you want to display the price to the user so you don't need to worry about currency symbols. |
title | ✓ | ✓ | Returns the title Android and localizedTitle on iOS |
introductoryPrice | ✓ | ✓ | Formatted introductory price of a subscription, including its currency sign, such as €3.99. The price doesn't include tax. |
subscriptionPeriodNumberIOS | ✓ | | The unit in string like DAY or WEEK or MONTH or YEAR |
subscriptionPeriodUnitIOS | ✓ | | The unit number of subscription period |
subscriptionPeriodAndroid | | ✓ | Subscription period, specified in ISO 8601 format. For example, P1W equates to one week, P1M equates to one month, P3M equates to three months, P6M equates to six months, and P1Y equates to one year. |
introductoryPriceCyclesAndroid | | ✓ | The number of subscription billing periods for which the user will be given the introductory price, such as 3. |
introductoryPricePeriodAndroid | | ✓ | The billing period of the introductory price, specified in ISO 8601 format. |
freeTrialPeriodAndroid | | ✓ | Trial period configured in Google Play Console, specified in ISO 8601 format. For example, P7D equates to seven days. |
End Billing Connection
When you are done with the billing, you should release it for android(READ). It is not needed in ios. No need to check platform either since nothing will happen in ios. This can be used in componentWillUnMount
.
componentWillUnmount() {
RNIap.endConnection();
}
Purchase
Once you have called getProducts(), and you have a valid response, you can call buyProduct().
const purchase = await RNIap.buyProduct('com.example.coins100');
In RNIapExample, upon receiving receiving a purchase receipt, main page will navigate to Second.js.
Purchase Example 2 (Advanced)
this.setState({ progressTitle: 'Please wait...' });
RNIap.buyProduct('com.example.coins100').then(purchase => {
this.setState({
receipt: purchase.transactionReceipt,
progressTitle: 'Purchase Successful!',
coins: this.state.coins + 100
});
}).catch(err => {
console.warn(err);
this.setState({ progressTitle: 'Buy 100 Coins for only $0.99' });
alert(err.message);
})
Subscribable products can be purchased just like consumable products.
Users can cancel subscriptions by using the iOS System Settings.
Consumption and Restoring Purchases
You can use getAvailablePurchases()
to do what's commonly understood as "restoring" purchases. Once an item is consumed, it will no longer be available in getAvailablePurchases()
and will only be available via getPurchaseHistory()
. However, this method has some caveats on Android -- namely that purchase history only exists for the single most recent purchase of each SKU -- so your best bet is to track consumption in your app yourself. By default all items that are purchased will not be consumed unless they are automatically consumed by the store (for example, if you create a consumable item for iOS.) This means that you must manage consumption yourself. Purchases can be consumed by calling consumePurchase()
. If you want to consume all items, you have to iterate over the purchases returned by getAvailablePurchases()
.
getPurchases = async() => {
try {
const purchases = await RNIap.getAvailablePurchases();
let restoredTitles = '';
let coins = CoinStore.getCount();
purchases.forEach(purchase => {
if (purchase.productId == 'com.example.premium') {
this.setState({ premium: true });
restoredTitles += 'Premium Version';
} else if (purchase.productId == 'com.example.no_ads') {
this.setState({ ads: false });
restoredTitles += restoredTitles.length > 0 ? 'No Ads' : ', No Ads';
} else if (purchase.productId == 'com.example.coins100') {
CoinStore.addCoins(100);
await RNIap.consumePurchase(purchase.purchaseToken);
}
})
Alert.alert('Restore Successful', 'You successfully restored the following purchases: ' + restoredTitles);
} catch(err) {
console.warn(err);
Alert.alert(err.message);
}
}
Returned purchases is an array of each purchase transaction with the following keys:
| iOS | Android | Comment |
---|
productId | ✓ | ✓ | The product ID for the product. |
purchaseToken | ✓ | ✓ | A token that uniquely identifies a purchase for a given item and user pair. |
transactionReceipt | ✓ | ✓ | receipt for ios and purchaseToken for android. |
transactionId | ✓ | ✓ | A unique order identifier for the transaction. |
transactionDate | ✓ | ✓ | The time the product was purchased, in milliseconds since the epoch (Jan 1, 1970). |
autoRenewingAndroid | | ✓ | Indicates whether the subscription renews automatically. If true, the subscription is active, and will automatically renew on the next billing date. If false, indicates that the user has canceled the subscription. |
dataAndroid | | ✓ | Original json for purchase data. |
signatureAndroid | | ✓ | String containing the signature of the purchase data that was signed with the private key of the developer. The data signature uses the RSASSA-PKCS1-v1_5 scheme. |
originalTransactionDateIOS | ✓ | | For a transaction that restores a previous transaction, the date of the original transaction. |
originalTransactionIdentifierIOS | ✓ | | For a transaction that restores a previous transaction, the transaction identifier of the original transaction. |
You need to test with one sandbox account, because the account holds previous purchase history.
Receipt validation
From react-native-iap@0.3.16
, we support receipt validation. For android, you need seperate json file from service account to get the access_token
from google-apis
, therefore it is impossible to implement serverlessly. You should have your own backend and get access_token
. With access_token
you can simplly call validateReceiptAndroid
method we implemented. Further reading is here.
Currently, serverless receipt validation is possible using validateReceiptIos
method. First parameter, you should pass transactionReceipt
which returns after buyProduct
. Second parameter, you should pass whether this is test
environment. If true
, it will request to sandbox
and false
it will request to production
.
const receiptBody = {
'receipt-data': purchase.transactionReceipt,
};
const result = await validateReceiptIos(receiptBody, false, 54);
console.log(result);
For further information, please refer to guide.
iOS Purchasing process right way.
Purchasing consumable products in iOS consists of the following steps.
Step 1 : Purchasing via IAP (Apple server)
Step 2 : Check the validation of the receipt (either on device or server)
Step 3 : Apply the product to the Application
But, sometimes app doesn't make it to step 3, and user loose the product with successful payment.
Non-consumable products can be restored via getPurchaseHistory function, but consumable products can be lost.
In this case, use buyProductWithoutFinishTransaction to purchase action and use finishTransaction to finish payment after receipt validation and supply the products to user.
Supporting react-native-iap
react-native
is open source project with MIT license. We are willing to maintain this repository to support devs to monetize around the world. Since, IAP
itself is not perfect in each platform, we desperately needs this project to be maintained. If you'd like to help us, please consider to be with us in Open Collective.
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]
Backers
Please be our Backers.
Contributing
Please make sure to read the Contributing Guide before making a pull request.
Thank you to all the people who helped to maintain and upgrade this project!