React Native 应用内购买与付费墙概述

0:02:00

欢迎来到 RevenueCat 的 React Native SDK Codelab!

目标: 在本 Codelab 中,您将学习如何将 RevenueCat React Native SDK 集成到新的 React Native 应用程序中,以获取产品方案并显示预构建的原生付费墙 UI。

您将构建的内容:

一个简单的单屏应用,显示加载指示器,从 RevenueCat 获取您配置的"产品方案",然后显示 RevenueCatUI 付费墙。

前提条件

在开始之前,您必须完成以下设置:

  1. React Native 环境: 您的 React Native 开发环境应已准备就绪,包括 Node.js、React Native CLI 和所需的平台特定工具。
  2. 平台要求:
iOS:最低部署目标为 13.4 Android:最低 API 级别 23(版本 6.0) React Native 版本 0.64 或更高
  1. RevenueCat 账户:revenuecat.com 注册的免费账户。
  2. App Store / Play Store 配置:
在 App Store Connect 或 Google Play Console 中创建的应用内产品(订阅或一次性购买)。如果您想了解更多关于配置 Google Play 的信息,请查看 Codelab1:RevenueCat Google Play 集成
  • 在您的 RevenueCat 控制台中将此产品链接到权益产品方案。这是最关键的步骤。如果您没有"当前"产品方案,付费墙将不会显示。
  1. 物理设备或配置好的模拟器: 用于测试应用内购买。

完成本 Codelab 后,您将能够在 React Native 应用中成功实现应用内购买,并使用 RevenueCat 的 React Native SDK 显示动态付费墙。

概述

导入 RevenueCat SDK

0:05:00

首先,在实现应用内购买之前,您需要将 RevenueCat SDK 导入到您的现有或新项目中。首先,将以下依赖项添加到您的项目中:

您可以在 GitHub 上查看最新发布版本

安装

使用 npm:

bash
npm install --save react-native-purchases

或使用 yarn:

bash
yarn add react-native-purchases

平台特定配置

iOS 设置

对于 iOS,您需要启用"应用内购买"功能:

  1. 在 Xcode 中打开您的项目
  2. 选择您的项目目标
  3. 转到"Signing & Capabilities"
  4. 点击"+ Capability"并添加"In-App Purchase"

Android 设置

对于 Android,将计费权限添加到您的 AndroidManifest.xml

xml
<uses-permission android:name="com.android.vending.BILLING" />

此外,确保您的 Activity 的 launchModeAndroidManifest.xml 中设置为 standardsingleTop

xml
<activity
  android:name=".MainActivity"
  android:launchMode="standard"
  ...
/>

初始化 RevenueCat SDK

现在,让我们初始化 RevenueCat SDK。我们将在您的主 App.jsApp.tsx 文件中完成此操作。

  1. 打开 App.js(或 TypeScript 项目的 App.tsx
  2. 在文件顶部导入 SDK
  3. 将您的 API 密钥粘贴到常量中
javascript
// App.js

import React, { useEffect, useState } from 'react';
import { Platform, View, Text, ActivityIndicator, StyleSheet } from 'react-native';
import Purchases from 'react-native-purchases';

// --- 在此处粘贴您的 REVENUECAT 密钥 ---
const androidApiKey = 'goog_YOUR_KEY_HERE';
const iosApiKey = 'appl_YOUR_KEY_HERE';

const App = () => {
  const [isConfigured, setIsConfigured] = useState(false);

  useEffect(() => {
    const initializePurchases = async () => {
      try {
        // 为开发启用调试日志
        Purchases.setLogLevel(Purchases.LOG_LEVEL.DEBUG);

        // 使用适当的 API 密钥配置 SDK
        if (Platform.OS === 'ios') {
          await Purchases.configure({ apiKey: iosApiKey });
        } else if (Platform.OS === 'android') {
          await Purchases.configure({ apiKey: androidApiKey });
        }

        console.log('RevenueCat 配置成功!');
        setIsConfigured(true);
      } catch (e) {
        console.error('配置 RevenueCat 时出错:', e);
      }
    };

    initializePurchases();
  }, []);

  if (!isConfigured) {
    return (
      <View style={styles.centered}>
        <ActivityIndicator size="large" />
        <Text style={styles.loadingText}>正在初始化 RevenueCat...</Text>
      </View>
    );
  }

  // 主应用内容将在后续步骤中添加
  return (
    <View style={styles.container}>
      <Text>RevenueCat 已准备就绪!</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#fff',
  },
  centered: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  loadingText: {
    marginTop: 10,
    fontSize: 16,
  },
});

export default App;

我们刚刚创建了一个基本的 React Native 应用,使用 useEffect 钩子初始化 RevenueCat SDK。这确保在进行任何购买之前 SDK 已配置并准备就绪。这是初始化需要从一开始就可用的插件的正确方式。

太棒了!您现在已经完成了 50% 的实现工作。

验证用户权益

0:03:00

现在让我们继续验证用户权益。

如前所述,权益(Entitlement)代表用户在购买后解锁的访问级别或功能。这对于确定是否显示广告横幅或授予高级访问权限等场景非常有用。

您可以使用以下代码片段轻松检查用户是否拥有活跃的权益:

javascript
const ENTITLEMENT_IDENTIFIER = ".."; // 从您的 RevenueCat 控制台获取特定的权益标识符

try {
  const customerInfo = await Purchases.getCustomerInfo();
  const isEntitled = customerInfo.entitlements.active[ENTITLEMENT_IDENTIFIER]?.isActive;

  if (isEntitled) {
    // 用户有权访问此权益
    console.log('用户有权益');
  } else {
    // 用户没有访问权限
    console.log('用户没有权益');
  }
} catch (e) {
  console.error('检查权益时出错:', e);
}

一旦您检查了用户是否拥有特定权益,就可以根据您应用的商业模式决定如何继续。

例如,如果您的应用是广告支持的,您可能会选择显示或隐藏 AdMob 横幅。或者,您可以选择显示付费墙或购买对话框,让用户解锁高级功能或内容。

以下是如何实现该逻辑的示例:

javascript
if (isEntitled) {
  // 如果用户被授予此权益的访问权限,则不需要显示横幅。
} else {
  // 在此处显示横幅 UI 或显示付费墙
}

实现应用内购买

0:04:00

现在,让我们实现应用内购买以提供无广告体验。首先,您需要从 RevenueCat 控制台获取相关产品信息。这些产品数据将用于向用户展示购买选项。

您可以通过调用 Purchases.getProducts() 来获取可用产品,如以下示例所示:

javascript
// 从 RevenueCat 服务器获取产品信息
const products = await Purchases.getProducts(['paywall_tester.subs']);

// 执行应用内购买
const purchaseResult = await Purchases.purchaseStoreProduct(products[0]);

如果您提供多种产品变体,例如 paywall_tester.subs:weeklypaywall_tester.subs:monthlypaywall_tester.subs:yearly,您可以使用基础产品标识符 paywall_tester.subs 作为 productIds 字段的值来简化产品获取。这将告诉 RevenueCat 以列表形式获取所有相关产品变体,以便您可以在付费墙 UI 中动态展示它们。

一旦您获取了产品数据,就可以通过调用 Purchases.purchaseStoreProduct(product) 来启动应用内购买流程。这将自动触发 Google Play 购买对话框Apple 的购买弹窗,使用户能够在您的应用内完成交易。

就这样,您只用几行代码就集成了一个完整功能的应用内购买流程——无需处理收据、商店 API 或手动购买验证的复杂性。

完整的代码示例如下所示:

javascript
import Purchases from 'react-native-purchases';

/**
 * 通过产品 ID 获取特定产品并启动购买流程。
 *
 * 此函数处理潜在错误,如产品未找到、
 * 用户取消购买或其他商店错误。
 */
const purchaseProduct = async () => {
  // 1. 定义要获取的产品标识符。
  const productId = 'paywall_tester.subs';

  try {
    // 2. 从 RevenueCat 获取 StoreProduct
    console.log('正在获取产品...');
    const products = await Purchases.getProducts([productId]);

    // 3. 检查产品列表是否不为空
    if (products.length === 0) {
      console.error('错误:未找到产品。请检查 ID 和您的 RevenueCat 设置。');
      // 可选:向用户显示错误消息
      return;
    }

    const productToPurchase = products[0];
    console.log('找到产品:', productToPurchase.title, '。正在启动购买...');

    // 4. 启动购买流程
    const { customerInfo } = await Purchases.purchaseStoreProduct(productToPurchase);

    // 5. 通过验证权益来检查购买是否成功
    // 将 "your_premium_entitlement" 替换为您在 RevenueCat 中的实际权益标识符
    if (customerInfo.entitlements.active['your_premium_entitlement']?.isActive) {
      console.log('购买成功!用户现在拥有高级访问权限。');
      // 授予高级内容访问权限
    } else {
      console.log('购买完成,但权益未激活。');
    }
  } catch (e) {
    // 6. 处理潜在错误
    if (e.code === Purchases.PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {
      console.log('用户取消了购买。');
    } else {
      console.error('购买失败,错误:', e.message);
    }
  }
};

实现付费墙

0:07:00

现在,是时候在 React Native 中实现付费墙了。

步骤 1:安装 RevenueCatUI 包

首先,安装提供预构建付费墙组件的 RevenueCatUI 包:

bash
npm install --save react-native-purchases-ui
# 或
yarn add react-native-purchases-ui

步骤 2:导入 RevenueCatUI

在文件顶部导入付费墙展示方法:

javascript
import { presentPaywallIfNeeded } from 'react-native-purchases-ui';

步骤 3:获取产品方案并显示付费墙

让我们更新 App.js 以获取产品方案并显示付费墙。用以下代码替换您现有的应用代码:

javascript
// App.js

import React, { useEffect, useState } from 'react';
import {
  Platform,
  View,
  Text,
  ActivityIndicator,
  StyleSheet,
  SafeAreaView,
} from 'react-native';
import Purchases from 'react-native-purchases';
import { presentPaywallIfNeeded } from 'react-native-purchases-ui';

// --- 在此处粘贴您的 REVENUECAT 密钥 ---
const androidApiKey = 'goog_YOUR_KEY_HERE';
const iosApiKey = 'appl_YOUR_KEY_HERE';

const App = () => {
  const [isConfigured, setIsConfigured] = useState(false);
  const [isLoading, setIsLoading] = useState(true);
  const [offering, setOffering] = useState(null);
  const [errorMessage, setErrorMessage] = useState(null);

  useEffect(() => {
    const initializePurchases = async () => {
      try {
        // 为开发启用调试日志
        Purchases.setLogLevel(Purchases.LOG_LEVEL.DEBUG);

        // 使用适当的 API 密钥配置 SDK
        if (Platform.OS === 'ios') {
          await Purchases.configure({ apiKey: iosApiKey });
        } else if (Platform.OS === 'android') {
          await Purchases.configure({ apiKey: androidApiKey });
        }

        console.log('RevenueCat 配置成功!');
        setIsConfigured(true);
      } catch (e) {
        console.error('配置 RevenueCat 时出错:', e);
        setErrorMessage('初始化 RevenueCat 失败');
        setIsLoading(false);
      }
    };

    initializePurchases();
  }, []);

  useEffect(() => {
    if (!isConfigured) return;

    const loadOfferings = async () => {
      try {
        setIsLoading(true);
        setErrorMessage(null);

        // 从 RevenueCat 获取当前产品方案
        const offerings = await Purchases.getOfferings();

        if (offerings.current !== null) {
          setOffering(offerings.current);
        } else {
          setErrorMessage('未找到当前产品方案。请检查您的 RevenueCat 控制台。');
        }
      } catch (e) {
        console.error('获取产品方案时出错:', e);
        setErrorMessage(`加载产品方案失败: ${e.message}`);
      } finally {
        setIsLoading(false);
      }
    };

    loadOfferings();
  }, [isConfigured]);

  useEffect(() => {
    if (!offering) return;

    const showPaywall = async () => {
      try {
        const paywallResult = await presentPaywallIfNeeded({
          offering: offering,
        });

        console.log('付费墙结果:', paywallResult);

        if (paywallResult === Purchases.PAYWALL_RESULT.PURCHASED) {
          console.log('用户完成了购买!');
        } else if (paywallResult === Purchases.PAYWALL_RESULT.RESTORED) {
          console.log('用户恢复了购买!');
        } else if (paywallResult === Purchases.PAYWALL_RESULT.CANCELLED) {
          console.log('用户取消了付费墙');
        }
      } catch (e) {
        console.error('展示付费墙时出错:', e);
      }
    };

    showPaywall();
  }, [offering]);

  if (isLoading) {
    return (
      <SafeAreaView style={styles.centered}>
        <ActivityIndicator size="large" color="#6366f1" />
        <Text style={styles.loadingText}>正在加载付费墙...</Text>
      </SafeAreaView>
    );
  }

  if (errorMessage) {
    return (
      <SafeAreaView style={styles.centered}>
        <Text style={styles.errorText}>{errorMessage}</Text>
      </SafeAreaView>
    );
  }

  return (
    <SafeAreaView style={styles.container}>
      <Text style={styles.title}>RevenueCat 付费墙演示</Text>
      <Text style={styles.subtitle}>付费墙显示成功!</Text>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#fff',
    padding: 20,
  },
  centered: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#fff',
  },
  loadingText: {
    marginTop: 16,
    fontSize: 16,
    color: '#666',
  },
  errorText: {
    fontSize: 16,
    color: '#ef4444',
    textAlign: 'center',
    padding: 20,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 8,
    color: '#111',
  },
  subtitle: {
    fontSize: 16,
    color: '#666',
  },
});

export default App;
我们刚刚做了什么?
  1. 我们创建了状态变量来管理加载状态、产品方案和错误消息。
  2. 我们首先在组件挂载时初始化 RevenueCat SDK。
  3. 配置完成后,我们使用 Purchases.getOfferings() 获取产品方案。
  4. 当产品方案可用时,我们使用 presentPaywallIfNeeded() 自动展示付费墙。
  5. 我们处理各种付费墙结果,包括购买、恢复和取消。

步骤 4:运行您的应用程序

完成!

现在,在设备或模拟器上运行您的应用程序:

对于 iOS:
bash
npx react-native run-ios
对于 Android:
bash
npx react-native run-android
您应该看到:
  1. 带有加载器的"正在加载付费墙..."消息。
  2. 然后是您完整功能的原生付费墙 UI,如下图所示。
结果

故障排除

如果付费墙未显示,请检查调试控制台并查看这些常见问题: "未找到当前产品方案":这是最常见的错误。这意味着您的 RevenueCat 控制台缺少"当前"产品方案。前往产品方案,选择一个产品方案,并确保它被标记为"当前"。 API 密钥错误:仔细检查您是否复制了正在测试的平台的正确公共 API 密钥。 产品未配置:确保您在商店控制台中的应用内产品已正确设置,并在 RevenueCat 中链接到权益和产品方案。 沙盒/测试用户问题:确保您使用沙盒测试账户登录(iOS)或您的电子邮件已添加为许可证测试人员(Android)。 平台特定问题 iOS:验证 In-App Purchase 功能已在 Xcode 中启用
  • Android:检查 BILLING 权限是否在 AndroidManifest.xml 中

配置完成!现在,每当用户没有所需权益时,您就可以显示付费墙,使用的正是您在付费墙编辑器中配置的设计。

正如您在 Codelab:RevenueCat Google Play 集成(创建付费墙)中所见,付费墙系统建立在服务端驱动的 UI 之上。这意味着您可以直接从控制台动态更新付费墙的内容和设计,无需推送应用更新或经过审核流程。

总结

在本 Codelab 中,您学习了如何集成 RevenueCat 的 React Native SDK、实现应用内购买以及在 React Native 中构建付费墙。现在是时候发布您的应用并赚取更多收入了!

您还可以通过以下资源了解更多关于使用 RevenueCat SDK 的信息: