You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RevenueCat活跃权限重启后返回false问题(疑似缓存问题)

问题场景与疑问

用户完成订阅调用purchase()后,订阅成功可正常跳转登录页,登录后进入首页。但退出重启应用后,付费墙再次弹出,而非直接进入首页。调用Purchases.getCustomerInfo()时,info?.entitlements?.active?.[entitlementName].isActive返回false,点击「恢复购买」后恢复正常。已知getCustomerInfo存在缓存机制,且等待5分钟后刷新也能恢复状态,确认是缓存导致的问题。现需解决两个核心问题:

  1. 如何正确初始化RevenueCat并检查购买权限
  2. 缓存导致的订阅状态不一致问题的解决办法

当前代码实现

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);
      }
    }
};

解决方案

一、正确初始化与购买权限检查优化

  1. 确保用户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]);
    
  2. 强化权限检查逻辑
    不要仅依赖服务器返回的缓存数据,结合本地存储的isActive状态做兜底:

    • 若本地存储显示已订阅,但服务器返回无有效订阅,立即触发强制刷新
    • 若本地和服务器状态一致,再更新UI状态

二、缓存问题解决办法

  1. 添加强制刷新机制
    修改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]);
    
  2. 监听订阅状态实时更新
    使用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]);
    
  3. 恢复购买时强制刷新
    在「恢复购买」逻辑中,调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 21:20:10