EasyStarter logoEasyStarter

RevenueCat 内购

配置 iOS 和 Android 订阅、内购与 Webhook

接入 RevenueCat 内购

EasyStarter 移动端使用 RevenueCat 管理 iOS 和 Android 的订阅与内购。RevenueCat 负责与 App Store / Google Play 对接,并通过 Webhook 将订阅状态同步到服务端。

前置准备

在开始之前,请确保以下账号和配置已就绪:

  • RevenueCat 账号(免费开始)
  • iOS:Apple Developer 账号,App Store Connect 中已创建 App 内购买项目
  • Android:Google Play Console 账号,已创建订阅产品

前置:在商店后台创建产品

在配置 RevenueCat 之前,需要先在 App Store Connect 和 Google Play Console 中创建订阅和内购产品,并记录每个产品的 产品 ID

详细步骤请参阅 创建内购产品 章节。

在 RevenueCat 后台配置

注册并登录 RevenueCat 后,按以下步骤完成后台配置。

1. 创建 App

进入 Project Settings → Apps 页面,点击 New app configuration,选择平台(iOS 选 App Store,Android 选 Google Play)。

配置完成后点击 Save changes

2. 导入 Products

Products 是 RevenueCat 对 App Store / Google Play 中具体商品的映射。

进入 Product catalog → Products,点击右上角 Import 按钮。RevenueCat 会自动拉取您在商店后台已创建的产品列表,勾选所有需要的产品(包括月度订阅、年度订阅、终身买断等),点击 Import 完成导入。

如果 Import 后列表为空,说明商店凭据尚未生效或产品尚未在商店后台审核通过。iOS 产品需要先创建好 In-app purchase 并处于"Ready to Submit"及以上状态才能被拉取到。

导入完成后,每个 Product 会显示其 Product Identifier(即 App Store / Google Play 中的产品 ID)。请记录这些 ID,后续在 app-config.ts 中会用到。

3. 配置 Offerings

Offerings 定义了向用户展示的购买方案组合,每个 Offering 下可包含多个 Package(对应不同时长或类型的产品)。

  1. 进入 Product catalog → Offerings,RevenueCat 默认会有一个名为 default 的 Offering。
  2. 点击 default 进入详情页,点击 Add Package,依次添加以下三个 Package:
    • Monthly:Package Type 选 Monthly,关联对应的月度订阅 Product
    • Yearly:Package Type 选 Annual,关联对应的年度订阅 Product
    • Lifetime:Package Type 选 Lifetime,关联对应的终身买断 Product
  3. 每个 Package 添加后,在右侧面板中选择对应的 Product(iOS 和 Android 分别选择),然后点击 Save

Offerings 决定了 App 内显示哪些购买方案。您可以创建多个 Offering 用于 A/B 测试,但 SDK 默认使用 default Offering。

4. 配置 Entitlements

Entitlements 定义了用户购买后获得的权益(即解锁哪些功能)。通常一个 App 只需要一个 Entitlement,例如 pro

  1. 进入 Product catalog → Entitlements,点击右上角 New 按钮,填写 Identifier(例如 pro),点击 Save 创建。
  2. 进入该 Entitlement 的详情页,点击 Attach,在弹出的产品列表中勾选所有需要授予此权益的 Products(Monthly、Yearly、Lifetime 三个都勾选),点击 Attach 保存。

Entitlement Identifier 需要与 eas.json 中的 EXPO_PUBLIC_REVENUECAT_ENTITLEMENT_ID 保持一致。如果您使用 pro,则环境变量也填 pro

Entitlement 配置完成后,当用户购买任意一个已关联的 Product 时,RevenueCat 会自动将该 Entitlement 标记为 active,App 端通过 SDK 即可判断用户是否有权限。

5. 配置付费墙(可选)

RevenueCat 内置了可视化付费墙编辑器,支持无需发版即可远程修改内购页面。

进入 Paywalls 页面,点击 New paywall,从内置模板中选择一个样式开始编辑:

  • 选择要绑定的 Offering(选 default
  • 填写付费墙名称
  • 配置隐私协议和服务条款的 URL

根据 App Store 审核指南,内购页面必须包含隐私协议和服务条款的链接,否则可能被拒审。

如果所选模板没有 Lifetime Package 的位置,可以复制一个现有 Package 区块,将其类型改为 Lifetime,并修改对应的展示文案。编辑完成后点击 Publish 发布,付费墙即可在 App 中生效。

更多付费墙变量和自定义选项参考 RevenueCat 官方文档

6. 获取 API Key 和 Entitlement ID

获取 SDK API Key:进入 Project Settings → API Keys,找到对应平台的 Public SDK key(iOS 以 appl_ 开头,Android 以 goog_ 开头),点击 Show Key → Copy,分别填入 eas.jsonEXPO_PUBLIC_REVENUECAT_IOS_API_KEYEXPO_PUBLIC_REVENUECAT_ANDROID_API_KEY

获取 Secret API Key:在同一页面找到 Secret keys,复制后填入服务端环境变量 REVENUECAT_SECRET_API_KEY(用于服务端查询订阅状态)。

获取 Entitlement ID:进入 Product catalog → Entitlements,复制您创建的 Entitlement 的 Identifier,填入 eas.jsonEXPO_PUBLIC_REVENUECAT_ENTITLEMENT_ID

7. 配置 Webhook(同步订阅状态到服务端)

RevenueCat 通过 Webhook 将订阅事件(购买、续费、取消等)实时推送到 Server。EasyStarter 的服务端已内置处理逻辑,只需配置好 Webhook URL 即可。

开发环境(ngrok)

本地开发时 Server 运行在 localhost,RevenueCat 无法直接访问。需要使用 ngrok 将本地端口暴露到公网。

  1. 安装 ngrok(如果尚未安装):

    brew install ngrok
  2. 启动本地 Server:

    pnpm dev:server

    默认监听 http://localhost:3001

  3. 开启 ngrok 隧道:

    ngrok http 3001

    ngrok 会输出一个公网 URL,例如:

    Forwarding  https://a1b2-123-456-789.ngrok-free.app -> http://localhost:3001
  4. 在 RevenueCat Dashboard → Project Settings → Integrations → WebhooksAdd webhook,Webhook URL 填写:

    https://a1b2-123-456-789.ngrok-free.app/api/webhooks/revenuecat
  5. Authorization header 字段中填入你自己生成的随机密钥。推荐使用完整的 Bearer Token 格式:

    # 先生成一个随机值
    openssl rand -hex 32

    将生成的值拼上 Bearer 前缀,例如 Bearer a1b2c3d4...,填入 RevenueCat Webhook 配置的 Authorization header 字段。

    然后把同一个完整值填入 apps/server/.dev.vars

    apps/server/.dev.vars
    REVENUECAT_WEBHOOK_SECRET=Bearer your_random_secret

REVENUECAT_WEBHOOK_SECRET 是你自己生成并配置的值,而不是 RevenueCat 颁发的密钥。RevenueCat 每次调用 Webhook 时会将这个值作为 Authorization 请求头原样带过来,服务端再做比对验证。建议直接写完整的 Bearer xxx 形式,最清晰且不易配错。

ngrok 免费版每次重启隧道 URL 会变化,需要同步更新 RevenueCat 后台的 Webhook URL。如果频繁调试,可以注册 ngrok 账号使用固定域名。

生产环境

部署到 Cloudflare Workers 后,Server 拥有固定的公网地址,直接配置即可。

  1. 在 RevenueCat Dashboard → Project Settings → Integrations → WebhooksAdd webhook,Webhook URL 填写:

    https://your-server.workers.dev/api/webhooks/revenuecat
  2. Authorization header 字段中填入你自己生成的随机密钥(与开发环境使用相同的格式):

    openssl rand -hex 32

    将生成值拼上 Bearer 前缀,例如 Bearer a1b2c3d4...,填入 RevenueCat Webhook 配置的 Authorization header 字段,同时将同一个完整值填入 apps/server/.env.production(或通过 wrangler secret 管理):

    apps/server/.env.production
    REVENUECAT_WEBHOOK_SECRET=Bearer your_random_secret
  3. 推送 Secret 到 Cloudflare:

    pnpm -F server secrets:bulk:production

更新 eas.json 环境变量

将获取的 API Keys 和 Entitlement ID 填入 apps/native/eas.json 中各 profile 的 env 块:

apps/native/eas.json
{
  "build": {
    "development": {
      "env": {
        "EXPO_PUBLIC_SERVER_API_URL": "https://your-server.workers.dev",
        "EXPO_PUBLIC_WEB_APP_URL": "https://your-app.com",
        "EXPO_PUBLIC_REVENUECAT_IOS_API_KEY": "appl_xxxxxxxxxxxxxxxx",
        "EXPO_PUBLIC_REVENUECAT_ANDROID_API_KEY": "goog_xxxxxxxxxxxxxxxx",
        "EXPO_PUBLIC_REVENUECAT_ENTITLEMENT_ID": "pro"
      }
    },
    "preview": {
      "env": {
        // ... 同上,可使用相同或独立的 RevenueCat 项目
      }
    },
    "production": {
      "env": {
        "EXPO_PUBLIC_SERVER_API_URL": "https://your-server.workers.dev",
        "EXPO_PUBLIC_WEB_APP_URL": "https://your-app.com",
        "EXPO_PUBLIC_REVENUECAT_IOS_API_KEY": "appl_xxxxxxxxxxxxxxxx",
        "EXPO_PUBLIC_REVENUECAT_ANDROID_API_KEY": "goog_xxxxxxxxxxxxxxxx",
        "EXPO_PUBLIC_REVENUECAT_ENTITLEMENT_ID": "pro"
      }
    }
  }
}

EXPO_PUBLIC_ 前缀的变量会被 Expo 注入到客户端代码中,不要将私密信息放在这里。RevenueCat SDK Key 是公开的客户端凭据,放在此处是安全的。

更新 app-config.ts 定价配置

packages/app-config/src/app-config.ts 中的 native.payments 定义了 App 内的定价方案,需要与 RevenueCat 后台和 App Store / Google Play 的产品 ID 保持一致:

packages/app-config/src/app-config.ts
native: {
  payments: {
    enabled: true,
    provider: "revenuecat",
    ios: {
      plans: [
        {
          id: "pro",
          prices: [
            {
              id: "monthly",
              provider: "revenuecat",
              providerPriceId: "easystarternative_10_1m", // App Store Connect 中的产品 ID
              currency: "usd",
              amountCents: 1000,
              priceType: "subscription",
              interval: "month",
              status: "active",
            },
            {
              id: "yearly",
              provider: "revenuecat",
              providerPriceId: "easystarternative_100_1y",
              currency: "usd",
              amountCents: 10000,
              priceType: "subscription",
              interval: "year",
              status: "active",
            },
          ],
        },
        {
          id: "lifetime",
          prices: [
            {
              id: "lifetime",
              provider: "revenuecat",
              providerPriceId: "easystarternative_299_lifetime",
              currency: "usd",
              amountCents: 29900,
              priceType: "lifetime",
              status: "active",
            },
          ],
        },
      ],
    },
    android: {
      plans: [
        {
          id: "pro",
          prices: [
            {
              id: "monthly",
              provider: "revenuecat",
              providerPriceId: "pro_monthly_android", // Google Play Console 中的产品 ID
              currency: "usd",
              amountCents: 800,
              priceType: "subscription",
              interval: "month",
              status: "active",
            },
          ],
        },
      ],
    },
  },
},
字段说明
providerPriceId对应 App Store Connect / Google Play Console 中的产品 ID,必须完全一致
amountCents用于 App 内展示价格,单位为分($10.00 = 1000)
priceTypesubscription(订阅)或 lifetime(一次性购买)
statusactive 为上线,inactive 为下架

Server 端变量

除了客户端配置,Server 也需要两个 RevenueCat 变量才能处理 Webhook 和服务端 API 调用:

apps/server/.env.production
REVENUECAT_SECRET_API_KEY=          # RevenueCat Secret API Key(用于服务端查询订阅状态)

REVENUECAT_SECRET_API_KEY 在 RevenueCat Dashboard → 项目 → API Keys → Secret keys 中获取。