React Native : Subscriptions, In-App Purchases & Service Fees (Android & IOS)

Image for post
Image for post

What is In-App Purchase?

What is Subscription?

Types Of In-App Purchases

In-app purchase product creation

1. Configure in-app purchase on iTunes Connect (for iOS)

Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post

2. Configure In-App Purchase on Google Play Store Console

Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
<uses-permission android:name=”com.android.vending.BILLING” />

3.Install package react-native-iap library in React native

yarn add react-native-iap or npm install react-native-iapcd ios && pod install //For IOS

Usage

Init IAP, In App Billing

import * as RNIap from 'react-native-iap';const itemSkus = Platform.select({
ios: [
'com.example.coins100'
],
android: [
'com.example.coins100'
]
});

Get Valid Items

async componentDidMount() {
try {
const products: Product[] = await RNIap.getProducts(itemSkus);
this.setState({ products });
} catch(err) {
console.warn(err); // standardized err.code and err.message available
}
}

Purchase

import RNIap, {
purchaseErrorListener,
purchaseUpdatedListener,
type ProductPurchase,
type PurchaseError
} from 'react-native-iap';
class RootComponent extends Component<*> {
purchaseUpdateSubscription = null
purchaseErrorSubscription = null
componentDidMount() {
RNIap.initConnection().then(() => {
// we make sure that "ghost" pending payment are removed
// (ghost = failed pending payment that are still marked as pending in Google's native Vending module cache)
RNIap.flushFailedPurchasesCachedAsPendingAndroid().catch(() => {
// exception can happen here if:
// - there are pending purchases that are still pending (we can't consume a pending purchase)
// in any case, you might not want to do anything special with the error
}).then(() => {
this.purchaseUpdateSubscription = purchaseUpdatedListener((purchase: InAppPurchase | SubscriptionPurchase | ProductPurchase ) => {
console.log('purchaseUpdatedListener', purchase);
const receipt = purchase.transactionReceipt;
if (receipt) {
yourAPI.deliverOrDownloadFancyInAppPurchase(purchase.transactionReceipt)
.then( async (deliveryResult) => {
if (isSuccess(deliveryResult)) {
// Tell the store that you have delivered what has been paid for.
// Failure to do this will result in the purchase being refunded on Android and
// the purchase event will reappear on every relaunch of the app until you succeed
// in doing the below. It will also be impossible for the user to purchase consumables
// again until you do this.
if (Platform.OS === 'ios') {
await RNIap.finishTransactionIOS(purchase.transactionId);
} else if (Platform.OS === 'android') {
// If consumable (can be purchased again)
await RNIap.consumePurchaseAndroid(purchase.purchaseToken);
// If not consumable
await RNIap.acknowledgePurchaseAndroid(purchase.purchaseToken);
}
// From react-native-iap@4.1.0 you can simplify above `method`. Try to wrap the statement with `try` and `catch` to also grab the `error` message.
// If consumable (can be purchased again)
await RNIap.finishTransaction(purchase, true);
// If not consumable
await RNIap.finishTransaction(purchase, false);
} else {
// Retry / conclude the purchase is fraudulent, etc...
}
});
}
});
this.purchaseErrorSubscription = purchaseErrorListener((error: PurchaseError) => {
console.warn('purchaseErrorListener', error);
});
})
})
}
componentWillUnmount() {
if (this.purchaseUpdateSubscription) {
this.purchaseUpdateSubscription.remove();
this.purchaseUpdateSubscription = null;
}
if (this.purchaseErrorSubscription) {
this.purchaseErrorSubscription.remove();
this.purchaseErrorSubscription = null;
}
}
}
requestPurchase = async (sku: string) => {
try {
await RNIap.requestPurchase(sku, false);
} catch (err) {
console.warn(err.code, err.message);
}
}
requestSubscription = async (sku: string) => {
try {
await RNIap.requestSubscription(sku);
} catch (err) {
console.warn(err.code, err.message);
}
}
render() {
...
onPress={() => this.requestPurchase(product.productId)}
...
}

New Purchase Flow

Finishing a Purchase

Restoring Purchases

getPurchases = async () => {
try {
const purchases = await RNIap.getAvailablePurchases();
const newState = { premium: false, ads: true }
let restoredTitles = [];
purchases.forEach(purchase => {
switch (purchase.productId) {
case 'com.example.premium':
newState.premium = true
restoredTitles.push('Premium Version');
break
case 'com.example.no_ads':
newState.ads = false
restoredTitles.push('No Ads');
break
case 'com.example.coins100':
await RNIap.consumePurchaseAndroid(purchase.purchaseToken);
CoinStore.addCoins(100);
}
})
Alert.alert('Restore Successful', 'You successfully restored the following purchases: ' + restoredTitles.join(', '));
} catch(err) {
console.warn(err); // standardized err.code and err.message available
Alert.alert(err.message);
}
}

Receipt validation

With IAPHUB

With Google Play

With App Store

const receiptBody = {
'receipt-data': purchase.transactionReceipt,
'password': '******'
};
const result = await RNIap.validateReceiptIos(receiptBody, false);
console.log(result);

Testing

Service Fees

Ios

Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post

Android

How much does a Google & Apple Developer Account Cost?

Reference

Software Engineer

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