RevenueCat活跃权限重启后返回false问题(疑似缓存问题)
问题场景与疑问
用户完成订阅调用purchase()后,订阅成功可正常跳转登录页,登录后进入首页。但退出重启应用后,付费墙再次弹出,而非直接进入首页。调用Purchases.getCustomerInfo()时,info?.entitlements?.active?.[entitlementName].isActive返回false,点击「恢复购买」后恢复正常。已知getCustomerInfo存在缓存机制,且等待5分钟后刷新也能恢复状态,确认是缓存导致的问题。现需解决两个核心问题:
- 如何正确初始化RevenueCat并检查购买权限
- 缓存导致的订阅状态不一致问题的解决办法
当前代码实现
const [isActive = false, setIsActive] = useMMKVBoolean( storageKeys.purchases.isActive ); // 本地存储订阅状态 const [userId, setUserId] = useMMKVString(storageKeys.user.id); // 本地存储用户ID const [isReady, setIsReady] = useState(false); const [sawPaywall, setSawPaywall] = useState(false); const [isLoadingRestoreData, setIsLoadingRestoreData] = useState(false); // 初始化配置 useMySWR({ key: 'init_purchases', fetcher: async () => { try { if (!APIKEY) { throw new Error('Missing API Key for purchases'); } if (isReady) return; await Purchases.setLogLevel(Purchases.LOG_LEVEL.DEBUG); Purchases.configure({ apiKey: APIKEY, appUserID: userId }); await getInfo(); } catch (e) { console.error((e as Error).message); !__DEV__ && Bugsnag.notify({ name: 'initPurchaseConfig Error', message: (e as Error).message, }); Bugsnag.leaveBreadcrumb('initPurchaseConfig Error', { country: JSON.stringify(getLocales()?.[0]), }); setMessage(['error', (e as Error).message]); } finally { setIsReady(true); } }, }); const getInfo = useCallback(async () => { try { const info = await Purchases.getCustomerInfo(); packageRef.current = info?.entitlements?.active?.[entitlementName]?.productIdentifier; const { isActive: isEntitled } = info?.entitlements?.active?.[entitlementName] || {}; setIsActive(!!isEntitled); if (isEntitled) { setSawPaywall(true); } return info; } catch (e) { throw new Error((e as Error).message); } }, [setIsActive]); const purchase = async (purchasePackage: PurchasesPackage) => { try { await Purchases.purchasePackage(purchasePackage); await getInfo(); } catch (e) { if ((e as Error).message !== 'Purchase was cancelled.') { throw new Error((e as Error).message); } } };
解决方案
一、正确初始化与购买权限检查优化
确保用户ID就绪后再初始化
原代码使用useMySWR初始化,可能在userId未完全就绪时执行配置,导致用户身份不匹配。改用useEffect监听userId变化,仅当userId存在且未初始化时执行RevenueCat配置:useEffect(() => { const initPurchases = async () => { if (!APIKEY || !userId || isReady) return; try { await Purchases.setLogLevel(Purchases.LOG_LEVEL.DEBUG); await Purchases.configure({ apiKey: APIKEY, appUserID: userId }); // 本地有订阅记录时强制刷新服务器数据 await getInfo(isActive); } catch (e) { // 保留原有错误处理逻辑 } finally { setIsReady(true); } }; initPurchases(); }, [userId, isReady, isActive]);强化权限检查逻辑
不要仅依赖服务器返回的缓存数据,结合本地存储的isActive状态做兜底:- 若本地存储显示已订阅,但服务器返回无有效订阅,立即触发强制刷新
- 若本地和服务器状态一致,再更新UI状态
二、缓存问题解决办法
添加强制刷新机制
修改getInfo方法,支持传入参数强制从服务器拉取最新数据,跳过缓存:const getInfo = useCallback(async (forceRefresh = false) => { try { const fetchOptions = forceRefresh ? { fetchPolicy: Purchases.FETCH_POLICY.FORCE_REFRESH } : {}; const info = await Purchases.getCustomerInfo(fetchOptions); packageRef.current = info?.entitlements?.active?.[entitlementName]?.productIdentifier; const { isActive: isEntitled } = info?.entitlements?.active?.[entitlementName] || {}; setIsActive(!!isEntitled); if (isEntitled) setSawPaywall(true); return info; } catch (e) { throw new Error((e as Error).message); } }, [setIsActive]);监听订阅状态实时更新
使用RevenueCat的监听API,实时同步服务器端的订阅状态变化,避免缓存过期前的状态不一致:useEffect(() => { if (!isReady) return; // 添加订阅状态更新监听 const listener = Purchases.addCustomerInfoUpdateListener((latestInfo) => { const { isActive: isEntitled } = latestInfo?.entitlements?.active?.[entitlementName] || {}; setIsActive(!!isEntitled); if (isEntitled) setSawPaywall(true); }); // 组件卸载时移除监听 return () => Purchases.removeCustomerInfoUpdateListener(listener); }, [isReady]);恢复购买时强制刷新
在「恢复购买」逻辑中,调用getInfo(true)强制刷新,确保立即同步服务器最新状态:const restorePurchases = async () => { setIsLoadingRestoreData(true); try { await Purchases.restorePurchases(); await getInfo(true); // 强制刷新,跳过缓存 } catch (e) { // 错误处理逻辑 console.error('恢复购买失败:', e); } finally { setIsLoadingRestoreData(false); } };
内容的提问来源于stack exchange,提问作者Phil Lucks
相关产品推荐
相关产品推荐

