Users who reinstall an app or transfer to a new device lose any premium state stored only in local storage (AsyncStorage, SharedPreferences, UserDefaults, MMKV). CWE-311 (Missing Encryption of Sensitive Data) is a related vector — but the core failure here is data-integrity: the entitlement record does not survive the lifecycle events it must. The business consequence is straightforward: paying users who lose their subscription after a reinstall file chargebacks directly with their bank or credit card company, which harms the developer's payment standing. They also leave negative reviews and generate disproportionate support volume. ISO/IEC 25010 reliability requirements apply — a subscription entitlement is a durable system state that must survive device lifecycle events.
High because entitlement loss after reinstall causes paying users to file chargebacks and leave negative reviews, with direct financial and reputational impact.
Call the platform entitlement refresh on every app startup — not only after a purchase. Never rely solely on a local flag that gets wiped on reinstall.
// App.tsx or your app initialization hook
useEffect(() => {
const restoreEntitlements = async () => {
if (isLoggedIn) {
const customerInfo = await Purchases.getCustomerInfo();
setIsPremium(customerInfo.entitlements.active['premium'] !== undefined);
}
};
restoreEntitlements();
}, [isLoggedIn]);
For server-based entitlements, fetch subscription status from your backend (GET /api/user/subscription or equivalent) at login and on app-foreground events. Store the result in memory or a local cache that is treated as derived, not authoritative — the server or platform SDK is the source of truth.
app-store-iap-subscriptions.restore-entitlements.reinstall-survivalhighAsyncStorage only (React Native), SharedPreferences only (Android), UserDefaults only (iOS), MMKV only — all of these are wiped on app reinstall or device transfer, meaning premium status is lost. The correct patterns are: (1) server-side entitlement: premium status fetched from your backend at login (keyed to user account, not device); (2) RevenueCat/Adapty/Qonversion customerInfo fetched fresh on app launch — these query Apple/Google servers and survive reinstall; (3) StoreKit 2's Transaction.currentEntitlements sequence (async stream) — queries the App Store on-demand and survives reinstall. Search for a startup sequence that restores entitlements: Purchases.getCustomerInfo() in App.tsx or equivalent initialization, a server call like GET /api/user/subscription or GET /api/entitlements in the auth/startup flow, or for await (let transaction in Transaction.currentEntitlements) in Swift. Fail pattern: premium flag written to AsyncStorage at purchase time and read from AsyncStorage at startup — this data does not survive reinstall.getCustomerInfo(), or StoreKit 2 currentEntitlements) or from a server database on app startup or at login. At least 1 implementation must be verified. Premium access does not depend solely on local device storage.AsyncStorage, SharedPreferences, UserDefaults, MMKV, SQLite on-device) with no mechanism to re-derive it from the store or a server after reinstall."isPremium flag stored in AsyncStorage at purchase time and read from AsyncStorage on startup — premium status is permanently lost after app reinstall" or "No call to Purchases.getCustomerInfo() or equivalent on app startup — entitlements not refreshed from the platform store"// App.tsx or your app initialization
useEffect(() => {
const restoreEntitlements = async () => {
if (isLoggedIn) {
const customerInfo = await Purchases.getCustomerInfo();
setIsPremium(customerInfo.entitlements.active['premium'] !== undefined);
}
};
restoreEntitlements();
}, [isLoggedIn]);