react-brain
Browse decisions
React & language foundations 3App architecture 10UI 13Platform & native 9Build, test, observe, secure 6AI in React apps 3stack composerdecision recordscensusstaleness bencharchitecturechangelogmethodologyroadmap

entriesplatform-native · verified 2026-08-18 · react-native

In-app purchases & payments

reviewedconfidence: mediumthis tier holds 67% on the public scorecard (6/19 graded · 0 overturned) →

re-verified 3× — 2026-08-18 · 2026-08-04 · 2026-07-06 · changelog

recommendation

Subscriptions → RevenueCat (it absorbs receipt validation + entitlement infra). Direct store billing without a third party → expo-iap (Expo) or react-native-iap.

  • small team, subscriptions, don't want a billing backend → RevenueCat
  • one-time purchases / avoid vendor fees → expo-iap / react-native-iap (you own validation)
  • wallet passes (tickets/loyalty/coupons) → @premieroctet/react-native-wallet (set up an Apple Pass Type ID + entitlements; not tap-to-pay)
  • remote paywall config / pricing A-B tests → that is RevenueCat's tooling layer, not a separate library decision

Options & tradeoffs

the field considered — and why each one isn’t the default here

optiontradeoffevidence
expo-iap (5.3.1)OpenIAP-compliant IAP; Google Play Billing v8 / Apple StoreKit; avoids ejecting. Verified 2026-08-14 — still shipping in lockstep with react-native-iap185k/wk
react-native-iap (16.3.1)long-standing community IAP wrapper; its STANDALONE repo is archived but the package is NOT deprecated — it moved into the hyodotdev/openiap monorepo and still ships in lockstep with expo-iap (both published 2026-08-14). DIST-TAG TRAP: on both packages the `next` tag is BEHIND `latest` (react-native-iap next = 15.4.0-rc.3, expo-iap next = 4.4.0-rc.7, verified 2026-08-18) — installing @next silently downgrades you a major118k/wk
RevenueCat (react-native-purchases 10.7.1)hosted subscription infra + entitlements across stores; fees vs less server work. Also brings remote-configurable paywalls and pricing experiments — features of the hosting decision, not separate choices511k/wk
@premieroctet/react-native-wallet (1.0.2)Apple PassKit / Google Wallet passes (tickets, loyalty, coupons) — NOT tap-to-pay; iOS needs a Pass Type ID + entitlements. MIND THE PACKAGE NAME: bare `react-native-wallet` on npm is an UNRELATED 2020 module (1.0.8, erikpoort, Apple-Wallet-only); the library meant here is the scoped @premieroctet one — last published 2025-06-26, so treat it as stable-but-quiet and pin it

evidence: npm weekly downloads (signals snapshot) · “ships in n/D” = adoption across the production-app census, honest denominators

npm weekly downloads (from the corpus's last signals run): react-native-purchases 511k · react-native-iap 118k · expo-iap 185k · @premieroctet/react-native-wallet 975

Verified notes

Store rules + receipt validation make IAP testing-heavy; hosted options (RevenueCat) trade fees for less backend. STALE FACT CORRECTED (2026-08-04, firsthand watch): this entry previously called react-native-iap "deprecated/archived → prefer expo-iap". That read an archived REPO as a dead PACKAGE. Both libraries moved into the hyodotdev/openiap monorepo (the OpenIAP protocol project; the old hyochan/react-native-iap and hyochan/expo-iap repos were archived 2026-04-26 on the way out) and both are actively released from it: react-native-iap 16.0.2 and expo-iap 5.0.1 shipped the SAME day (2026-08-03), neither carries an npm deprecation flag. Pick between them on runtime (Expo module vs bare RN wrapper), not on liveness. GRADUATED drafted → reviewed 2026-08-18 (upkeep-routine step 4), and the graduation found a REAL ERROR rather than confirming the entry: the wallet option row was written as `react-native-wallet`, which on npm is an unrelated 2020 package (1.0.8, Apple-Wallet-only, erikpoort). The library the row actually describes — PassKit AND Google Wallet, from premieroctet, which the entry already linked in sources — is published as @premieroctet/react-native-wallet (1.0.2, 2025-06-26). Same failure the twir-291 spot-check caught with tsrx/octanejs: an npm NAME that resolves is not the same as the npm name resolving to YOUR library. Verify identity (repository field), not existence. Version state re-verified 2026-08-18: react-native-iap 16.3.1 and expo-iap 5.3.1 (both 2026-08-14, lockstep intact), react-native-purchases 10.7.1 (2026-08-13), no deprecation flags anywhere.

Canonical reading

Editorial annotations on why each piece matters — the articles themselves are the originals; read them there.

Cross-platform subscription state: sharing entitlements between Android and iOSJaewoong Eum (RevenueCat)

Why unifying subscription entitlements across iOS/Android is hard — Apple signed-JWS vs Google purchase-token REST, HTTPS notifications vs Cloud Pub/Sub, subscription groups vs base plans/offers — and the four-part DIY architecture. The root reason IAP libraries/services exist.

Building the RC Fortress: protecting payments against outagesRevenueCat Engineering

A resilience case study for keeping purchases working when subscription backends fail — cached paywall snapshots, temporary offline entitlements, buffered request log/replay — with tradeoffs made explicit. The realities of subscription infra regardless of library.

Sources

The full explanation

The reviewed long-form essay behind this entry — the why, not a how-to. Also on GitHub.

About in-app purchases & payments (React Native)

Diataxis: Explanation. This page builds understanding of why buying something inside a mobile app is a backend decision wearing a client-library costume, and why the recommendation splits the way it does. It is not a StoreKit or Play Billing tutorial. Token storage belongs to RB-E-STORAGE, identity to RB-E-AUTH, and "did the purchase actually work in production" to RB-E-OBSERVABILITY. Read this for the why.

The decision is not "which library" — it is "who owns entitlement state"

Every in-app purchase question eventually reduces to one: when your app asks "is this user entitled to the pro features right now?", who answers?

The client library is the easy half. It shows the store sheet, returns a receipt or purchase token, and restores purchases. That part is mechanical, and the two community options do it well.

The hard half is everything after the transaction: validating the receipt somewhere the user cannot tamper with, reconciling renewals and cancellations that happen while the app is closed, handling refunds and grace periods, and surviving the fact that Apple and Google model all of this differently. Apple hands you signed JWS payloads and HTTPS server notifications organised around subscription groups; Google hands you purchase tokens validated over REST with notifications on Cloud Pub/Sub, organised around base plans and offers. Nothing lines up. The first reading in the index entry walks exactly this asymmetry and the four-part architecture a DIY implementation ends up needing — read it before deciding to own this.

So the real question is whether you build that reconciliation service or rent it.

Renting it: RevenueCat

RevenueCat's product is the entitlement service. It ingests both stores' notifications, keeps a per-user entitlement state you query, and gives you the operational surface — remote-configured paywalls, pricing experiments, cohort reporting — that teams otherwise build in-house months later. Those extras are worth naming as features of this choice, not separate decisions to agonise over.

The tradeoff is the usual hosted one: fees, and a vendor in the critical path of revenue. The second reading in the index entry is unusually honest about the second half — it is RevenueCat's own write-up of how they keep purchases working when the backend is unavailable (cached paywall snapshots, temporary offline entitlements, a buffered request log that replays). Read it as the question you should ask any subscription vendor, including this one: what does my app do when you are down?

Owning it: expo-iap and react-native-iap

Both packages come out of the same place — the OpenIAP project (hyodotdev/openiap), which is the useful thing to understand here. They are not competitors so much as two runtimes for one protocol: expo-iap (5.3.1) is the Expo module, react-native-iap (16.3.1) the bare-RN wrapper, and they ship in lockstep — both published 2026-08-14.

That lockstep matters because this entry previously carried the opposite belief. Until 2026-08-04 it said react-native-iap was "deprecated/archived → prefer expo-iap", which read an archived repository as a dead package. The standalone repos (hyochan/react-native-iap, hyochan/expo-iap) were archived 2026-04-26 while the code moved into the monorepo; neither package carries an npm deprecation flag. Pick between them on runtime — Expo module vs bare wrapper — not on liveness.

One trap worth knowing before you install: on both packages the next dist-tag is behind latest (react-native-iap next = 15.4.0-rc.3, expo-iap next = 4.4.0-rc.7, verified 2026-08-18). Reaching for @next here silently downgrades you a major version.

Owning it means you also own receipt validation on a server you control, and the testing burden that comes with store sandboxes. That is the fee you pay instead of RevenueCat's.

The thing that is not a purchase at all: wallet passes

Tickets, loyalty cards and coupons are Apple PassKit / Google Wallet objects, not purchases, and they are not tap-to-pay. The library is @premieroctet/react-native-wallet (1.0.2), and on iOS you need a Pass Type ID and matching entitlements before any of it runs.

Note the scope. Bare react-native-wallet on npm is an unrelated 2020 package (1.0.8, Apple-Wallet only). This entry named that one by mistake until 2026-08-18 — a reminder that an npm name which resolves is not the same as an npm name resolving to your library. The scoped package is stable but quiet (last published 2025-06-26): pin it, and expect to read its source if the platform moves.

How to read the recommendation

  1. Subscriptions, and you do not want to run a reconciliation service? RevenueCat. Ask the outage question up front.
  2. One-time purchases, or you refuse the vendor fee? expo-iap on Expo, react-native-iap on bare RN — and budget the server-side validation as real work, not a follow-up ticket.
  3. Passes, not purchases? @premieroctet/react-native-wallet, with the Pass Type ID setup.
  4. Whichever you pick, test on real store sandboxes early. Store rules and receipt validation are what make this domain testing-heavy; no library removes that.

Related in platform-native: native · media · storage · native-ui · keyboard · brownfield · games · alt-frameworks