React Native Payments - atabegruslan/Notes GitHub Wiki

Plain basic method (without RevenueCat)

Introductory article: https://docs.expo.dev/guides/in-app-purchases

Start with react-native-iap's simple demo app

Use this library: https://github.com/hyochan/react-native-iap

It support both IAP and Subscriptions. It supports both iOS and Android.

In it, there is already an example demo app: https://github.com/hyochan/react-native-iap/blob/main/IapExample/README.md

An independent good video tutorial for using this: https://www.youtube.com/watch?v=kOWdwnHf4xY

git clone [email protected]:hyochan/react-native-iap.git
cd react-native-iap-main/IapExample
yarn
cd ios
pod install
cd ..

Then edit like this: https://github.com/facebook/metro/issues/431#issuecomment-566262158

yarn ios

You can actually try it in action without setting up payments in your own AppStoreConect or Google Play Console, because the maker of react-native-iap have their own demos setup on their accounts.
See: https://medium.com/dooboolab/react-native-in-app-purchase-121622d26b67
PS: Here, no need to add the Payment capability via xCode. Apparently the makers of this already did.

To test it on real iPhone:

https://github.com/atabegruslan/Notes/wiki/React-Native-Payments#with-real-iphones

To test it on iOS Simulator:

Open react-native-iap-main > IapExample > ios in xCode.

Then refer to this: https://github.com/atabegruslan/Notes/wiki/React-Native-Payments#with-simulator

PS: If the IapExample uses product ID of com.cooni.point1000, then create the same product ID here.

Tweek the demo example app’s source code, just to view the result data.

The run it from xCode (Do NOT run via the React Native script yarn ios)

To test it on Android: https://github.com/atabegruslan/Notes/wiki/React-Native-Payments#android

However, as of now, if you run yarn android:play, you will notice that it won't work, because nowadays Android is unstable.

Code it up yourself

Reference: https://react-native-iap.hyo.dev/docs/get-started/

Steps

Step 0: Set up on AppStoreConnect and Google Play Console

Set up these pre-requisites first in Apple and Google: https://github.com/atabegruslan/Notes/wiki/React-Native-Payments#how-do-money-ultimately-reach-you

iOS: AppStoreConnect > Go into App > Left Side Menu > App Store section > Monetization subsection: In App Purchases or Subscriptions

Android: Google Play Console > Go into App > Left Side Menu > Monetize with Play section > Products subsection > In App Products or Subscriptions

Creating IAP in Apple and Google is straightforward. But creating Subscriptions can be a bit more confusing.

Here are 2 good tutorials for creating Subscriptions in Apple and Google:

Tricky Part 1: In Apple, after you filled all the required fields for Subscription, but you still see a "missing metadata" status. That's because you should also enter some Localizations for its Subscription Group.

Tricky Part 2: In Google, when you fill out the prices for all the countries, it may seem like that you are forced to manually enter all the prices for all the countries. But rather you should use the "Set Prices" button here: https://youtu.be/ak4WiPK6HwE?t=254

Note for Apple:

So, Tricky Part 3: If you already had a previous build released, and your dashboard looks like this:

Then you should first cancel that release and then continue.

Additional notes:

Step 1: npx expo install react-native-iap

Step 2: in app.json, add:

{
 "expo": {
   "plugins": ["react-native-iap"]
 }
}

If step 1 didn't automatically do that.

Step 3 (ios only): Open ios folder in xCode, add the In App Purchase capability. Here is how you can add a capability: https://developer.apple.com/documentation/xcode/adding-capabilities-to-your-app

Step 4: https://github.com/hyochan/react-native-iap/blob/main/docs/docs/api-reference/hooks.md#installation

If you are using expo-router and your existing code is like this:

export default function RootLayout() { 
    return(
        <Stack>
            <Stack.Screen name="index" />
        </Stack>
    )
}

then add it in this way:

import { withIAPContext } from 'react-native-iap';

function RootLayout() { 
    return(
        <Stack>
            <Stack.Screen name="index" />
        </Stack>
    )
}

export default withIAPContext(RootLayout);

If you are using React Navigation, you can imitate this https://github.com/hyochan/react-native-iap/blob/main/IapExample/src/navigators/StackNavigator.tsx#L18

Step 5:

Todo: beyond here, I am not sure yet

Initialize Connection:

import { initConnection, endConnection } from 'react-native-iap';

At the beginning, initialize the connection to the store:
await initConnection();

At the end, if there is a connection, end it by endConnection()

Fetch Products:
To get your in-app products (IAPs):

import { getProducts } from 'react-native-iap'; 

const products = await getProducts({ skus: ['product_id'] });

Request Purchase:
Trigger the purchase flow:

import { requestPurchase } from 'react-native-iap'; 

await requestPurchase({ sku: 'product_id' });

Handle Purchase Updates:
Listen for purchase updates:

import { purchaseUpdatedListener, finishTransaction } from 'react-native-iap'; 

const purchaseUpdateSubscription = purchaseUpdatedListener(async (purchase) => {
    const { transactionReceipt } = purchase;
    if (transactionReceipt) {
        // Handle transaction, send receipt to your server for validation
        await finishTransaction({ purchase, isConsumable: true });
    }
});

return () => {
    purchaseUpdateSubscription.remove();
};

Subscriptions:

import { getSubscriptions, requestSubscription } from 'react-native-iap';

const subscriptions = await getSubscriptions({ skus: ['product_id'] }); // This step is needed in iOS
const result = await requestSubscription({ sku: 'product_id' }); // This way is insufficient in Android

// In Android, you actually need to do it this way
// await requestSubscription({
//     'product_id',
//     ...(offerToken && { subscriptionOffers: [{ sku, offerToken }] }),
// });

subscriptions:

[
   {
      "localizedPrice":"$24.99",
      "type":"subs",
      "price":"24.99",
      "introductoryPricePaymentModeIOS":"",
      "currency":"USD",
      "title":"1 month subscription for calls",
      "introductoryPriceSubscriptionPeriodIOS":"",
      "introductoryPrice":"",
      "introductoryPriceAsAmountIOS":"",
      "description":"bla bla bla",
      "discounts":[
         
      ],
      "introductoryPriceNumberOfPeriodsIOS":"",
      "subscriptionPeriodNumberIOS":"1",
      "countryCode":"USA",
      "subscriptionPeriodUnitIOS":"MONTH",
      "productId":"your.product.id",
      "platform":"ios"
   }
]

result:

{
      "productId":"your.product.id",
      "transactionId":"0",
      "transactionReceipt":"MIAGC…xo/4MAAAAAAAA=",
      "transactionDate":1749025582877
}

More info about the required Offer Token in Android: https://github.com/hyochan/react-native-iap/issues/2155#issuecomment-1689152125 .
You can obtain the Offer Token by import { useIAP } from 'react-native-iap'; then const { subscriptions } = useIAP();.
The contents of subscriptions:

[
   {
      "description":"",
      "name":"the title",
      "platform":"android",
      "productId":"xxx.yyy.zzz",
      "productType":"subs",
      "subscriptionOfferDetails":[
         {
            "basePlanId":"the-base-plan-name",
            "offerId":null,
            "offerTags":[
               
            ],
            "offerToken":"Xxxx==",
            "pricingPhases":{
               "pricingPhaseList":[
                  "Array"
               ]
            }
         }
      ],
      "title":"the title (xxx.yyy.zzz (unreviewed))"
   }
]

Things that may go wrong:

"Validate" Receipts

Disclaimer: This nominally "validating receipt" step is actually more like a results retrieval step.

Google Play Developer API

  1. purchaseToken will be obtained from the mobile app after purchase.
  2. Need OAuth2 access token from Google service account.
  3. Checks if the purchase is valid, refunded, etc:

Other resources:

App Store Server API

  1. Use Private key (.p8) , Key ID & Issuer ID to obtain JWT.
  2. Getting the full purchase data:

Data to keep to confirm/refute disputes later

Android

  • purchaseToken - Obtained when the purchase happens. Use it with the Play Developer API to fetch status (orderId, refund, etc).
  • orderId
  • productId
  • purchaseTime

There's also:

  • Monetize > Reports > sales & retention data

iOS

  • transaction_id
  • product_id
  • purchase_date

There's also:

  • Sales & Trends > aggregate reports
  • Financial Reports > revenue summaries

Another good tutorial of plain IAP and Subscriptions in React Native

With RevenueCat

FAQ

Testing without real money (ie: with dummy tester)

Android

This tutorial should cover it all: https://www.youtube.com/watch?v=u5p1P0gCmJo
In short, create some Licensed Testers in Google Play Console, verify those emails, then use those accounts in Google Play Store.

More documentation: https://developer.android.com/google/play/billing/test

iOS

With real iPhones

Create sandbox tester account here: https://appstoreconnect.apple.com/access/users/sandbox

Follow this tutorial: https://www.youtube.com/watch?v=GeB2ws0YgsQ
Then you also get email to verify your email.

Note:

  • Was: Settings > iTunes & App Store
  • Now: App Store app > scroll down > sign in with Sandbox Apple ID

More documentation:

With Simulator

StoreKit Testing:

In xCode: Create a StoreKit Config File (named Products) and an InApp Consumable.

Then edit the Schema to include the StoreKit Config file.

Then run Simulator from xCode (Press the Play button near the top left of xCode).

If you are using React Native, then DONT run it via the React Native script in terminal. You must run it from xCode, otherwise the StoreKit config file won't be taken into account.

If you are using React Native Expo, and after you pressed the Play button in xCode, you got the error saying that it cant connect to the server. Then you need to first run in the terminal npx expo run:ios and keep it running. Then run from xCode by pressing the Play button.

Testing long period subscriptions

Android

iOS

More documentations:

Where money come from

Android

GooglePay / Google Play Store, or physically via a shop

iOS

Apple pay (which is just a wrapper around credit cards, paypal, momo, ...) or iTunes wallet

How do money ultimately reach you

Android

Ensure your info is setup here:

More info:

iOS

Ensure your info is setup here:
https://appstoreconnect.apple.com > "Agreements, Tax & Banking"

More info: https://developer.apple.com/help/app-store-connect/getting-paid/overview-of-receiving-payments

Cancellation

https://github.com/hyochan/react-native-iap/blob/main/docs/docs/faq.mdx#how-can-a-user-cancel-a-subscription-in-my-app

Android

How to cancel: https://support.google.com/googleplay/answer/7018481?hl=en&co=GENIE.Platform%3DAndroid

Webhook that fires when cancellation happens: https://developer.android.com/google/play/billing/rtdn-reference

iOS

How to cancel: https://www.youtube.com/shorts/VhEVpWFWG48

Webhook that fires when cancellation happens: https://developer.apple.com/documentation/appstoreservernotifications

More info:


Other general and useful resources

Android

iOS

⚠️ **GitHub.com Fallback** ⚠️