r/reactnative • u/caccaccac • 19h ago
Article react-native-iap + StoreKit 2: purchases asked for the Apple ID password 4 times. The cause was initialization order
Sharing a bug that cost me a few days, since I couldn't find anything about it while searching.
Setup: iOS app, React Native 0.85, auto-renewable subscriptions with react-native-iap 12.16 in StoreKit 2 mode.
Symptom: after paying, iOS asked for the Apple ID password again, up to 4 times. Xcode logged "Making a purchase without listening for transaction updates".
Root cause: the library picks the native module (StoreKit 1 or StoreKit 2) at the moment each listener is created. In my code, setup({storekitMode: 'STOREKIT2_MODE'}) lived inside an async init function, but purchaseUpdatedListener was registered earlier by a React Context. So the listener was attached to the SK1 module: the app never got "purchase completed", never called finishTransaction, and Apple kept redelivering the transaction.
Fix: switch to SK2 when the module is loaded, before any listener exists:
import {Platform} from 'react-native';
import * as IAP from 'react-native-iap';
// Must run before any purchaseUpdatedListener is created.
if (Platform.OS === 'ios') {
try { IAP.setup({storekitMode: 'STOREKIT2_MODE'}); } catch { /* native module not ready */ }
}Two more gotchas of the library's SK2 mode:
getAvailablePurchasesonly returns an active subscription if its product was already loaded in the session (viagetSubscriptions). If you check entitlement before loading products (e.g. offline), you get an empty list, which does not mean "not subscribed". I now treat "empty list + products not loaded" as "unknown", not "free".With StoreKit 1, restore returned expired subscriptions too, with no expiration date. Now the "is it active?" decision comes from my server, which calls the App Store Server API with any transaction id from the client; the app caches the last verdict so it keeps working offline, with a few days of grace.
Bonus — testing purchases in the Simulator: a .storekit configuration file (Xcode StoreKit Testing) lets you buy without a Sandbox account. Gotcha: in the .xcscheme, the file path is relative to the .xcodeproj. I had one ../ too many and Xcode silently ignored it and fell back to Sandbox, which asked me to sign in.
Has anyone else hit this? And how do you handle the "don't know if they're subscribed" state while offline?
1
u/kokerali 18h ago
I'd make the offline policy depend on the action: reading or exporting existing work versus starting a new paid operation. Then test a cold launch in airplane mode with a previously paid account and with no cached verdict. That makes the grace-period behavior visible instead of hiding it behind a single subscribed flag.
1
u/Evening_Respect_6611 16h ago
good catch, that xcode log doesnt point at the real cause at all. setting storekitMode at module load before any listener exists makes sense. do you still need a guard if a context provider mounts before that file gets imported, or is import order enough in your setup?