乐于分享
好东西不私藏

从零实现 App 订阅?别自己写后端了——聊聊 RevenueCat

从零实现 App 订阅?别自己写后端了——聊聊 RevenueCat

做过 App 内购的开发者心里都清楚:苹果一套 API,谷歌一套 API,收据验证要自己搭后端,订阅状态变更还要接 Webhook……一个订阅功能,前后端加双平台,没两周搞不定。直到 RevenueCat 出现。

如果你做过带订阅功能的 App,大概率经历过这种场景:

iOS 端接入 StoreKit,配置 SKProduct,监听支付回调,处理收据验证。Android 端来一套 BillingClient,注册 PurchasesUpdatedListener,接上 Google Play Developer API。两边都跑通了,产品经理说:"加个 A/B 测试看看年付和月付哪个转化率高。"

这时候你知道,真正的麻烦才刚刚开始。


01 内购这件事,到底有多麻烦?

先看如果不借助第三方,自己搞一套完整的内购系统需要做哪些事:

模块
iOS (StoreKit)
Android (BillingClient)
商品查询
SKProductsRequestqueryProductDetailsAsync
发起购买
SKPaymentQueue.addlaunchBillingFlow
支付回调
SKPaymentTransactionObserverPurchasesUpdatedListener
收据验证
自己搭 Server 验 receipt
自己调 Google Play Developer API
订阅状态
接 App Store Server Notifications
接 RTDN (Cloud Pub/Sub)
跨平台同步
自己实现
自己实现
退款处理
App Store Server Notifications
Developer API 轮询
促销/优惠
StoreKit 手动管理
手动管理 offer token

两套完全不同的 API,两套回调机制,两套通知系统。如果你还做了 Web 端用 Stripe,那就是三套。

更麻烦的是维护成本:苹果每年 WWDC 可能改 StoreKit API,Google 也会更新 Billing Library。每次升级都要重新适配。核心工程师离职了,新人接手可能得啃几个月。


02 RevenueCat 是什么?

RevenueCat 做的就是一件事:把上面所有的脏活累活包了,给你一套统一的 SDK。

它的架构很简单:

你的 App → RevenueCat SDK → RevenueCat 后端 → Apple/Google API                               ↓                          统一的 CustomerInfo + Webhook

你不需要自己管理 StoreKit 或 BillingClient 的连接生命周期,不需要自己搭收据验证服务器,不需要自己维护订阅状态机。SDK 初始化一次,后面全是调用 Purchases.sharedInstance 的几个方法。

来看看这个成绩单:

  • 91,000+
     App 正在使用
  • 年处理订阅流水 $130亿+
  • 99.99%
     可用性
  • SOC2 认证、
    GDPR 合规
  • AWS 多可用区部署

定价方面也很良心:月收入 $2,500 以下完全免费,超过后才按使用量付费。


03 核心概念详解

RevenueCat 的核心抽象层有四个层级,从底层到顶层依次是:Product → Offering → Package → Entitlement。

3.1 四层架构总览

┌─────────────────────────────────────────────────────────┐│  Entitlement  (用户权限,业务层)                         ││  例:pro_access → 是否解锁高级功能                       │├─────────────────────────────────────────────────────────┤│  Offering  (定价页面,一组商品的集合)                    ││  例:"default" → 包含月付/年付/永久                      │├─────────────────────────────────────────────────────────┤│  Package  (跨平台商品映射,同一逻辑商品)                 ││  例:"monthly" → iOS的premium_monthly = Android的premium │├─────────────────────────────────────────────────────────┤│  Product / StoreProduct  (各商店实际创建的商品)          ││  例:iOS: com.app.premium_monthly / Android: premium_m  │└─────────────────────────────────────────────────────────┘

3.2 Products:你在商店里实际创建的商品

这是最底层,对应你在 App Store Connect 或 Google Play Console 里真实创建的订阅商品。

  • iOS 侧:
    每个 Auto-Renewable Subscription 有一个 product identifier(如 com.myapp.premium_monthly),关联特定的时长和价格
  • Android 侧
    :结构更复杂——一个 subscription 下面可以有多个 base plan(订阅基准方案),每个 base plan 又可以有多个 offer(优惠方案)

RevenueCat 把这两者的差异封装掉了。你在 SDK 里拿到的是一个统一的 StoreProduct 对象,不用关心底层是 iOS 的 SKProduct 还是 Android 的 ProductDetails。

3.3 Offerings & Packages:业务层面的"卖什么"

Offering 是一组商品的集合,本质上就是一个"定价页面"的抽象。

你可能同时有多个 Offering:给新用户的 "default"、给老用户的 "loyalty"、节日促销的 "holiday_sale"。切换到哪个 Offering 完全由 RevenueCat 后台控制,不需要发版。

Package 则是在不同平台上代表"同一件商品"。你在 iOS 上有一个 product ID premium_monthly,在 Android 上有一个 premium_monthly,它们应该在同一个 Package 里。

Offering: "default"├── Package: "$rc_monthly"  → iOS: premium_monthly, Android: premium_monthly├── Package: "$rc_annual"   → iOS: premium_yearly,  Android: premium_yearly└── Package: "lifetime"     → iOS: premium_lifetime, Android: premium_lifetime

RevenueCat 内置了几个标准 Package 标识符:$rc_weekly、$rc_monthly、$rc_annual、$rc_six_month、$rc_three_month 等。用标准标识符的话,SDK 可以直接通过 .monthly、.annual 这样的属性访问,不需要硬编码字符串。

3.4 Entitlements:用户权限的统一抽象

Entitlement 是整个系统最巧妙的设计。

它的思想是:用户购买的是"权限",而不是某个具体的商品。一个用户可能通过月付获得 pro_access,另一个用户可能通过年付获得 pro_access,还可能有用户通过永久买断获得 pro_access。在代码里,你判断的永远只是:

if customerInfo.entitlements["pro_access"]?.isActive ==true

你不需要关心用户是月付、年付还是永久。哪怕你把商品从 premium_monthly 换成 pro_monthly_v2,Entitlement 的逻辑完全不用改。

Entitlement 和 Product 的关联在 RevenueCat Dashboard 里配,一个 Entitlement 可以关联多个 Product。用户购买了任意一个关联的商品,Entitlement 就会变成 active。

3.5 举个例子:完整配置链路

1. 在 App Store Connect 创建商品   → Product ID: com.myapp.monthly, com.myapp.yearly2. 在 Google Play Console 创建商品   → Product ID: monthly_sub, yearly_sub (含 base plan)3. 在 RevenueCat Dashboard:   a. Products 页面 → 商品自动同步,或手动添加   b. Offerings → 新建 "default" Offering   c. 在 default 里加两个 Package:      - "$rc_monthly" → 绑定 iOS com.myapp.monthly + Android monthly_sub      - "$rc_annual"  → 绑定 iOS com.myapp.yearly  + Android yearly_sub   d. Entitlements → 新建 "pro_access"   e. 把 monthly Package 和 annual Package 关联到 pro_access4. 客户端代码:   let offerings = await Purchases.shared.offerings()   let monthly = offerings.current!.monthly  // 拿到 Package   let result = await Purchases.shared.purchase(package: monthly)   // result.customerInfo.entitlements["pro_access"]?.isActive == true ✅

这个链路虽然看起来步骤多,但大部分是一次性的配置工作。配完以后,以后改价格、调方案、加促销,全在 Dashboard 里点几下就行。


04 三端配置:RevenueCat + App Store + Google Play

开始写代码之前,有三端需要配置好。按顺序来。

4.1 RevenueCat Dashboard 配置

第一步,打开 app.revenuecat.com 注册账号。

创建 Project:

登录后创建一个 Project,给它起个名字。每个 Project 对应你的一个 App(或一套 App)。

添加平台 App:

在 Project Settings → Apps 里添加你的平台:

  • 添加 iOS App → 系统生成一个 appl_ 开头的 Public API Key
  • 添加 Android App → 系统生成一个 goog_ 开头的 Public API Key

这两个 key 分别用在 iOS 和 Android 的 SDK 初始化中。

你还需要区分两组 API Key:

  • Test Store API Key:
    开发测试用,不需要连接真实商店
  • Production API Key:
    生产环境用,需要连接 App Store / Google Play

建议用 Build Configuration 或环境变量来切换,不要写死在代码里。

创建 Products、Offerings、Entitlements:

在 RevenueCat Dashboard 左侧菜单:

  1. Products → + New → 输入商品 ID、时长、价格(也可以从商店自动同步)
  2. Offerings → + New → 创建 "default" → + Add Package → 选择 Package 标识符 → 绑定 Product
  3. Entitlements → + New → 创建 "pro_access" → 关联对应的 Products 或 Packages

连接商店(生产环境必需):

如果不连接商店,RevenueCat 只能用自己的 Test Store(模拟购买,不出真钱)。要上线必须连:

  • 连接 App Store:
    需要上传 .p8 文件 + Key ID + Issuer ID(见下文 4.2)
  • 连接 Google Play:
    需要上传 Service Account JSON Key(见下文 4.3)

在 App Settings → Store Integration 里配置。

4.2 App Store Connect 配置

创建订阅商品:

  1. 在 App Store Connect → 你的 App → 功能 → 内购项目
  2. 点 + → "自动续期订阅"
  3. 填写:
    • 参考名称:
      对内显示的标识(如 "月度会员")
    • 产品 ID:
      格式 com.yourcompany.yourapp.monthly
    • 订阅时长:
      周/月/季/半年/年
    • 价格:
      选择价格等级(如 Tier 1 = $0.99)
  4. 创建完成后确保商品状态为"准备提交"(Ready to Submit)(无需真正提交审核),Sandbox 环境下即可测试

生成 IAP Key (.p8 文件):

RevenueCat 需要用 IAP Key 来调用 App Store Server API 验证收据。

  1. App Store Connect → 用户和访问 → 密钥 → App 内购买项目
  2. 点 + 生成密钥 → 下载 .p8 文件(仅下载一次,妥善保存
  3. 记下 Key ID 和 Issuer ID

回到 RevenueCat Dashboard → iOS App Settings → In-App Purchase Key Configuration:

  • 上传 .p8 文件
  • 填入 Key ID 和 Issuer ID

获取 Shared Secret(仅当你需要自己接 Apple 通知时需要):

App Store Connect → 你的 App → 功能 → App 内购买项目 → 订阅状态的 "共享密钥" → 生成。注意 RevenueCat 通过 IAP Key 已经自动完成了收据验证,通常不需要配置 Shared Secret。这个密钥主要在你需要自己接收 App Store Server Notifications 时使用。

4.3 Google Play Console 配置

创建订阅商品:

  1. Google Play Console → 你的 App → 创收 → 订阅
  2. 创建订阅 → 填写产品 ID(如 monthly_sub)、标题、说明
  3. 创建 Base Plan(订阅基准方案):
    • 计费周期:月/季/半年/年
    • 价格
    • 续订类型:自动续订 / 预付费

Android 这边需要注意:一个 Subscription 下可以有多个 Base Plan(比如月度方案和年度方案放在同一个商品里),也可以拆成多个 Subscription。RevenueCat 两种都支持。

创建 Service Account:

RevenueCat 需要用 Service Account 调用 Google Play Developer API 来验证购买。

  1. Google Cloud Console → 选择你的项目(或新建)
  2. 启用 Google Play Developer API
  3. IAM 与管理 → 服务账号 → 创建服务账号
  4. 创建后 → 密钥 → 添加密钥 → JSON → 下载

回到 Google Play Console → 用户和权限 → 邀请新用户:

  • 输入服务账号的邮箱
  • 权限选择 "查看财务数据"(Financial data viewer,只读权限就可以)

回到 RevenueCat Dashboard → Android App Settings → Store Integration:

  • 上传 JSON Key 文件
  • 填入 Package Name

配置完成后,RevenueCat 会自动通过 Google Play Developer API 验证每一笔购买。


05 Paywall:远程配置的订阅购买页面

RevenueCat 的 Paywall 是你整个付费流程里最直接面对用户的那层 UI。本质上它就是你把 Offering 展示给用户的视觉页面——但是厉害的地方在于,这个页面完全在 Dashboard 里远程配置,不需要写代码,更不需要发版。

5.1 什么是 Paywall

  • 每个 Offering 可以绑定一个 Paywall(也可以没有)
  • Paywall 是原生的——iOS 用 SwiftUI 渲染,Android 用 Jetpack Compose 渲染,不是 WebView
  • 你可以在 Dashboard 的 WYSIWYG 编辑器里拖拽设计、改文案、调样式
  • 改完 实时生效,用户下次打开 App 就看到新页面

传统的做法:改一次价格 → 改代码 → 提审 → 等审核 → 用户更新 App → 看到新价格。RevenueCat Paywall 的流程:Dashboard 改个数字 → 保存 → 用户下次打开就看到新价格。没有发版,没有审核。

5.2 Dashboard 配置

在 RevenueCat 左侧菜单进入 Paywalls,可以看到这样的流程:

  1. 选模板
     — RevenueCat 提供多套预设模板(基础款、功能对比款、试用品推广款等),可以直接用或在此基础上改
  2. 拖拽编辑器
     — 图层式编辑,支持修改:商品展示顺序、价格文案、按钮文字、背景色/渐变、图片、字体大小等
  3. 按条件展示
     — 同一个 Offering 可以配多个 Paywall,按用户属性(地区、使用天数、是否订阅过等)展示不同版本
  4. 预览
     — 支持在 Dashboard 里直接预览手机效果,也可以生成链接在真机上预览

5.3 A/B 测试(Experiments)

RevenueCat 的 Experiments 功能是 Paywall 最实用的能力之一:

  • 最多同时跑 4 个变体
  • 每个变体可以绑定不同的 Offering + Paywall(不同的商品组合、不同的设计)
  • 追踪完整转化漏斗:展示 → 点击 → 购买
  • 长期追踪留存数据,看哪个变体的用户 LTV 更高
  • Dashboard 自动计算统计显著性,告诉你哪个变体胜出

5.4 代码集成

iOS (SwiftUI):

import RevenueCatUI// 方式一:自动判断是否需要展示 PaywallstructContentView:View {var body: someView {ContentView()            .presentPaywallIfNeeded(                requiredEntitlementIdentifier: "pro_access"            ) { customerInfo in// 用户购买成功或已拥有权限,关闭 Paywall            }    }}// 方式二:手动展示 PaywallViewstructPaywallContainer:View {var body: someView {PaywallView()    }}

Android (Jetpack Compose):

// 方式一:Paywall DialogPaywallDialog(    onDismiss = { /* 关闭回调 */ },    onPurchaseCompleted = { customerInfo ->if (customerInfo.entitlements["pro_access"]?.isActive == true) {// 购买成功        }    })// 方式二:嵌入到你的 Composable 中PaywallView(    offering = offerings.current,    onPurchaseCompleted = { /* ... */ })

5.5 注意事项

要求
iOS
Android
最低系统版本
iOS 15.0+
Android 7.0 (API 24+)
最低 SDK 版本
purchases-ios 5.27.1+
purchases-android 8.19.2+
额外依赖
RevenueCatUI
(通过 SPM 加)
com.revenuecat.purchases:purchases-ui
支持框架
SwiftUI
Jetpack Compose

UIKit 怎么办? RevenueCat 的 Paywall SDK 主要面向 SwiftUI 和 Jetpack Compose。如果你还在用 UIKit 或 View System,可以自己构建 UI,用 RevenueCat SDK 获取 Offering 和 Package 数据,然后走 purchase(package:) 手动完成购买流程。功能上完全不受影响,只是 Paywall 的远程配置能力用不了。


06 iOS 接入实战(Swift)

安装 SDK

通过 Swift Package Manager 添加:

https://github.com/RevenueCat/purchases-ios-spm.git

选择 RevenueCat 和 RevenueCatUI(用于 Paywall)两个 Product。别忘了在 Target → Capabilities 中开启 In-App Purchase 能力。

初始化

在 App 启动时配置,只需要一次:

import RevenueCat// 通常在 AppDelegate 或 App.init() 中Purchases.logLevel = .debug  // 开发时开启Purchases.configure(    withAPIKey: "appl_你的_public_api_key",    appUserID: nil// nil 会自动生成匿名 ID)

appUserID 传 nil 时,SDK 自动生成一个匿名 ID。如果你有自己的用户系统,之后通过 logIn() 关联。

获取商品并发起购买

// 获取当前配置的 Offeringlet offerings =tryawaitPurchases.shared.offerings()guardlet current = offerings.current else { return }let monthly = current.monthly  // 直接拿到 Package// 发起购买do {let result =tryawaitPurchases.shared.purchase(package: monthly)if result.customerInfo.entitlements["pro_access"]?.isActive ==true {// 购买成功,解锁功能    }catchlet error {print("购买失败: \(error)")}

SDK 内部帮你完成了:StoreKit 支付 → 收据上传 → 服务端验证 → CustomerInfo 更新 → finishTransaction。

检查订阅状态

let info =tryawaitPurchases.shared.customerInfo()let isPro = info.entitlements["pro_access"]?.isActive ==truelet willRenew = info.entitlements["pro_access"]?.willRenew ==truelet expDate = info.entitlements["pro_access"]?.expirationDate

恢复购买

let info =tryawaitPurchases.shared.restorePurchases()

苹果要求 App 必须有恢复购买按钮,这一行搞定。


07 Android 接入实战(Kotlin)

添加依赖

// app/build.gradle.ktsdependencies {    implementation("com.revenuecat.purchases:purchases:9.x.x")    implementation("com.revenuecat.purchases:purchases-ui:9.x.x")}

SDK 自带 BillingClient,不需要再加。

初始化

classMyApplication:Application() {overridefunonCreate() {super.onCreate()        Purchases.logLevel = LogLevel.DEBUG        Purchases.configure(            PurchasesConfiguration.Builder(this"goog_你的_api_key")                .appUserID(null)                .build()        )    }}

原本需要手动创建 BillingClient、处理连接重连、注册 PurchasesUpdatedListener、查询已有商品——现在这些都在 configure() 内部自动完成了。

获取商品并发起购买

classPaywallViewModel {// 获取当前 OfferingsuspendfunfetchProducts(): Package? {val offerings = Purchases.sharedInstance.awaitOfferings()return offerings.current?.monthly    }// 发起购买suspendfunpurchase(activity: Activity, pkg: Package) {try {val result = Purchases.sharedInstance.awaitPurchase(                PurchaseParams.Builder(activity, pkg).build()            )if (result.customerInfo.entitlements["pro_access"]?.isActive == true) {// 购买成功 → 解锁功能            }        } catch (e: PurchasesTransactionException) {when {                e.userCancelled -> { /* 用户取消 */ }else -> { /* 错误处理 */ }            }        }    }}

awaitPurchase() 内部:构建 BillingFlowParams → launchBillingFlow → 等待回调 → 上传 token → RevenueCat 服务端验证 → 自动 acknowledgePurchase → 返回结果。可重试的错误自动重试。

检查订阅状态

val info = Purchases.sharedInstance.awaitCustomerInfo()val isPro = info.entitlements["pro_access"]?.isActive

监听状态变更

Purchases.sharedInstance.updatedCustomerInfoListener =    UpdatedCustomerInfoListener { customerInfo ->        updateUI(customerInfo)    }

08 iOS vs Android 接入对比

环节
iOS (Swift)
Android (Kotlin)
SDK 安装
SPM / CocoaPods
Gradle
初始化
Purchases.configure(withAPIKey:)PurchasesConfiguration.Builder
获取商品
offerings()awaitOfferings()
发起购买
purchase(package:)awaitPurchase(PurchaseParams)
状态检查
customerInfo()awaitCustomerInfo()
恢复购买
restorePurchases()awaitRestore()
状态监听
Delegate + async
updatedCustomerInfoListener

无论 iOS 还是 Android,调用方式几乎一样——这就是 RevenueCat 最大的价值。


09 Dashboard 背后的能力

RevenueCat 真正值钱的不只是 SDK 封装,而是服务端+管理后台那一整套能力:

收据验证,不用自己搭服务器 — 每次购买完成后 RevenueCat 后端自动调 Apple/Google 的 API 验证收据真实性。你不需要维护收据验证服务,不用担心验证逻辑过期。

订阅生命周期自动化 — 续订、过期、宽限期、账单重试、退款——这些状态流转全部由 RevenueCat 管理。你的 App 只需问一句 entitlement.isActive 就知道是否该放行。

Webhook 事件通知 — 任何订阅状态变更(续订、取消、退款、过期等)通过 Webhook 推送到你的服务器,格式统一、跨平台归一。


10 什么时候该用,什么时候不该用

适合用 RevenueCat
不太适合
独立开发者/小团队
已有成熟自建 IAP 系统
跨平台 App (iOS + Android)
只有单一平台且逻辑极简单
需要 A/B 测试、Paywall 管理
受严格合规监管(金融等)
不想维护订阅后端
对数据隐私要求极度严格

对大多数 App 来说,把内购基础设施交给 RevenueCat,省下的不只是开发时间,更是持续的维护成本。下次产品经理说"加个 A/B 测试"的时候,你就不用再骂人了。


一句话总结:RevenueCat 把 Apple 和 Google 两套内购 API 封装成一套 SDK,收据验证、订阅状态管理、Paywall 配置全包了。省掉一个后端加双平台适配的工作量,对于独立开发者和小团队来说,性价比确实很高。