# RevenueCat Documentation — Full Corpus > RevenueCat is the most popular way to build, analyze, and grow in-app purchases and subscriptions on iOS, Android, and the web — no server code required. Each link below points to the Markdown version of a documentation page; append `.md` to any docs URL to fetch its clean Markdown for AI agents and LLMs. This file concatenates the Markdown content of every listed RevenueCat documentation page. For a lighter-weight index of links, see [llms.txt](https://www.revenuecat.com/docs/llms.txt). --- # Welcome to RevenueCat Source: https://www.revenuecat.com/docs/welcome/overview Markdown: https://www.revenuecat.com/docs/welcome/overview.md RevenueCat is the go-to platform for developers who want to focus on building amazing products—not dealing with the complexities of billing and app stores. With dynamic [paywalls](https://www.revenuecat.com/docs/tools/paywalls), actionable [analytics](https://www.revenuecat.com/docs/dashboard-and-metrics/overview), and plug-and-play [experimentation tools](https://www.revenuecat.com/docs/tools/experiments-v1), RevenueCat helps you make smarter decisions and drive growth, whether you're just starting out or scaling to millions of customers. Get started with RevenueCat by [creating an account](https://app.revenuecat.com/signup). All you need is an email address. :::info We host live office hours every other Friday, where we demo the platform and answer your questions live. [Register for the next office hours →](https://app.livestorm.co/revenuecat/live-revenuecat-demo?type=detailed) ::: ## Start implementing Get started by creating a new project and connecting to a store. [Create a project](https://www.revenuecat.com/docs/projects/overview) ### Migrating existing subscriptions? Whether you have just one or one million subscribers, you can easily replace your existing setup with RevenueCat. Supercharge your business and start taking advantage of powerful features like Experiments, Paywalls, and Targeting. Read our [migration guide](https://www.revenuecat.com/docs/migrating-to-revenuecat/migration-paths) for more information. ## Learn more about RevenueCat Check out our features, guides, and best practices for implementing different monetization models. ### Guides [Playbooks](https://www.revenuecat.com/docs/playbooks/overview) — Learn best practices and proven strategies for subscription success [App Launch Checklist](https://www.revenuecat.com/docs/test-and-launch/launch-checklist) — Ensure your app is ready to launch with our pre-launch checklist ### Features [Entitlements & Subscription Status](https://www.revenuecat.com/docs/getting-started/entitlements) — Ensure customers have correct access even if your entitlement structure is complex. [Paywalls](https://www.revenuecat.com/docs/tools/paywalls) — Remotely configure your product offering with powerful paywalls. [Events & Integrations](https://www.revenuecat.com/docs/integrations/integrations) — Clean, normalized subscription data at your fingertips and in every system. [Charts, Metrics, & Data](https://www.revenuecat.com/docs/dashboard-and-metrics/overview) — Get insights into your business with charts, metrics, and data exports. [Experiments](https://www.revenuecat.com/docs/tools/experiments-v1) — Run A/B tests to find the most effective pricing model. ### AI tools Our documentation is optimized for large language models. For AI assistants, see our [llms.txt](https://www.revenuecat.com/docs/llms.txt) index for structured access to key resources. [RevenueCat MCP Server](https://www.revenuecat.com/docs/tools/mcp) — Use AI to answer questions about your RevenueCat data. ## Not sure where to start? Talk to an expert to learn how RevenueCat can help grow your business. [Talk to Sales](https://www.revenuecat.com/talk-to-sales/) --- # Billing and account settings Source: https://www.revenuecat.com/docs/welcome/set-up-revenuecat/account-management Markdown: https://www.revenuecat.com/docs/welcome/set-up-revenuecat/account-management.md ## Account Security & 2FA Read more about account security and two-factor authentication in our [Account Security](https://www.revenuecat.com/docs/welcome/set-up-revenuecat/security) guide. ## Update your email or name You can change your account email and name from your [account settings](https://app.revenuecat.com/settings/account) in the dashboard. ## How does billing work? RevenueCat bills based on **Monthly Tracked Revenue**, or MTR, for each plan. MTR is different than Monthly Recurring Revenue, or MRR, and includes the revenue from all purchases and renewals including non-subscription products. You can see your account's current MTR [here](https://app.revenuecat.com/settings/billing). Billing periods will not follow the calendar month, but rather will be from the same date of a month until the same date of the next month (for example: July 15th until August 15th). Read more on our [Pricing](https://www.revenuecat.com/pricing) page. ### What happens when you reach $2.5k in MTR? As your app grows, RevenueCat will remain free until you reach $2.5k in MTR, and beyond that limit for everyone on the Pro Plan, we will bill for 1% of revenue. If you do fall below that amount in subsequent months, RevenueCat will return to being free for you. An example of how you can expect to be charged for the Pro plan can be found below: - Your MTR Tracked \< $2,500 = Free - Your MTR Tracked > $2,500 = 1% of your MTR tracked. For instance, if you earned $2,600 in the previous billing cycle, you would incur a charge of $26. If you join RevenueCat and quickly exceed $2.5k in MTR in your first month, you will receive a grace period of 30 days starting from when you reach the limit in order to add a credit card, fix your billing details, or make your payment through another method. If no payment is completed by the end of this grace period, your access will be restricted until a payment has been made successfully. If you pass the initial month after you joined and pass the limit later on, your access to those features will be restricted immediately until a payment has been made successfully. For example, this situation would occur if an app exceeded $2.5k in MTR after 2 months of using RevenueCat. The abilities that would be restricted are as follows: - View and Filter Charts - Create new Customer Lists - Export Customer Lists - View Customer History (Viewing the Customer Details will remain) - View individual events - Add Customer Attributes - Create new Experiments - Edit running Experiments (Viewing Results and stopping will remain) - Create new Paywalls - Edit existing Paywalls (Using existing Paywalls will remain) ### Where to find invoices? An invoice will be emailed to the owner of a project at the end of the current billing period. If you want to have the invoices emailed to additional email addresses, you can reach out to [Developer Support](https://app.revenuecat.com/settings/support) in order to have them added to your profile for future invoices. You can also view a history of invoices on the [Invoices page](https://app.revenuecat.com/settings/billing/invoices) under the billing category of the project owner's account settings. The history will only list invoices with non-zero billed amounts, so you may see gaps between billing periods if you do not meet the $2.5k limit in every billing period. ### Invoice details You can update how your company name, address, and Tax ID/VAT number appear on your invoices in [billing settings](https://app.revenuecat.com/settings/billing) under **Invoice details**. ![update your invoice details in your billing settings](https://www.revenuecat.com/docs_images/account/invoice-details.png) If you have not added a payment method, you will not receive invoices nor will you be able to change your invoice details. If you need more than one Tax ID, or you would like to forward your invoices to a different email address, please reach out to [RevenueCat Support](https://app.revenuecat.com/settings/support) for assistance. ## Display Currency You can select a currency to be used across the dashboard. See [Display Currency](https://www.revenuecat.com/docs/dashboard-and-metrics/display-currency) for more information. ## Delete your account To delete your RevenueCat account, you'll first need to delete **all of your [Projects](https://www.revenuecat.com/docs/projects/overview)**. Please note, deleting any active Projects will prevent users from accessing their purchases via the RevenueCat SDK but **will not** cancel any of your customer's active subscriptions. RevenueCat will not delete your projects for you. Once your projects have been deleted, navigate to your [account settings](https://app.revenuecat.com/settings/account) and click `Delete this Account`: ![delete account button](https://www.revenuecat.com/docs_images/account/delete-account.png) You will be asked to enter your password to confirm. If your account is managed by an SSO organization, reach out to RevenueCat Support via the dashboard [Contact Us](https://app.revenuecat.com/settings/support) form in your account settings and request your account to be deleted. Shutting down your RevenueCat account RevenueCat's goal is to be useful in your app development journey. If you need to shut down your RevenueCat account and/or remove RevenueCat from your app, follow these steps to ensure a smooth transition. 1. Export your data - If needed, contact [RevenueCat Support](https://app.revenuecat.com/settings/support) to export all user receipts - Download additional data exports from the dashboard: - [Scheduled Data Exports](https://www.revenuecat.com/docs/integrations/scheduled-data-exports) - Charts exports - Customer list exports 2. If removing RevenueCat from your app, update your application - Remove the RevenueCat SDK from your app - If needed, configure your own purchase validation and subscription management 3. Delete your RevenueCat project - **Warning**: This action is immediate and irreversible. Users still on older app versions will lose access to RevenueCat services 4. Delete your RevenueCat account Please note that active subscriptions will not be canceled when shutting down your RevenueCat account. If you have any questions, don't hesitate to reach out to [RevenueCat Support](https://app.revenuecat.com/settings/support). --- # Data & Compliance Source: https://www.revenuecat.com/docs/welcome/set-up-revenuecat/data-and-compliance Markdown: https://www.revenuecat.com/docs/welcome/set-up-revenuecat/data-and-compliance.md Learn how RevenueCat stores and handles data in our [privacy policy](https://www.revenuecat.com/privacy) and [GDPR statement](https://www.revenuecat.com/gdpr). For further questions related to compliance or our data practices, please reach out to us at compliance@revenuecat.com. ## Data Processing Addendum (DPA) Our Data Processing Addendum (DPA) can be found [here](https://www.revenuecat.com/dpa). You do not need a signed/counter-signed DPA from RevenueCat—this is included in our standard terms and conditions that you agree to when creating a [RevenueCat account](https://www.revenuecat.com/docs/welcome/set-up-revenuecat/account-management). --- # HackerOne Vulnerability Disclosure Program Source: https://www.revenuecat.com/docs/welcome/set-up-revenuecat/hackerone Markdown: https://www.revenuecat.com/docs/welcome/set-up-revenuecat/hackerone.md At RevenueCat, we enjoy working with the security community to ensure our platform is secure and your data is kept private. In pursuit of these goals, we accept vulnerability reports from security researchers and hackers through the HackerOne program. We offer "bug bounties" ranging from $250 for small bugs to $5000 for the most critical vulnerabilities. ## Why This Program Exists Whereas we maintain SOC2 compliance, take great care to fortify our infrastructure and services, and always prioritize the privacy of our customers and their users, we welcome the expertise of the security community writ large to ensure our security is flawless. By collaborating through HackerOne, we can work quickly to identify and patch potential security vulnerabilities while offering security researchers financial compensation for their hard work. ## How to Submit a Report If you believe you’ve found a security vulnerability in any of our services, send us an email at hackerone@revenuecat.com *with your full report*. You will receive an email inviting you to submit the report: ![Submit a vulnerability report](https://www.revenuecat.com/docs_images/account/submit-vulnerability.png) You will then be directed to HackerOne to confirm your report and submit it to our program. ## Program Guidelines #### Act in good faith. Our team carefully reviews each submission and verifies the severity and practical impact of the vulnerability. Repeated offenses of misleading reports will be marked as such, affecting your hacker reputation. #### Provide detailed reports with reproducible steps. If the report is not detailed enough to reproduce the issue, the issue will not be eligible for a reward. #### Submit one vulnerability per report. Unless you need to chain vulnerabilities to provide impact. #### Social engineering (e.g. phishing, vishing, smishing) is prohibited. We will mark such attempts in HackerOne, affecting your hacker reputation. #### Respect the privacy of the program As this is a private program, please do not discuss this program or any vulnerabilities (even resolved ones) outside of the program without express consent from the organization. *** We will acknowledge receipt of your report within a 5–10 business days and work to deliver a bounty within 14 business days. Thank you for helping to keep RevenueCat secure! --- # Account security Source: https://www.revenuecat.com/docs/welcome/set-up-revenuecat/security Markdown: https://www.revenuecat.com/docs/welcome/set-up-revenuecat/security.md It's a dangerous world out there! But you can make things much safer by enabling two-factor authentication in your RevenueCat account settings. Once you do, you'll need a code generated on your mobile device any time you log in to your RevenueCat account. :::info\[Password Security] RevenueCat protects your account by securely checking if your password has been exposed in public data breaches. We won't allow using an unsafe password during sign-up. If we detect that your existing password has appeared in a data breach, you may also be required to reset your password before logging in. If this happens, use the "Forgot password" link on the login page to set a new, secure password. ::: ### Enabling Two-Factor Authentication #### 1. Set up Navigate to your [**Account > Security**](https://app.revenuecat.com/settings/security) settings in the RevenueCat dashboard and click **Set up** under Two-factor Authentication to begin the setup process. ![](https://www.revenuecat.com/docs_images/account/security.png) #### 2. Scan barcode You'll be prompted to re-enter your password. Once re-authenticated, you'll be presented with a QR code that you should scan with an authenticator app such as [Authy](https://authy.com/features/setup/) or [Google Authenticator](https://apps.apple.com/app/id388497605). #### 3. Enter two-factor code Enter the two-factor code from the authenticator app then click **Enable**. ![](https://www.revenuecat.com/docs_images/account/setup-2fa.png) #### 4. Save recovery codes Save your recovery codes. You'll only be shown these codes once, and are required if you ever lose access to your authenticator app. Some authenticator apps, like Authy, also provide their own backups in case you lose your phone. :::info\[Save recovery codes in a safe place] If you ever lose access to your two-factor code from your authenticator app (e.g. you got a new phone) the recovery codes are required to access RevenueCat. For security reasons, RevenueCat Support may not be able to restore access to accounts with two-factor authentication enabled if you lose your two-factor authentication credentials or lose access to your account recovery codes. ::: ### Enforcing Two-Factor For Your Project If you have invited collaborators to your app, you can check see if they've enabled two-factor authentication for their account on the [**Project > Collaborators**](https://www.revenuecat.com/docs/projects/collaborators) page. Project Owners and Administrators also have the ability to enforce two-factor authentication for any new collaborators. With this setting enabled, invited collaborators will not be able to join your project until they've set up two-factor authentication for their account. ![](https://www.revenuecat.com/docs_images/projects/invite-collaborators.png) :::warning\[Everyone must already have two-factor before enforcing] Before you can enforce two-factor authentication for your project, all existing collaborators must already have two-factor authentication enabled. You can remove current collaborators and re-invite them if you need to enforce two-factor immediately. ::: ### Disabling Two-Factor Authentication To disable two-factor authentication vavigate to your [**Account > Security**](https://app.revenuecat.com/settings/security) settings in the RevenueCat dashboard and click **Disable** under Two-factor Authentication. ![](https://www.revenuecat.com/docs_images/account/disable-2fa.png) :::warning\[Leave projects that require two-factor before disabling] If you are a collaborator on a Project that requires two-factor authentication, you must leave that project before disabling. ::: ### Setting Up Single Sign-On Please refer to our [SSO guide](https://www.revenuecat.com/docs/projects/sso) for more information on activating SSO on your account. --- # Install the AdMob Adapter SDK Source: https://www.revenuecat.com/docs/getting-started/adapter-sdks/admob Markdown: https://www.revenuecat.com/docs/getting-started/adapter-sdks/admob.md :::warning\[Beta Feature] This feature is currently in beta. ::: :::warning\[Enable Impression-Level Ad Revenue in AdMob] You must enable **"Impression-level ad revenue"** in your AdMob account before RevenueCat can receive ad revenue events. Follow [Google's guide to turn on impression-level ad revenue](https://support.google.com/admob/answer/11322405). ::: The RevenueCat AdMob adapter wraps standard AdMob ad loading calls so RevenueCat can track ad events automatically. Install it once for your platform, then follow the [AdMob SDK Integration guide](https://www.revenuecat.com/docs/ad-monetization/admob) to start tracking ads with `loadAndTrack`. ## Requirements #### iOS - **Minimum iOS version**: iOS 15.0+ - **RevenueCat SDK**: `purchases-ios` 5.0.0+ - **AdMob SDK**: Google Mobile Ads SDK 12.0.0+ - **Swift only**: This adapter does not expose Objective-C entrypoints. Use RevenueCat's base `AdTracker` APIs directly for Objective-C integrations. #### Android - **Minimum SDK**: `purchases-android` 8.0.0+ - **AdMob SDK**: Google Mobile Ads SDK 22.0.0+ ## Installation #### iOS Add the AdMob adapter package via Swift Package Manager: ```swift .package(url: "https://github.com/RevenueCat/purchases-ios-admob", from: "5.0.0") ``` Then add the `RevenueCatAdMob` product to your target. :::info SPM Dependency Note If your project depends on RevenueCat via SPM, use the [`purchases-ios-spm`](https://github.com/RevenueCat/purchases-ios-spm) package (not `purchases-ios`). The adapter declares this dependency correctly, but if you add RevenueCat separately, make sure both resolve from the same `purchases-ios-spm` repository to avoid duplicate package errors. ::: #### Android Add the AdMob adapter module to your app's `build.gradle`: ```gradle dependencies { implementation 'com.revenuecat.purchases:purchases-admob:8.0.0+' } ``` This module depends on the RevenueCat Purchases SDK and Google Mobile Ads SDK. ## Next Steps Once the adapter is installed, follow the integration guide: - [AdMob SDK Integration](https://www.revenuecat.com/docs/ad-monetization/admob) --- # Configuring the SDK Source: https://www.revenuecat.com/docs/getting-started/configuring-sdk Markdown: https://www.revenuecat.com/docs/getting-started/configuring-sdk.md If this is your first time integrating RevenueCat into your app, we recommend following our [Quickstart](https://www.revenuecat.com/docs/getting-started/quickstart) guide. :::success\[Test Store works out of the box] Once configured, the SDK automatically works with your Test Store products—no additional setup required. You can start testing purchases immediately without connecting to the App Store or Google Play. **SDK Version Requirements:** Test Store requires minimum SDK versions (iOS 5.43.0, Android 9.9.0, Flutter 9.8.0, React Native 9.5.4, Capacitor 11.2.6, Cordova 7.2.0, Unity 8.3.0, KMP 2.2.2, Web 1.15.0). [See all versions](https://www.revenuecat.com/docs/test-and-launch/sandbox#testing-with-revenuecat-test-store). ::: :::info\[Using an older SDK (v3.x)] View our migration guide to v4.x [here](https://www.revenuecat.com/docs/sdk-guides/ios-native-3x-to-4x-migration). ::: ## Initialization Once you've [installed](https://www.revenuecat.com/docs/getting-started/installation) the SDK for your app, it's time to initialize and configure it. You should only configure *Purchases* once, usually early in your application lifecycle. After configuration, the same instance is shared throughout your app by accessing the `.shared` instance in the SDK. Make sure you configure *Purchases* with your public SDK key only. You can read more about the different API keys available in our [Authentication guide](https://www.revenuecat.com/docs/projects/authentication). **Note:** If you're using a hybrid SDK, such as React Native or Flutter, you'll need to initialize the SDK with a separate API key for each platform (i.e., iOS and Android). The keys can be found in the RevenueCat dashboard under **Project Settings > API keys > App specific keys**. **SwiftUI** ```swift import RevenueCat @main struct SampleApp: App { init() { Purchases.logLevel = .debug Purchases.configure(withAPIKey: , appUserID: ) } var body: some Scene { WindowGroup { ContentView() } } } ``` **Swift** ```swift import RevenueCat func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { Purchases.logLevel = .debug Purchases.configure(withAPIKey: , appUserID: ) } ``` **Objective-C** ```objectivec - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. RCPurchases.logLevel = RCLogLevelDebug; [RCPurchases configureWithAPIKey: appUserID:]; return YES; } ``` **Kotlin** ```kotlin // If you're targeting only Google Play Store class MainApplication: Application() { override fun onCreate() { super.onCreate() Purchases.logLevel = LogLevel.DEBUG Purchases.configure(PurchasesConfiguration.Builder(this, ).build()) } } // If you're building for the Amazon Appstore, you can use flavors to determine which keys to use // In your build.gradle: flavorDimensions "store" productFlavors { amazon { buildConfigField "String", "STORE", "\"amazon\"" } google { buildConfigField "String", "STORE", "\"google\"" } } ///... class MainApplication: Application() { override fun onCreate() { super.onCreate() Purchases.logLevel = LogLevel.DEBUG if (BuildConfig.STORE.equals("amazon")) { Purchases.configure(AmazonConfiguration.Builder(this, ).build()) } else if (BuildConfig.STORE.equals("google")) { Purchases.configure(PurchasesConfiguration.Builder(this, ).build()) } } } ``` **Kotlin MP** ```kotlin import com.revenuecat.purchases.kmp.LogLevel import com.revenuecat.purchases.kmp.Purchases import com.revenuecat.purchases.kmp.configure // If you have common initialization logic, call configure() there. If not, // call it early in the app's lifecycle on the respective platforms. // Note: make sure you use the correct api key for each platform. You could // use Kotlin Multiplatform's expect/actual mechanism for this. Purchases.logLevel = LogLevel.DEBUG Purchases.configure(apiKey = "") { appUserId = "" // Other configuration options. } ``` **Java** ```java // If you're targeting only Google Play Store public class MainApplication extends Application { @Override public void onCreate() { super.onCreate(); Purchases.setDebugLogsEnabled(true); Purchases.configure(new PurchasesConfiguration.Builder(this, ).build()); } } // If you're building for the Amazon Appstore, // click the Kotlin tab to see how to set up flavors in your build.gradle: ///... public class MainApplication extends Application { @Override public void onCreate() { super.onCreate(); Purchases.setDebugLogsEnabled(true); PurchasesConfiguration.Builder builder = null; if (BuildConfig.STORE.equals("amazon")) { builder = new AmazonConfiguration.Builder(this, "public_amazon_sdk_key"); } else if (BuildConfig.STORE.equals("google")) { builder = new PurchasesConfiguration.Builder(this, "public_google_sdk_key"); } Purchases.configure(builder.build()); } } ``` **Flutter** ```dart import 'dart:io' show Platform; //... Future initPlatformState() async { await Purchases.setLogLevel(LogLevel.debug); PurchasesConfiguration configuration; if (kIsWeb) { // Only needed if you're targeting web configuration = PurchasesConfiguration() } else if (Platform.isAndroid) { configuration = PurchasesConfiguration(); if (buildingForAmazon) { // use your preferred way to determine if this build is for Amazon store // checkout our MagicWeather sample for a suggestion configuration = AmazonConfiguration(); } } else if (Platform.isIOS) { configuration = PurchasesConfiguration(); } await Purchases.configure(configuration); } ``` **React Native** ```jsx import { Platform, useEffect } from 'react-native'; import Purchases from 'react-native-purchases'; import { GALAXY_BILLING_MODE } from 'react-native-purchases-store-galaxy'; //... export default function App() { useEffect(() => { Purchases.setLogLevel(Purchases.LOG_LEVEL.DEBUG); if (Platform.OS === 'web') { // Only needed if you're targeting web Purchases.configure({ apiKey: }); } else if (Platform.OS === 'ios') { Purchases.configure({ apiKey: }); } else if (Platform.OS === 'android') { Purchases.configure({ apiKey: }); // OR: if building for Amazon, be sure to follow the installation instructions then: Purchases.configure({ apiKey: , useAmazon: true }); // OR: if building for Galaxy Store, install react-native-purchases-store-galaxy, then: Purchases.configure({ apiKey: , store: 'GALAXY', galaxyBillingMode: GALAXY_BILLING_MODE.TEST, }); } }, []); } ``` **Cordova** ```jsx document.addEventListener("deviceready", onDeviceReady, false); function onDeviceReady() { Purchases.setDebugLogsEnabled(true); if (window.cordova.platformId === 'ios') { Purchases.configureWith({ apiKey: }); } else if (window.cordova.platformId === 'android') { Purchases.configureWith({ apiKey: }); } // OR: if building for Amazon, be sure to follow the installation instructions then: Purchases.configureWith({ apiKey: , useAmazon: true }); } ``` **Capacitor** ```jsx const onDeviceReady = async () => { await Purchases.setLogLevel({level: LOG_LEVEL.DEBUG}); if (Capacitor.getPlatform() === 'ios') { await Purchases.configure({ apiKey: }); } else if (Capacitor.getPlatform() === 'android') { await Purchases.configure({ apiKey: }); } // OR: if building for Amazon, be sure to follow the installation instructions then: await Purchases.configure({ apiKey: , useAmazon: true }); } ``` **Unity** ```cpp // The SDK can be configured through the Unity Editor. // See Unity installation instructions https://docs.revenuecat.com/docs/unity // If you'd like to configure the SDK programmatically, // make sure to check "Use runtime setup" in the Unity Editor, and then: Purchases.PurchasesConfiguration.Builder builder = Purchases.PurchasesConfiguration.Builder.Init(); Purchases.PurchasesConfiguration purchasesConfiguration = .SetAppUserId() .Build(); purchases.Configure(purchasesConfiguration); ``` **Web (JS/TS)** ```ts const appUserId = authentication.getAppUserId(); // Replace with your own authentication system const purchases = Purchases.configure({ apiKey: WEB_BILLING_PUBLIC_API_KEY, appUserId: appUserId, }); ``` ## Enabling Debug Logs Be sure to enable and view debug logs while implementing the SDK and testing your app. Debug logs contain important information about what's happening behind the scenes and should be the first thing you check if your app is behaving unexpectedly. As detailed in the sample code above, debug logs can be enabled or disabled by setting the `Purchases.logLevel` property before configuring *Purchases*. Debug logs will provide detailed log output in Xcode or LogCat for what is going on behind the scenes and should be the first thing you check if your app is behaving unexpectedly, and also to confirm there aren't any unhandled warnings or errors. ## Testing with Test Store After configuring the SDK, you can immediately start testing with your Test Store products—no additional SDK configuration required. Simply use your Test Store API key when initializing the SDK, and test purchases will work automatically. See [Sandbox Testing](https://www.revenuecat.com/docs/test-and-launch/sandbox) for details on testing with Test Store vs platform sandboxes. ### Switching between Test Store and Real Stores Test Store uses a **separate API key** from your real store API keys. This allows you to control which store your app communicates with: - **Test Store API Key**: Use during development and testing - **Platform Store API Keys** (iOS, Android, etc.): Use for production builds You can find both types of keys in **Project Settings > API keys** in the RevenueCat dashboard. Switch between them by changing which key you pass to the SDK configuration. :::danger\[CRITICAL: Never submit apps with Test Store API key] **You must NEVER submit an app to the App Store or Google Play that is configured with a Test Store API key.** Always use the correct platform-specific API key (iOS, Android, etc.) for release builds. We recommend using build configurations or environment variables to automatically use the correct API key for each build type: - **Development/Debug builds**: Test Store API key - **Production/Release builds**: Platform-specific API key (iOS, Android, etc.) ::: ## Additional Configuration The SDK allows additional configuration on first setup: - **API Key (required)**: The public API key that corresponds to your app, found via **Project Settings > API keys > App specific keys** in the RevenueCat dashboard. - **App User ID (optional)**: An identifier for the current user. Pass `null` if you don't have a user identifier at the time of configuration, RevenueCat will generate an anonymous App User ID for you. See our [guide on identifying users](https://www.revenuecat.com/docs/customers/user-ids) for more information. - **Purchases Completed By (optional)**: A boolean value to tell RevenueCat not to [complete purchases](https://www.revenuecat.com/docs/migrating-to-revenuecat/sdk-or-not/finishing-transactions). Only set purchase completion to your app if you have your own code handling purchases. - **User Defaults (optional, iOS only)**: A key to override the standard user defaults used to cache `CustomerInfo`. This is required if you need to access `CustomerInfo` in an [iOS App Extension](https://developer.apple.com/app-extensions/). ### Proxies & configuration for users in Mainland China, Russia, and Myanmar We've received reports of our API being blocked in mainland China, Russia, and Myanmar. While we work on a long-term solution, if your app has a significant user base in these regions, set the `proxyURL` property to `https://api.rc-backup.com/` before initializing the RevenueCat SDK. Ensure this configuration occurs prior to SDK setup to prevent connection issues for users in these regions. :::caution\[If you already have a proxy server] If you have your own proxy server and already use the `proxyURL` API, you don't need any further configuration. ::: **Swift** ```swift Purchases.proxyURL = URL(string: "https://api.rc-backup.com/")! ``` **Objective-C** ```objectivec [RCPurchases setProxyURL:[NSURL URLWithString:@"https://api.rc-backup.com/"]]; ``` **Kotlin** ```kotlin Purchases.proxyURL = URL("https://api.rc-backup.com/") ``` **Kotlin MP** ```kotlin Purchases.proxyURL = URL("https://api.rc-backup.com/") ``` **Java** ```java Purchases.setProxyURL(new URL("https://api.rc-backup.com/")); ``` **Flutter** ```dart await Purchases.setProxyURL("https://api.rc-backup.com/"); ``` **React Native** ```jsx await Purchases.setProxyURL("https://api.rc-backup.com/"); ``` **Cordova** ```jsx Purchases.setProxyURL("https://api.rc-backup.com/"); ``` **Capacitor** ```jsx await Purchases.setProxyURL({ url: 'https://api.rc-backup.com/' }); ``` **Unity** ```cpp // if you are configuring the SDK programmatically: Purchases purchases = GetComponent(); purchases.proxyURL = "https://api.rc-backup.com/"; // if you're configuring the SDK through the visual setup in Unity Editor instead, // set up the Proxy URL value in the configuration to `https://api.rc-backup.com/` ``` ### iOS #### Listening for CustomerInfo updates :::info\[Note] RevenueCat doesn't push new data to the SDK, so this method is only called when CustomerInfo is updated from another SDK method or after a purchase is made on the current device. ::: Implement the following delegate method to receive updates to the `CustomerInfo` object: ``` purchases:receivedUpdated ``` Called whenever *Purchases* receives an updated `CustomerInfo` object. This may happen periodically throughout the life of the app if new information becomes available (e.g. after making a purchase). #### Handling Promoted Purchases Implement the following delegate method to handle promoted purchases: ``` purchases:readyForPromotedProduct ``` Called when a user initiates a promoted in-app purchase from the App Store. If your app is able to handle a purchase at the current time, run the `defermentBlock` in this method. If the app is not in a state to make a purchase: cache the `defermentBlock`, then call the `defermentBlock` when the app is ready to make the promoted purchase. If the purchase should never be made, you don't need to ever call the `defermentBlock` and *Purchases* will not proceed with promoted purchases. ### Android #### Listening for CustomerInfo updates :::info\[Note] RevenueCat doesn't push new data to the SDK, so this method is only called when CustomerInfo is updated from another SDK method or after a purchase is made on the current device. ::: Implement the following listener to receive updates to the `CustomerInfo` object: ``` UpdatedCustomerInfoListener ``` Called whenever *Purchases* receives an updated `CustomerInfo` object. This may happen periodically throughout the life of the app if new information becomes available (e.g. after making a purchase). ## Next steps Once you've configured the SDK, you're ready to set up your products: - **Start with Test Store** (recommended): Your project already has a Test Store provisioned. [Create test products](https://www.revenuecat.com/docs/offerings/products-overview) and start testing immediately. - **Connect real stores**: When you're ready for production, [configure your products](https://www.revenuecat.com/docs/offerings/products-overview) in App Store Connect, Google Play Console, or other platforms. [Set up your products →](https://www.revenuecat.com/docs/projects/configuring-products) --- # iOS App Extensions Source: https://www.revenuecat.com/docs/getting-started/configuring-sdk/ios-app-extensions Markdown: https://www.revenuecat.com/docs/getting-started/configuring-sdk/ios-app-extensions.md [App Extensions](https://developer.apple.com/app-extensions/) in iOS are an important component of the iOS ecosystem that are supported by RevenueCat. The most popular use of App Extensions for subscription apps are Today Widgets and iMessage apps. Other target types, such as [App Clips](https://developer.apple.com/documentation/appclip) and [watchOS Apps](https://developer.apple.com/documentation/watchos-apps) are not app extensions, but can also be integrated with RevenueCat. Simply use the same public API key from your main RevenueCat project — there is no need to create an additional one. :::warning\[Purchases aren't allowed on extensions] Even though you can configure the SDK, it's just read-only. Purchasing will not work because extensions don't have access to the parent's app Bundle and therefore can't extract the receipt after a purchase ::: ## Configuring for App Extensions To enable data sharing between the main app and extensions, you'll need to use Xcode or the Developer portal to [enable app groups for the containing app and its contained app extensions](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW1). Then, [register the app group in the portal](https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html#//apple_ref/doc/uid/TP40011195-CH4-SW19) and specify the app group to use in the containing app. If you are building a Safari extension, you will need to configure and interact with the RevenueCat SDK in the Swift code, rather than in the Javascript code. After you enable app groups, you will be able to access a user's active subscriptions in your App Extension by configuring *Purchases* with a custom UserDefaults that's shared across your App Extension. ```swift Purchases.configure( with: Configuration.Builder(withAPIKey: ) .with(userDefaults: .init(suiteName: )) .build() ) ``` Now the app extension and parent app can both use the a shared UserDefaults suite. --- # Displaying Products Source: https://www.revenuecat.com/docs/getting-started/displaying-products Markdown: https://www.revenuecat.com/docs/getting-started/displaying-products.md If you've [configured Offerings](https://www.revenuecat.com/docs/getting-started/entitlements) in RevenueCat, you can control which products are shown to users without requiring an app update. Building paywalls that are dynamic and can react to different product configurations gives you maximum flexibility to make remote updates. :::info Before products and offerings can be fetched from RevenueCat, be sure to initialize the Purchases SDK by following our [Quickstart](https://www.revenuecat.com/docs/getting-started/quickstart) guide. ::: ## Fetching Offerings Offerings are fetched through the SDK based on their [configuration](https://www.revenuecat.com/docs/offerings/overview) in the RevenueCat dashboard. The `getOfferings` method will fetch the Offerings from RevenueCat. These are pre-fetched in most cases on app launch, so the completion block to get offerings won't need to make a network request in most cases. **Swift** ```swift Purchases.shared.getOfferings { (offerings, error) in if let packages = offerings?.current?.availablePackages { self.display(packages) } } ``` **Objective-C** ```objectivec [[RCPurchases sharedPurchases] getOfferingsWithCompletion:^(RCOfferings *offerings, NSError *error) { if (offerings.current && offerings.current.availablePackages.count != 0) { // Display packages for sale } else if (error) { // optional error handling } }]; ``` ```kotlin Purchases.sharedInstance.getOfferingsWith({ error -> // An error occurred }) { offerings -> offerings.current?.availablePackages?.takeUnless { it.isNullOrEmpty() }?.let { // Display packages for sale } } ``` ```kotlin Purchases.sharedInstance.getOfferings( onError = { error -> // An error occurred }, onSuccess = { offerings -> offerings.current?.availablePackages?.takeUnless { it.isEmpty() }?.let { // Display packages for sale } } ) ``` ```java Purchases.getSharedInstance().getOfferings(new ReceiveOfferingsCallback() { @Override public void onReceived(@NonNull Offerings offerings) { if (offerings.getCurrent() != null) { List availablePackages = offerings.getCurrent().getAvailablePackages(); // Display packages for sale } } @Override public void onError(@NonNull PurchasesError error) { // An error occurred } }); ``` **Flutter** ```dart try { Offerings offerings = await Purchases.getOfferings(); if (offerings.current != null && offerings.current.availablePackages.isNotEmpty) { // Display packages for sale } } on PlatformException catch (e) { // optional error handling } ``` **React Native** ```jsx try { const offerings = await Purchases.getOfferings(); if (offerings.current !== null && offerings.current.availablePackages.length !== 0) { // Display packages for sale } } catch (e) {   } ``` **Cordova** ```jsx func displayUpsellScreen() { Purchases.getOfferings( offerings => { if (offerings.current !== null && offerings.current.availablePackages.length !== 0) { // Display packages for sale } }, error => { } ); } ``` **Capacitor** ```jsx const displayUpsellScreen = async () => { try { const offerings = await Purchases.getOfferings(); if (offerings.current !== null && offerings.current.availablePackages.length !== 0) { // Display packages for sale } } catch (error) { // Handle error } } ``` **Unity** ```cpp var purchases = GetComponent(); purchases.GetOfferings((offerings, error) => { if (offerings.Current != null && offerings.Current.AvailablePackages.Count != 0){ // Display packages for sale } }); ``` **Web (JS/TS)** ```ts try { const offerings = await Purchases.getSharedInstance().getOfferings(); if ( offerings.current !== null && offerings.current.availablePackages.length !== 0 ) { // Display packages for sale displayPackages(offerings.current.availablePackages); } } catch (e) { // Handle errors } ``` :::warning\[Avoid pre-warming offerings cache in your Android's Application] Don't call `getOfferings` in your Android app's `Application.onCreate`. This might trigger additional network requests in some situations (like push notifications) without need, using your customer's data. The offerings cache should be pre-fetched automatically by the SDK. ::: :::info\[Offerings, products or available packages empty] If your offerings, products, or available packages are empty, it's due to some configuration issue in App Store Connect or the Play Console. You can find more information about troubleshooting this issue in our [Troubleshooting Guide](https://www.revenuecat.com/docs/offerings/troubleshooting-offerings). ::: You must choose one Offering that is the "Default Offering" - which can easily be accessed via the `current` property of the returned offerings for a given customer. :::info\[What's the difference between a current Offering and a default Offering?] The current Offering for a given customer may change based on the experiment they're enrolled in, any targeting rules they match, or the default Offering of your Project. Your Project's default Offering is the Offering that will be served as "current" when no other conditions apply for that customer. ::: To change the default Offering of your Project, navigate to the Offerings tab for that Project in the RevenueCat dashboard, and find the Offering you'd like to make default. Then, click on the icon in the Actions column of that Offering to reveal the available options, and click **Make Default** to make the change. ![Make default offering](https://www.revenuecat.com/docs_images/offerings/make-default.png) If you'd like to customize the Offering that's served based on an audience, or their location in your app, check out [Targeting](https://www.revenuecat.com/docs/tools/targeting). Offerings can be updated at any time, and the changes will go into effect for all users right away. ### Fetching Offerings by Placement Alternatively, if your app has multiple paywall locations and you want to control each location uniquely, you can do that with Placements and the `getCurrentOffering(forPlacement: "string")` method. ```swift Purchases.shared.getOfferings { offerings, error in if let offering = offerings?.currentOffering(forPlacement: "your-placement-identifier") { // TODO: Show paywall } else { // TODO: Do nothing or continue on to next view } } ``` **Kotlin** ```kotlin Purchases.sharedInstance.getOfferingsWith({ error -> // An error occurred }) { offerings -> offerings.getCurrentOfferingForPlacement("your-placement-identifier")?.let { // TODO: Show paywall } ?: run { // TODO: Do nothing or continue on to next view } } ``` **Java** ```java Purchases.getSharedInstance().getOfferings(new ReceiveOfferingsCallback() { @Override public void onReceived(@NonNull Offerings offerings) { Offering offering = offerings.getCurrentOfferingForPlacement("your-placement-identifier"); if (offering != null) { // TODO: Show paywall } else { // TODO: Do nothing or continue on to next view } } @Override public void onError(@NonNull PurchasesError error) { // An error occurred } }); ``` **Flutter** ```dart Offering offering = await Purchases.getCurrentOfferingForPlacement(placementIdentifier: "your-placement-identifier"); if (offering != null) { // TODO: Show paywall } else { // TODO: Do nothing or continue on to next view } ``` **React Native** ```jsx const offering = await Purchases.getCurrentOfferingForPlacement(inputValue); if (offering) { // TODO: Show paywall } else { // TODO: Do nothing or continue on to next view } ``` **Cordova** ```jsx const offering = await Purchases.getCurrentOfferingForPlacement("your-placement-identifier"); if (offering !== null) { // TODO: Show paywall } else { // TODO: Do nothing or continue on to next view } ``` **Capacitor** ```jsx const offering = await Purchases.getCurrentOfferingForPlacement({placementIdentifier: "your-placement-identifier"}); if (offering !== null) { // TODO: Show paywall } else { // TODO: Do nothing or continue on to next view } ``` **Unity** ```cpp var purchases = GetComponent(); purchases.GetCurrentOfferingForPlacement("your-placement-identifier", (offering, error) => { if (offering != null){ // TODO: Show paywall } else { // TODO: Do nothing or continue on to next view } } }); ``` To learn more about creating Placements and serving unique Offerings through them, [click here](https://www.revenuecat.com/docs/tools/targeting/placements). ### Custom Offering identifiers It's also possible to access other Offerings besides the Current Offering directly by its identifier. **Swift** ```swift Purchases.shared.getOfferings { (offerings, error) in if let packages = offerings?.offering(identifier: "experiment_group")?.availablePackages { self.display(packages) } } ``` **Objective-C** ```objectivec [[RCPurchases sharedPurchases] offeringsWithCompletionBlock:^(RCOfferings *offerings, NSError *error) { NSArray *availablePackages = [offerings offeringWithIdentifier:"experiment_group"].availablePackages; if (availablePackages) { // Display packages for sale } }]; ``` ```kotlin Purchases.sharedInstance.getOfferingsWith({ error -> // An error occurred }) { offerings -> offerings["experiment_group"]?.availablePackages?.takeUnless { it.isNullOrEmpty() }?.let { // Display packages for sale } } ``` ```kotlin Purchases.sharedInstance.getOfferings( onError = { error -> // An error occurred }, onSuccess = { offerings -> offerings["experiment_group"]?.availablePackages?.takeUnless { it.isEmpty() }?.let { // Display packages for sale } } ) ``` ```java Purchases.getSharedInstance().getOfferings(new ReceiveOfferingsCallback() { @Override public void onReceived(@NonNull Offerings offerings) { if (offerings.get("experiment_group") != null) { List availablePackages = offerings.get("experiment_group").getAvailablePackages(); // Display packages for sale } } @Override public void onError(@NonNull PurchasesError error) { // An error occurred } }); ``` **Flutter** ```dart try { Offerings offerings = await Purchases.getOfferings(); if (offerings.getOffering("experiment_group").availablePackages.isNotEmpty) { // Display packages for sale } } on PlatformException catch (e) { // optional error handling } ``` **React Native** ```jsx try { const offerings = await Purchases.getOfferings(); if (offerings.all["experiment_group"].availablePackages.length !== 0) { // Display packages for sale } } catch (e) {   } ``` **Cordova** ```jsx Purchases.getOfferings( offerings => { if (offerings.all["experiment_group"].availablePackages.length !== 0) { // Display packages for sale } }, error => { } ); ``` **Capacitor** ```jsx try { const offerings = await Purchases.getOfferings(); if (offerings.all["experiment_group"].availablePackages.length !== 0) { // Display packages for sale } } catch (error) { // Handle error } ``` **Unity** ```cpp var purchases = GetComponent(); purchases.GetOfferings((offerings, error) => { if (offerings.All.ContainsKey("experiment_group") && offerings.All["experiment_group"].AvailablePackages.Count != 0) { // Display packages for sale } }); ``` **Web (JS/TS)** ```ts try { const offerings = await Purchases.getSharedInstance().getOfferings(); if (offerings.all["experiment_group"].availablePackages.length !== 0) { // Display packages for sale displayPackages(offerings.all["experiment_group"].availablePackages); } } catch (e) { // Handle errors } ``` ## Displaying Packages Packages help abstract platform-specific products by grouping equivalent products across iOS, Android, and web. A package is made up of three parts: identifier, type, and underlying store product. | Name | Description | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Identifier | The package identifier (e.g. `com.revenuecat.app.monthly`) | | Type | The type of the package:
- `UNKNOWN`
- `CUSTOM`
- `LIFETIME`
- `ANNUAL`
- `SIX_MONTH`
- `THREE_MONTH`
- `TWO_MONTH`
- `MONTHLY`
- `WEEKLY` | | Product | The underlying product that is mapped to this package which includes details about the price and duration. | Packages can be access in a few different ways: 1. via the `.availablePackages` property on an Offering. 2. via the duration convenience property on an Offering 3. via the package identifier directly **Swift** ```swift let packages = offerings.offering(identifier: "experiment_group")?.availablePackages // -- let monthlyPackage = offerings.offering(identifier: "experiment_group")?.monthly // -- let packageById = offerings.offering(identifier: "experiment_group")?.package(identifier: "") ``` ```objectivec [offerings offeringWithIdentifier:"experiment_group"].availablePackages // -- [offerings offeringWithIdentifier:"experiment_group"].monthly // -- [[offerings offeringWithIdentifier:"experiment_group"] packageWithIdentifier:@""] ``` ```kotlin offerings["experiment_group"]?.availablePackages // -- offerings["experiment_group"]?.monthly // -- offerings["experiment_group"]?.getPackage("") ``` **Flutter** ```dart offerings.getOffering("experiment_group").availablePackages // -- offerings.getOffering("experiment_group").monthly // -- offerings.getOffering("experiment_group").getPackage("") ``` **React Native** ```jsx offerings.all["experiment_group"].availablePackages // -- offerings.all["experiment_group"].monthly // -- offerings.all["experiment_group"].availablePackages.find(package => package === "") ``` **Capacitor/Cordova** ```jsx offerings.all["experiment_group"].availablePackages // -- offerings.all("experiment_group").monthly // -- offerings.all("experiment_group").package("") ``` **Unity** ```cpp offerings.All["experiment_group"].AvailablePackages // -- offerings.All["experiment_group"].Monthly // -- // Manually filter AvailablePackages by the custom package identifier ``` **Web (JS/TS)** ```ts const allPackages = offerings.all["experiment_group"].availablePackages; // -- const monthlyPackage = offerings.all["experiment_group"].monthly; // -- const customPackage = offerings.all["experiment_group"].packagesById[""]; ``` #### Getting the Product from the Package Each Package includes an underlying product that includes more information about the price, duration, and other metadata. You can access the product via the `storeProduct` property (or `webBillingProduct` property for [RevenueCat Billing](https://www.revenuecat.com/docs/web/web-billing/web-sdk)): **Swift** ```swift Purchases.shared.getOfferings { (offerings, error) in // Accessing the monthly product if let product = offerings?.current?.monthly?.storeProduct { // Display the product information (like price and introductory period) self.display(product) } } ``` **Objective-C** ```objectivec // Accessing the monthly product [[RCPurchases sharedPurchases] offeringsWithCompletionBlock:^(RCOfferings *offerings, NSError *error) { if (offerings.current && offerings.current.monthly) { SKProduct *product = offerings.current.monthly.storeProduct; // Get the price and introductory period from the StoreProduct } else if (error) { // optional error handling } }]; ``` **Kotlin** ```kotlin // Accessing the monthly product Purchases.sharedInstance.getOfferingsWith({ error -> // An error occurred }) { offerings -> val product = offerings.current?.monthly?.product?.also { // Get the price and introductory period from the SkuDetails } } ``` ```kotlin Purchases.sharedInstance.getOfferings( onError = { error -> // An error occurred }, onSuccess = { offerings -> val product = offerings.current?.monthly?.storeProduct?.also { // Get the price and introductory period from the StoreProduct } } ) ``` **Java** ```java // Accessing the monthly product Purchases.getSharedInstance().getOfferings(new ReceiveOfferingsCallback() { @Override public void onReceived(@NonNull Offerings offerings) { if (offerings.getCurrent() != null && offerings.getCurrent().getMonthly() != null) { StoreProduct product = offerings.getCurrent().getMonthly().getProduct(); // Get the price and introductory period from the StoreProduct } } @Override public void onError(@NonNull PurchasesError error) { // An error occurred } }); ``` **Flutter** ```dart // Accessing the monthly product// Displaying the monthly product try { Offerings offerings = await Purchases.getOfferings(); if (offerings.current != null && offerings.current.monthly != null) { StoreProduct product = offerings.current.monthly.storeProduct; // Get the price and introductory period from the Product } } on PlatformException catch (e) { // optional error handling } ``` **React Native** ```jsx // Accessing the monthly product// Displaying the monthly product try { const offerings = await Purchases.getOfferings(); if (offerings.current && offerings.current.monthly) { const product = offerings.current.monthly.product; // Get the price and introductory period from the PurchasesProduct } } catch (e) {} ``` **Cordova** ```jsx // Accessing the monthly product func displayUpsellScreen() { Purchases.getOfferings( offerings => { if (offerings.current && offerings.current.monthly) { const product = offerings.current.monthly; // Get the price and introductory period from the PurchasesProduct } }, error => { } ); } ``` **Capacitor** ```jsx // Accessing the monthly product const displayUpsellScreen = async () => { try { const offerings = await Purchases.getOfferings(); if (offerings.current && offerings.current.monthly) { const product = offerings.current.monthly; // Get the price and introductory period from the PurchasesProduct } } catch (error) { // Handle error } } ``` **Unity** ```cpp // Accessing the monthly product var purchases = GetComponent(); purchases.GetOfferings((offerings, error) => { if (offerings.Current != null && offerings.Current.Monthly != null){ var product = offerings.Current.Monthly.Product; // Get the price and introductory period from the Product } }); ``` **Web (JS/TS)** ```ts // Accessing / displaying the monthly product try { const offerings = await Purchases.getSharedInstance().getOfferings({ currency: "USD", }); if (offerings.current && offerings.current.monthly) { const product = offerings.current.monthly.webBillingProduct; // Display the price and currency of the Web Billing Product displayProduct(product); } } catch (e) { // Handle errors } ``` ## Choosing which Offering to display In practice, you may not want to display the default current Offering to every user and instead have a specific cohort that see a different Offering. For example, displaying a higher priced Offering to users that came from [paid acquisition](https://www.revenuecat.com/docs/integrations/attribution) to help recover ad costs, or a specific Offering designed to show [iOS Subscription Offers](https://www.revenuecat.com/docs/subscription-guidance/subscription-offers/ios-subscription-offers) when a user has [cancelled their subscription](https://www.revenuecat.com/docs/customers/customer-info#section-get-entitlement-information). This can be accomplished through Targeting, which supports a handful of predefined dimensions from RevenueCat or **any** custom attribute you set for your customers. [Learn more here.](https://www.revenuecat.com/docs/tools/targeting) Or, alternatively, you could write your own logic locally in your app to serve custom Offering identifiers for each cohort you have in mind. **Swift** ```swift Purchases.shared.getOfferings { (offerings, error) in var packages: [Package]? if user.isPaidDownload { packages = offerings?.offering(identifier: "paid_download_offer")?.availablePackages } else if user.signedUpOver30DaysAgo { packages = offerings?.offering(identifier: "long_term_offer")?.availablePackages } else if user.recentlyChurned { packages = offerings?.offering(identifier: "ios_subscription_offer")?.availablePackages } // Present your paywall self.display(packages) } ``` **Objective-C** ```objectivec [[RCPurchases sharedPurchases] offeringsWithCompletionBlock:^(RCOfferings *offerings, NSError *error) { NSArray *packages; if (user.isPaidDownload) { packages = [offerings offeringWithIdentifier:"paid_download_offer"].availablePackages; } else if (user.signedUpOver30DaysAgo) { packages = [offerings offeringWithIdentifier:"long_term_offer"].availablePackages; } else if (user.recentlyChurned) { packages = [offerings offeringWithIdentifier:"ios_subscription_offer"].availablePackages; } [self presentPaywallWithPackages:packages]; }]; ``` **Kotlin** ```kotlin Purchases.sharedInstance.getOfferingsWith({ error -> // An error occurred }) { offerings -> val packages: Package? = when { user.isPaidDownload -> offerings["paid_download_offer"]?.availablePackages user.signedUpOver30DaysAgo -> offerings["long_term_offer"]?.availablePackages user.recentlyChurned -> offerings["ios_subscription_offer"].availablePackages else -> null } presentPaywall(packages) } ``` ```kotlin Purchases.sharedInstance.getOfferings( onError = { error -> // An error occurred }, onSuccess = { offerings -> val packages: List = when { user.isPaidDownload -> offerings["paid_download_offer"]?.availablePackages user.signedUpOver30DaysAgo -> offerings["long_term_offer"]?.availablePackages user.recentlyChurned -> offerings["ios_subscription_offer"]?.availablePackages else -> null }.orEmpty() presentPaywall(packages) } ) ``` **Java** ```java Purchases.getSharedInstance().getOfferings(new ReceiveOfferingsCallback() { @Override public void onReceived(@NonNull Offerings offerings) { List packages = null; if (user.isPaidDownload) { if (offerings.get("paid_download_offer") != null) { packages = offerings.get("paid_download_offer").getAvailablePackages(); } } else if (user.signedUpOver30DaysAgo) { if (offerings.get("long_term_offer") != null) { packages = offerings.get("long_term_offer").getAvailablePackages(); } } presentPaywall(packages); } @Override public void onError(@NonNull PurchasesError error) { // An error occurred } }); ``` **Flutter** ```dart try { Offerings offerings = await Purchases.getOfferings(); var packages; if (user.isPaidDownload) { packages = offerings?.getOffering("paid_download_offer")?.availablePackages; } else if (user.signedUpOver30DaysAgo) { packages = offerings?.getOffering("long_term_offer")?.availablePackages; } else if (user.recentlyChurned) { packages = offerings?.getOffering("ios_subscription_offer")?.availablePackages; } presentPaywall(packages); } on PlatformException catch (e) { // optional error handling } ``` **React Native** ```jsx try { const offerings = await Purchases.getOfferings(); let packages; if (user.isPaidDownload) { packages = offerings.all["paid_download_offer"].availablePackages; } else if (user.signedUpOver30DaysAgo) { packages = offerings.all["long_term_offer"].availablePackages; } else if (user.recentlyChurned) { packages = offerings.all["ios_subscription_offer"].availablePackages; } presentPaywall(packages); } catch (e) {   } ``` **Cordova** ```jsx Purchases.getOfferings( offerings => { let packages; if (user.isPaidDownload) { packages = offerings.all["paid_download_offer"].availablePackages; } else if (user.signedUpOver30DaysAgo) { packages = offerings.all["long_term_offer"].availablePackages; } else if (user.recentlyChurned) { packages = offerings.all["ios_subscription_offer"].availablePackages; } presentPaywall(packages); }, error => { } ); ``` **Capacitor** ```jsx Purchases.getOfferings( offerings => { let packages; if (user.isPaidDownload) { packages = offerings.all["paid_download_offer"].availablePackages; } else if (user.signedUpOver30DaysAgo) { packages = offerings.all["long_term_offer"].availablePackages; } else if (user.recentlyChurned) { packages = offerings.all["ios_subscription_offer"].availablePackages; } presentPaywall(packages); }, error => { } ); ``` **Unity** ```cpp var purchases = GetComponent(); purchases.GetOfferings((offerings, error) => { List packages; if (user.isPaidDownload) { packages = offerings.All["paid_download_offer"].AvailablePackages; } else if (user.signedUpOver30DaysAgo) { packages = offerings.All["long_term_offer"].AvailablePackages; } else if (user.recentlyChurned) { packages = offerings.All["ios_subscription_offer"].AvailablePackages; } presentPaywall(packages); }); ``` ## Best Practices | Do | Don't | | :-------------------------------------------------------------------------- | :------------------------------------------------------------- | | ✅ Make paywalls dynamic by minimizing or eliminating any hardcoded strings | ❌ Make static paywalls hardcoded with specific product IDs | | ✅ Use default package types | ❌ Use custom package identifiers in place of a default option | | ✅ Allow for any number of product choices | ❌ Support only a fixed number of products | | ✅ Support for different free trial durations, or no free trial | ❌ Hardcode free trial text | ## Next Steps - Now that you've shown the correct products to users, time to [make a purchase ](https://www.revenuecat.com/docs/getting-started/making-purchases) - Check out our [sample apps ](https://www.revenuecat.com/docs/platform-resources/sample-apps) for examples of how to display products. --- # Entitlements Source: https://www.revenuecat.com/docs/getting-started/entitlements Markdown: https://www.revenuecat.com/docs/getting-started/entitlements.md :::info\[Looking for details on product configuration?] See [Products Overview](https://www.revenuecat.com/docs/offerings/products-overview) for more information on configuring products and importing them to RevenueCat. ::: RevenueCat Entitlements represent a level of access, features, or content that a user is "entitled" to. Entitlements are scoped to a [project](https://www.revenuecat.com/docs/projects/overview), and are typically unlocked after a user purchases a [product](https://www.revenuecat.com/docs/offerings/products-overview). Entitlements are used to ensure a user has appropriate access to content based on their purchases, without having to manage all of the product identifiers in your app code. For example, you can use entitlements to unlock "pro" features after a user purchases a subscription. Most apps only have one entitlement, unlocking all premium features. However, if you had two tiers of content such as Gold and Platinum, you would have 2 entitlements. A user's entitlements are shared across all apps contained within the same project. ### Creating an Entitlement To create a new entitlement, click **Product catalog** in the left menu of the **Project** dashboard, click the **Entitlements** tab, and click **+ New entitlement.** You'll need to enter a unique identifier for your entitlement that you can reference in your app, like "pro". Most apps only have one entitlement, but create as many as you need. For example a navigation app may have a subscription to "pro" access, and one-time purchases to unlock specific map regions. In this case there would probably be one "pro" entitlement, and additional entitlements for each map region that could be purchased. ![](https://www.revenuecat.com/docs_images/offerings/entitlements.png) ### Attaching Products to Entitlements Once entitlements are created, you should attach products to entitlements. This lets RevenueCat know which entitlements to unlock for users after they purchase a specific product. When viewing an Entitlement, click the **Attach** button to attach a product. If you've already added your products, you'll be able to select one from the list to attach. ![](https://www.revenuecat.com/docs_images/offerings/entitlements-attach-product.png) When a product that is attached to an entitlement is purchased, that entitlement becomes active for the duration of the product. Subscription products will unlock entitlements for the subscription duration, and non-consumable and consumable purchases that are attached to an entitlement will unlock that content **forever**. If you have non-subscription products, you may or may not want to add them to entitlements depending on your use case. If the product is non-consumable (e.g. lifetime access to "pro" features), you likely want to attach it to an entitlement. However, if it is consumable (e.g. purchase more lives in a game) you likely do not want to add them to an entitlement. Attaching an entitlement to a product will grant that entitlement to any customers that have previously purchased that product. Likewise, detaching an entitlement from a product will remove it for any customers that have previously purchased that product. When designing your Entitlement structure, keep in mind that a single product can unlock multiple entitlements, and multiple products may unlock the same entitlement. ![Example Entitlement structure with associated Apple, Google, Stripe, or Amazon product identifiers.](https://www.revenuecat.com/docs_images/entitlements/example-structure.png) :::info When relying on entitlements to enable access to certain content, it's important that you remember to add new products to their associated entitlements if needed. Failing to add your products to an entitlement, could lead to your users making purchases that don't unlock access to the promised content. ::: ## Checking Entitlement Status Since an entitlement represents a level of access that a user is entitled to, you'll want to check for entitlement status in your app to unlock the appropriate content. If an entitlement is active, you can unlock the associated content. If an entitlement is inactive, you can display a paywall to the user. You can use the RevenueCat SDK to check for entitlement status, with the `getCustomerInfo` method. You can read more about checking subscription and purchase status in the [Checking Subscription Status](https://www.revenuecat.com/docs/customers/customer-info) guide. ## Next steps If you've configured your entitlements, it's time to create an Offering. [Create an Offering →](https://www.revenuecat.com/docs/offerings/overview) --- # Amazon Product Setup Source: https://www.revenuecat.com/docs/getting-started/entitlements/amazon-product-setup Markdown: https://www.revenuecat.com/docs/getting-started/entitlements/amazon-product-setup.md To set up products for the Amazon Appstore, start by logging into your [Amazon developer account](https://developer.amazon.com/apps-and-games). This guide assumes basic knowledge of the Amazon Appstore, as well as having an app set up and ready for adding in-app purchases. For more information, visit Amazon's [documentation and guides for Amazon Appstore](https://developer.amazon.com/documentation). ### Create an In-App Purchase To create an in-app purchase, go to [Amazon developer console](https://developer.amazon.com/dashboard) and select 'App List' under Amazon Appstore. ![1caf053-app\_list.png](https://www.revenuecat.com/docs_images/products/amazon/0fb6817-1caf053-app_list_73947c7bc79fc589401df846d8fa926a.png) ![cf73dad-app.png](https://www.revenuecat.com/docs_images/products/amazon/486ca7a-cf73dad-app_0cd77d7215a532f896897f5889d79368.png) In the sidebar, select **'In-App Items'**. ![9254d2c-in-app\_items.png](https://www.revenuecat.com/docs_images/products/amazon/8a31a08-9254d2c-in-app_items_18264a0bb9b690b8e145288cabc0b533.png) Click on **'+ Add Single IAP'**. ![c800dc2-add\_iap.png](https://www.revenuecat.com/docs_images/products/amazon/2c18950-c800dc2-add_iap_f7f7c0c8e1c2d72efcf94f656206b4f5.png) You will be presented with a dropdown where you select the type of in-app purchase you want to add to your app. We're going to show you how to set up a **Subscription** here, but the steps are similar for other types of in-app purchases. ![28999aa-iap\_types.png](https://www.revenuecat.com/docs_images/products/amazon/a19cd05-28999aa-iap_types_ad1a96167c45c31706846f98051af786.png) Next, you'll be asked to provide a **Subscription Title** and a **Subscription SKU**. ![af64f55-create\_subscription.png](https://www.revenuecat.com/docs_images/products/amazon/d6729ff-af64f55-create_subscription_434859776b785816d820257e21e1fc85.png) - **Title**: The title is the title of your item and will not be seen by the customer. The name cannot be longer than 128 characters. - **SKU**: The SKU is a unique string that will become the ID for the item. The SKU must be unique across all IAP items in all of your apps. Note that SKUs are case-sensitive, cannot be longer than 150 characters, and can contain the characters a-z, A-Z, 0-9, underscores, periods, and dashes. Since this is a subscription item, this SKU becomes the parent SKU for the subscription term SKUs that you will create later. After you click 'Add Subscription', you will be directed to the item's Details page where you can configure the following additional data: - **Description & Images**: A display name and description images for the item. - **Subscription Terms** (Subscriptions only): Specify subscription length and free trial information for the item. This is where you also set price for the subscription. - **Pricing** (Amazon's Consumables and Entitlements only): Set the price for the item. ![11998ba-description\_images.png](https://www.revenuecat.com/docs_images/products/amazon/3b1a1c4-11998ba-description_images_8283c5db41cb211f087ae44c053af510.png) Under *Description & Images*, add a Display Title and Description. This is what your customers will see. **(optional)** - Update localization: Check off boxes for every language your app has been localized for. ![74616c3-update\_localization.png](https://www.revenuecat.com/docs_images/products/amazon/23e00d4-74616c3-update_localization_b6a3b2346cb25f3b72df1420509ce6d8.png) Add a Display Title and Description for every language. ![7414f6f-localization.png](https://www.revenuecat.com/docs_images/products/amazon/94e5399-7414f6f-localization_cc7d16db1d5e924e2453406ae71f572c.png) Next, add an **Icon** for every language you support. - Small icon (114px x 114px) - Large icon (512px x 512 px) ### Add Subscription Terms ![26eb5df-terms.png](https://www.revenuecat.com/docs_images/products/amazon/950e50d-26eb5df-terms_8180232fca08bdea291845d0925f2543.png) ![2264040-blank\_term.png](https://www.revenuecat.com/docs_images/products/amazon/cdda82e-2264040-blank_term_66e436c29b9803591a227ecd31784b31.png) - **Term Period**: This starts on the date of purchase. Valid values are **Weekly**, **Bi-Weekly** (every two weeks), **Monthly**, **Bi-Monthly** (every two months), **Quarterly**, **Semi-Annually**) (every six months), or **Annually** (every twelve months). - **Term SKU**: This is the SKU that corresponds to this subscription term. This SKU is a child SKU of the SKU that you entered in the item detail page. For the purpose of this example, we want to create an annual subscription that costs $49.99 with a 1 week free-trial: ![f7ef8ad-create\_term.png](https://www.revenuecat.com/docs_images/products/amazon/1e48dda-f7ef8ad-create_term_9393d481d15803422ceadee7fac04e46.png) #### Tips for creating robust term SKU > **`___0`** - **app:** Some prefix that will be unique to your app, since the same product Id cannot but used in any future apps you create. - **price:** The price you plan to charge for the product in your default currency. - **duration:** The duration of the normal subscription period. - **free trial duration:** The duration of the trial period, if any. For example, using this format the identifier for a product that has a yearly subscription with a one week trial for $49.99 USD would be: > **`rc_4999_1y_1w0`** ![1ff8cd0-set\_price.png](https://www.revenuecat.com/docs_images/products/amazon/35ea745-1ff8cd0-set_price_65cb0a724bb7715182e7b03053721537.png) - **Free Trial**: Specify an optional free trial period for the subscription. Valid values are No (no free trial), 7 days, 14 days, 1 month, 2 months, and 3 months. - **Are you charging for this subscription?**: Yes, if you are charging for the subscription, No, if the subscription will be free. If you specify Yes, a field displays allowing you to set the base price and currency for the item. After you set the base price, you will have the option of either manually setting the price for other currencies, or allowing the Amazon Appstore to set those prices for you, based on conversion rates and taxes. Valid prices (in USD) can either be $0.00 or range from $0.99 to $299.99. If your app offers additional subscription periods, repeat this section for all terms your app provides, such as Weekly, Monthly, Bi-Annually, and so on. Once you are ready, click on **'Submit IAP'** at the top of the page. If this button will remain greyed-out until you provide all of the required information for the in-app item. ![5578d30-submit\_iap.png](https://www.revenuecat.com/docs_images/products/amazon/e4ae155-5578d30-submit_iap_01ad5a77c4c746345e33909db34e7581.png) ## Integrate with RevenueCat If you're ready to integrate your new Amazon in-app product with RevenueCat, continue our [product setup guide →](https://www.revenuecat.com/docs/getting-started/entitlements). --- # Google Play Product Setup Source: https://www.revenuecat.com/docs/getting-started/entitlements/android-products Markdown: https://www.revenuecat.com/docs/getting-started/entitlements/android-products.md To set up products for Android devices, start by logging into [Google Play Console](https://play.google.com/console). Google Play Console is Google's central hub for managing app releases, testing, in-app purchases, and more. This guide assumes basic knowledge of Google Play Console, as well as having an app set up and ready for adding in-app purchases. For more information, visit [Google's documentation and guides for Google Play Console](https://support.google.com/googleplay/android-developer/?hl=en#topic=3450769). ## Create an In-App Product or Subscription :::info You'll need to have an APK uploaded before you can create in-app products. Check out our guide on [sandbox testing on Android](https://www.revenuecat.com/docs/test-and-launch/sandbox/google-play-store) for details on how to upload an APK and roll out a release on a closed test track. ::: To create an in-app product or subscription, go to Google Play Console's 'All Applications' page and select your app from the list. In the sidebar, select the **Products** dropdown. Depending on your in-app product type, you will either choose **In-app products** or **Subscriptions**. ![](https://www.revenuecat.com/docs_images/products/google-play/config/create-app-product-subscription.png "2020-10-09 18.02.44 play.google.com 16c50bed37ae.png") After clicking Create, provide a couple pieces of metadata to Google: | Metadata | Description | | :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Product ID | The product ID is a unique alphanumeric ID that is used for accessing your product in development and syncing with RevenueCat. After you use a Product ID for one product in Google Play Console, **it can’t be used again across any of your apps, even if the product is deleted**. | | Name | A short name of the item, up to 55 characters. This will be displayed on your Google Play Store listing. | ### Tips for creating robust product IDs After you use a Product ID for one product in Google Play Console, **it can’t be used again across any of your apps, even if the product is deleted**. It helps to be a little organized here from the beginning - we recommend using a consistent naming scheme across all of your product identifiers such as: > **`__`** - **app:** Some prefix that will be unique to your app, since the same product Id cannot but used in any future apps you create. - **entitlement**: A name for what the product provides access to, e.g., "premium" - **version**: A version number For example, using this format the identifier for your first product that grants access to a "premium" subscription would be: > `rc_premium_v1` ![](https://www.revenuecat.com/docs_images/products/google-play/config/tips-creating-robust-product.png "Screen Shot 2022-06-28 at 5.51.57 PM.png") ### Create a base plan For subscription products, you'll need to add a base plan. Base plans define a billing period, price, and renewal type for purchasing your subscription. Customers never purchase a subscription product directly, they always purchase a base plan of a subscription. Click "Add base plan" and fill out the associated fields. Make sure to set a price, and click "Activate". Since Google introduced multiple base plans with Billing Client 5, it's good practice to be as clear as possible when naming your plans, such as: `-`, eg. `annual-autorenewing`. ![Screenshot 2023-07-27 at 4 56 24 PM](https://www.revenuecat.com/docs_images/products/google-play/config/screenshot-2023-07-27-56-24.png) :::success\[Migrated products from before May 2022] When Google introduced the new subscription features in May 2022, all existing subscriptions were migrated to subscription products with a single base plan. That base plan has an identifier representing the duration, like `P1Y` which stands for annual. ::: :::info\[Representation of Google Play subscription products in RevenueCat] RevenueCat Products map to Base Plans for Google Play subscriptions, since those are the products that customers can purchase. Newly set up products in RevenueCat follow the identifier format `:`, whereas products that were set up before February 2023 follow the identifier format ``. ::: :::danger\[Support for non backwards-compatible base plans] Old versions of RevenueCat SDKs do not support Google's new subscription features such as multiple base plans per subscription product. Only base plans marked as "[backwards compatible](https://support.google.com/googleplay/android-developer/answer/12124625?hl=en#backwards_compatible)" in Google Play Console are available in these SDK versions. [Learn more](https://www.revenuecat.com/docs/getting-started/entitlements/google-subscriptions-and-backwards-compatibility). Only one base plan per subscription can be marked as backwards compatible. ::: To mark a base plan as backwards compatible, click the overflow menu on the base plan and select "Use for deprecated billing methods". ![](https://www.revenuecat.com/docs_images/products/google-play/config/create-base-plan-mark.png "f309ab8-Screen_Shot_2022-07-07_at_2.12.18_PM.png") ### (Optional) Create an offer If you wish to create an offer for your base plan, you can do so from the subscription page by clicking "Add offer". Offers can be free trials, discounts, or simply special price setups that apply when a customer first purchases a subscription. ![](https://www.revenuecat.com/docs_images/products/google-play/config/optional-create-offer-you.png "Screen Shot 2022-06-30 at 3.58.40 PM.png") You can then select a product ID, eligibility, and offer phases. :::danger\[Support for non-backwards-compatible offers] Old versions of RevenueCat SDKs do not support Google's new subscription features such as multiple offers per base plan. Only offers marked as "[backwards compatible](https://support.google.com/googleplay/android-developer/answer/12124625?hl=en#backwards_compatible)" in Google Play Console are available in these SDK versions. [Learn more](https://www.revenuecat.com/docs/getting-started/entitlements/google-subscriptions-and-backwards-compatibility). Only one offer per base plan can be marked as backwards compatible. ::: To mark an offer as backwards compatible, click the overflow menu and select "Use for deprecated billing methods". ![](https://www.revenuecat.com/docs_images/products/google-play/config/optional-create-offer-mark.png "Screen Shot 2022-07-07 at 2.12.18 PM.png") ### Add non-consumable products If you want your customers to be able to purchase a certain In-App product only once (for example, a lifetime purchase), you need to configure the product as a non-consumable when creating it in the RevenueCat dashboard. ![](https://www.revenuecat.com/docs_images/products/google-play/config/non-consumable-android-support.png "non-consumable-android-support.png") If you don't configure it as a non-consumable, we will automatically `consume` the purchase and Google will allow the customer to purchase it again. The purchase will still be registered in that customer's `CustomerInfo`. You can also edit existing or imported consumable products to make them non-consumable. :::info Non-consumable support is supported in Android SDK version 7.11.0 and up. In previous versions, the SDK will always consume the purchase. ::: ### Making Subscriptions Editable, InAppProduct API **RevenueCat does not use the InAppProduct API for subscriptions.** You are safe to make subscriptions editable, **unless** you are manually using this API outside of RevenueCat. This is related to this notice: ![](https://www.revenuecat.com/docs_images/products/google-play/config/making-subscriptions-editable-inappproduct.png "Screen Shot 2022-07-07 at 2.23.03 PM.png") ![](https://www.revenuecat.com/docs_images/products/google-play/config/making-subscriptions-editable-inappproduct-1.png "4a6f1139-085f-4e5c-8132-5d5573ec2cca.png") If you are relying solely on RevenueCat for your subscriptions, you can safely select "Make editable". ## Editing products You can edit pricing, naming, and other metadata of products in Google Play Console and those changes will be available in your app within a few hours. ## Integrate with RevenueCat If you're ready to integrate your new Google Play in-app product with RevenueCat, continue our [product setup guide ](https://www.revenuecat.com/docs/getting-started/entitlements). --- # Galaxy Store Product Setup Source: https://www.revenuecat.com/docs/getting-started/entitlements/galaxy-products Markdown: https://www.revenuecat.com/docs/getting-started/entitlements/galaxy-products.md To set up products for the Galaxy Store, start by logging into your [Galaxy Store Seller Portal](https://seller.samsungapps.com/). The Galaxy Store Seller Portal is your central hub for managing app releases, testing, in-app purchases, and more. This guide assumes basic knowledge of the Galaxy Store, as well as having an app set up and ready for adding in-app purchases. For more information, visit Galaxy's [documentation and guides for the Galaxy Store](https://developer.samsung.com/galaxy-store/prepare.html). ## Create an In-App Product or Subscription To create an in-app product or subscription, sign in to the [Galaxy Store Seller Portal](https://seller.samsungapps.com/). Navigate to your app under **Apps**, then select **In App Purchase**, then **Add New Product**. ![Add New Product in Galaxy Store Seller Portal](https://www.revenuecat.com/docs_images/platform-resources/galaxy/add_new_product.png "Add New Product in Galaxy Store Seller Portal") In the dialog that appears, select if you'd like your product to be a **Subscription** or **Item** (one-time purchase). ### Product Details Enter the relevant product details: | Field | Description | | :----------------------- | :-------------------------------------------------------------------------------------------- | | **Product ID** | A unique identifier for your product. You'll use this to reference the product in RevenueCat. | | **Product Title** | The name of your product, displayed to users. | | **Description** | A description of what the product provides. | | **Price** | Your desired price point for the product. | | **Subscription Details** | For subscriptions, configure the billing period, renewal settings, etc. | ### Activate Your Product Once the product is created, you must activate it before users can purchase it: 1. Check the newly-created product checkbox 2. Click **Activate** 3. Click **OK** in the confirmation pop-up that appears ![Activate Product on the Galaxy Store Seller Portal](https://www.revenuecat.com/docs_images/platform-resources/galaxy/activate_product.png "Activate Product on the Galaxy Store Seller Portal") :::warning You must activate your newly created product in the Galaxy Store Seller Portal before you will be able to fetch the product and purchase it with the RevenueCat SDK. ::: ## Add the Product to RevenueCat After creating and activating your product in Galaxy Store, you need to add it to your RevenueCat product catalog. 1. In the RevenueCat dashboard, navigate to **Product catalog → Products** 2. Click **+ New product** and select your Galaxy Store app 3. Enter the product's **Identifier** (the Product ID from Galaxy Store) and **Display name** 4. Select the product's type: - **Subscription:** For recurring subscriptions - **Consumable:** One-time purchase that may be purchased more than once - **Non-consumable:** One-time purchase that can only be purchased once ![Create product in the RevenueCat dashboard](https://www.revenuecat.com/docs_images/platform-resources/galaxy/new_product_in_rc_dashboard.png "Create product in the RevenueCat dashboard") Once added, you can attach this product to an entitlement to define what access it unlocks for your users. [Learn more about products and entitlements →](https://www.revenuecat.com/docs/getting-started/entitlements) --- # Google Subscriptions and Backwards Compatibility Source: https://www.revenuecat.com/docs/getting-started/entitlements/google-subscriptions-and-backwards-compatibility Markdown: https://www.revenuecat.com/docs/getting-started/entitlements/google-subscriptions-and-backwards-compatibility.md ## RevenueCat compatibility with Google May 2022 Subscription Changes In May 2022, Google introduced several [new features for subscription products](https://developer.android.com/google/play/billing/compatibility). These features are not supported in old versions of the RevenueCat SDK ([see table below](#revenuecat-sdk-version-support)). Only products marked as “backwards compatible” in the Google Play Console are functional with RevenueCat in those older SDKs. As of purchases-android v6 (and equivalent versions of cross-platform SDKs), Google’s new subscription setup configurations are supported. We’ve automatically migrated your app to use those backwards-compatible products with all SDKs. To take full advantage of the newer Google subscription configurations and features in RevenueCat Offerings, RevenueCat now allows setting up a backwards compatible fallback product that will only be used for apps using old versions of the SDK. ## Product backwards compatibility When creating Google Play products you can now specify whether the product is marked as backwards compatible in the Play Console. RevenueCat uses this information to know whether this product can be purchased by older version of the SDK (versions 5 and below). ![](https://www.revenuecat.com/docs_images/products/google-play/config/1efcc0b-Screenshot_2023-04-10_at_15.15.08_c800fd2a9ce23735b3d2ca7251edfc79.png) This information is also displayed in the product details page and will be synced from the Google play store regularly (checking the product details and status will trigger a sync immediately). ![](https://www.revenuecat.com/docs_images/products/google-play/config/b7b34e8-Screenshot_2023-04-10_at_15.13.41_2540aee4fdc2595e40ebfecc38799b77.png) ## App compatibility setting In the settings page for Google Play apps, you can change whether RevenueCat Offerings should support only new versions of the RevenueCat SDK (versions 6 and above or equivalent cross-platform SDKs). You should choose the setting "Only Android SDK v6+" **only if** you have never used an earlier version of the RevenueCat Android SDK in production, or if you are confident that versions of your app using previous versions of the SDK do not constitute a substantial proportion of your customer base anymore. ![Google app setting: SDK support in offering setup](https://www.revenuecat.com/docs_images/products/google-play/config/96831d2-Screenshot_2023-03-27_at_11.03.06_1930c93c744b1ceaca8086972512db40.png) If you select the setting "SDK v6+ and backwards compatible" and you are attaching a non-backwards compatible product to an Offering, you will additionally be able to attach a backwards compatible fallback product for use with SDK versions 5 and below and equivalent cross-platform SDKs. Please note that each version of the SDK will always only see one product per package of an offering – when a fallback product is set up, SDK v6+ will only see the regular, non-backward compatible product, and SDK v5 and below will only see the backward compatible fallback product. ![Selecting a backwards compatible fallback product](https://www.revenuecat.com/docs_images/products/google-play/config/39a73e1-Screenshot_2023-03-21_at_10.54.52_74db96e1ed7314ffe45704b51e5e01bc.png) :::danger\[Why are my offerings empty when using "Only Android SDK v6+"?] If setting the SDK support to "Only Android SDK v6+", Offerings will not contain any products for older versions of the SDK. ::: ## Migration of existing products to SDK v6+ In order to support the new Google Play features through the RevenueCat Android SDK v6+ and above, any existing products set up in your app were automatically migrated. This step does not impact compatibility with previous versions of the SDK. Old SDK versions will continue to work as before, regardless of whether or not the migration was successful. In some cases, the migration might have failed. This could be due to invalid Play Store service credentials, a product identifier being mistyped in RevenueCat, or the product having been deleted in Google Play Console in the meantime. In these cases, a warning will be displayed in the products page and when attempting to attach such a product to an Offering: ![](https://www.revenuecat.com/docs_images/products/google-play/config/e465cfc-Screenshot_2023-01-30_at_12.18.01_da31ca53e353fdc690c07e5837906e7f.png "Screenshot 2023-01-30 at 12.18.01.png") Since we are lacking required data to purchase this product in the RevenueCat Android SDK v6+, it will not work with this version of the SDK. In addition, products that couldn't be migrated prevent the creation or import of new products with the same identifier to prevent conflicts. To fix this problem, you can try one of the following: - Delete the product in RevenueCat side and re-create or import it. - Update your [Play Store service credentials](https://www.revenuecat.com/docs/service-credentials/creating-play-service-credentials) in the app's settings in RevenueCat. This will re-trigger the migration. Please allow a few minutes for the migration to complete, and then check the product status again. - Create a new product with a new identifier. If neither of these helps, please contact our [support team](https://www.revenuecat.com/support). ## RevenueCat SDK version support The following table shows which SDK versions require backwards compatible products and which versions support all Google Play products: | RevenueCat SDK | Version requiring backwards compatible product | Versions supporting all products | | :----------------------- | :--------------------------------------------- | :------------------------------- | | purchases-android | v5 and below | v6 and above | | purchases-react-native | v5 and below | v6 and above | | purchases-flutter | v4 and below | v5 and above | | purchases-unity | v4 and below | v5 and above | | cordova-plugin-purchases | v3 and below | v4 and above | --- # iOS Product Setup Source: https://www.revenuecat.com/docs/getting-started/entitlements/ios-products Markdown: https://www.revenuecat.com/docs/getting-started/entitlements/ios-products.md To set up products for iOS, iPadOS, macOS, tvOS, and watchOS, start by logging into [App Store Connect](https://appstoreconnect.apple.com). App Store Connect is Apple's central hub for managing app releases, TestFlight, in-app purchases, and more. **This guide assumes basic knowledge of App Store Connect, as well as having an app set up and ready for adding in-app purchases.** If you're setting up your developer account for the first time, start with our [App Store Connect Setup Guide](https://www.revenuecat.com/docs/platform-resources/apple-platform-resources/app-store-connect-setup-guide). For more information, visit Apple's [documentation and guides for App Store Connect](https://developer.apple.com/support/app-store-connect/). :::info\[Make sure your Paid Applications Agreement is signed] Before you set up your products, make sure you have the latest Paid Applications Agreement signed in in the "Business" module in App Store Connect. **You will not be able to test in-app purchases until the latest version of this agreement is signed with Apple**. In addition, go through the "Tax" and "Banking" tabs and sign any forms as required. You have to link a bank account to App Store Connect and have the status be "Clear" before being able to test in-app purchases. ::: ## Create an In-App Purchase To create an in-app purchase, go to App Store Connect's '[My Apps](https://appstoreconnect.apple.com/apps)' page and select your app from the list. ![](https://www.revenuecat.com/docs_images/products/ios/config/create-app-purchase-create.png "Screen_Shot_2020-06-24_at_4.33.09_PM.png") ![](https://www.revenuecat.com/docs_images/products/ios/config/create-app-purchase-create-1.png "Screen Shot 2020-06-26 at 3.15.20 PM.png") In the sidebar, select '**Subscriptions**' under Features, then click the '**+**' symbol to create a Subscription Group. ![](https://www.revenuecat.com/docs_images/products/ios/config/create-app-purchase-sidebar.png "Screen Shot 2022-12-05 at 11.48.08 AM.png") :::info If you don't see the Subscriptions option, ensure your developer account has accepted all applicable contracts and have provided tax and banking information in the 'Agreements, Tax, and Banking' section of App Store Connect. **Signing all agreements and tax forms and adding banking information is required to continue through this guide.** ::: Subscription Groups are ways to organize your products in App Store Connect so users are able to switch between products. You can read more about Subscription Groups in our [blog post here](https://www.revenuecat.com/blog/ios-subscription-groups-explained). If you don't have any Subscription Groups configured yet, you'll be prompted to provide a **Reference Name**. Similar to the product Reference Name you set earlier, this is not user-facing so we recommend using a string you can understand. ![Screen Shot 2021-06-09 at 10.45.44 AM.png](https://www.revenuecat.com/docs_images/products/ios/config/f999077-Screen_Shot_2021-06-09_at_10.45.44_AM_6bb6d3c3c0ac257f41704d362796cccd.png) After creating your Subscription Group, click the **+** symbol to add a new product to the group. ![](https://www.revenuecat.com/docs_images/products/ios/config/create-app-purchase-creating.png "Screen Shot 2022-12-05 at 11.53.23 AM.png") This process is going to configure an **Auto-Renewable Subscription**, but the steps are similar for other types of in-app purchases. To configure other types of in-app purchases, click the "In-App Purchases" tab in the sidebar instead of "Subscriptions". Next, you'll be asked to provide a **Reference Name** and a **Product ID**. ![](https://www.revenuecat.com/docs_images/products/ios/config/create-app-purchase-next.png "Screen Shot 2020-06-26 at 3.24.49 PM.png") - **Reference Name:** The reference name will be used on App Store Connect and in Sales and Trends reports from Apple. It won't be displayed to your users on the App Store. We recommend using a human readable description of the purchase you plan to set up. The name can't be longer than 64 characters. - **Product ID:** The product Id is a unique alphanumeric ID that is used for accessing your product in development and syncing with RevenueCat. After you use a Product ID for one product in App Store Connect, **it can’t be used again across any of your apps, even if the product is deleted**. It helps to be a little organized here from the beginning - we recommend using a consistent naming scheme across all of your product identifiers such as: > **`___`** - **app:** Some prefix that will be unique to your app, since the same product Id cannot but used in any future apps you create. - **price:** The price you plan to charge for the product in your default currency. - **duration:** The duration of the normal subscription period. - **intro duration:** The duration of the introductory period, if any. - **intro price:** The price of the introductory period in your default currency, if any. In this case, I want to set up a yearly subscription with a one week trial for $39.99 USD. Using this format I've set my product identifier as: **`rc_3999_1y_1w0`** :::info\[Pro Tip] Using a consistent naming scheme across product identifiers in App Store Connect can save you time in the future and make it easier to organize and understand your products with only the identifier. ::: ## Setting Subscription Duration Once your product is created, you'll be able to set the duration of the auto-renewable subscription. Use the duration dropdown to choose an option, and click **Save**. ![](https://www.revenuecat.com/docs_images/products/ios/config/setting-subscription-duration-once.png "Screen Shot 2020-06-26 at 4.06.56 PM.png") ## Setting Subscription Price To set the price of your subscription, click the '**+**' icon in the **Subscription Prices** section. ![](https://www.revenuecat.com/docs_images/products/ios/config/setting-subscription-price-set.png "Screen Shot 2020-06-26 at 4.08.32 PM.png") You'll be presented with a modal where you can select a **Price** from a dropdown in your default currency. When you click **Next**, Apple will automatically set the price in all App Store regions based off the price and currency you selected. You'll have the option to edit these, but we recommend sticking with the defaults. When done, click **Create**. ![](https://www.revenuecat.com/docs_images/products/ios/config/setting-subscription-price-you.png "Screen Shot 2020-06-26 at 4.11.38 PM.png") Last step, don't forget to **Save**! ![](https://www.revenuecat.com/docs_images/products/ios/config/setting-subscription-price-last.png "Screen Shot 2020-06-26 at 4.15.06 PM.png") ## Adding Introductory Offers and Free Trials To add an introductory offer or free trial to your product, navigate to the **Introductory Offers** tab on the same page you just configured pricing. Click the '**+**' icon next to Introductory Offers to set one up. ![](https://www.revenuecat.com/docs_images/products/ios/config/adding-introductory-offers-free.png "Screen Shot 2020-06-26 at 4.18.05 PM.png") You'll be presented with a modal with a few configuration screens: 1. **Countries or Regions for Introductory Offer:** Use this if you want the introductory offer or trial to be region specific. Most of the time the answer here is "no", so go ahead and click Next. 2. **Introductory Offer Start/End Date:** Set the start and end dates if you want the introductory offer or trial to be a limited time deal. In most cases, you'll be setting the Start Date to today and No End Date, then click Next. On the last screen, you'll get to choose the **Type of Introductory Offer**. Free trials are the most common type of introductory offer, and that's what we'll set up here. Select the **Free** radio button and choose the desired **Duration** from the dropdown. You can read more about the different Introductory Offer types in our [blog post here](https://medium.com/revenuecat-blog/ios-introductory-prices-f1efb4f1a6a2). ![](https://www.revenuecat.com/docs_images/products/ios/config/adding-introductory-offers-free-1.png "Screen Shot 2020-06-26 at 4.30.31 PM.png") Just like with regular prices, don't forget to click **Save** when you're done. ![](https://www.revenuecat.com/docs_images/products/ios/config/adding-introductory-offers-free-2.png "Screen Shot 2020-06-26 at 4.34.38 PM.png") ## Adding Localization The next piece to set up is localization information for the App Store. This is the name and description of the in-app purchase that the user will see. In the App Store Information section, click the '**+**' icon next to Localization and choose the language you with to set up. ![](https://www.revenuecat.com/docs_images/products/ios/config/adding-localization-app-store.png "Screen Shot 2020-06-26 at 4.37.17 PM.png") Next, you'll need to provide a **Subscription Display Name** and a **Description**. ![](https://www.revenuecat.com/docs_images/products/ios/config/adding-localization-next-you.png "Screen Shot 2020-06-26 at 4.58.12 PM.png") The Subscription Display Name and Description **will be visible to the user** on the App Store and in their subscription management settings. We recommend a short display name that describes the level of access the purchase unlocks, and **we recommend using the same Subscription Display Name for all of your products that unlock the same level of access**. Using the same name will result in a cleaner App Store listing and cause less confusion among users as your suite of products grow. :::info\[Pro Tip] Use the same Subscription Display Name and Description for all of your products that unlock the same level of access. This results in a much cleaner App Store listing as your suite of products grows. ::: ## Add Reviewer Information The last part of setting up an in-app purchase in iOS is adding information for the reviewer. This is a Screenshot, and optional Review Notes. Often times developers overlook the screenshot, but you'll be unable to submit your product for review without it. ![](https://www.revenuecat.com/docs_images/products/ios/config/add-reviewer-information-last.png "Screen Shot 2020-06-26 at 5.07.04 PM.png") - **Screenshot:** A required image of your in-app purchase paywall for the reviewer. While testing, it's okay to upload an empty 640 x 920 image here of whatever you want. Before submitting for review, you should add a picture of your paywall. - **Review Notes:** An optional text area to clarify anything about your in-app purchase for the reviewer. ## Subscription Groups If you're configuring products for the first time and just set up a subscription group, you may see a warning in App Store Connect: Before you can submit your in-app purchase for review, you must add at least one localization to your subscription group. Add localizations ![](https://www.revenuecat.com/docs_images/products/ios/config/subscription-groups-you-submit.png "Screen Shot 2020-06-26 at 5.12.52 PM.png") Clicking on the **Add localizations** link will take you to the Subscription Group configuration. Similar to how you added localizations to the product, you'll need to add localizations to the Subscription Group as well. ![](https://www.revenuecat.com/docs_images/products/ios/config/subscription-groups-clicking-add.png "Screen Shot 2021-06-18 at 8.17.30 PM.png") Next, you'll need to provide a **Subscription Group Display Name** and an **App Name**. Like the Subscription Display Name you set up earlier, this **will be visible to the user** on the App Store and in their subscription management settings. ![](https://www.revenuecat.com/docs_images/products/ios/config/subscription-groups-next-you.png "Screen Shot 2020-06-26 at 5.19.31 PM.png") ![](https://www.revenuecat.com/docs_images/products/ios/config/subscription-groups-subscription-group.png "Screen Shot 2020-06-26 at 5.18.11 PM.png") - **Subscription Group Display Name:** Just like the product localizations, we recommend a short display name that describes the level of access the subscription group unlocks, and if you use a multi-subscription group strategy for things like price testing **we recommend using the same Subscription Group Display Name for all of your subscription groups that unlock the same level of access**. - **App Name:** Apple provides you with a couple of options for the app display name that the users will see on their subscription. You can choose your app name from the App Store listing, or a Custom Name. Using a Custom Name is useful if your App Store listing title is slightly different than your app name. For example, if your App Store listing was titled "*VSCO - Photo Filters*", you may want to use a Custom Name for your subscriptions of just "*VSCO*". :::info\[Pro Tip] Use the same Subscription Group Display Name if you plan on creating multiple Subscription Groups that unlock the same content. Typically these types of strategies are used for price testing and offering discounts. ::: Don't forget to click **Save** before exiting. ## Integrate with RevenueCat If you're ready to integrate your new App Store Connect in-app product with RevenueCat, continue our [product setup guide →](https://www.revenuecat.com/docs/getting-started/entitlements). --- # Paddle Product Setup Source: https://www.revenuecat.com/docs/getting-started/entitlements/paddle-products Markdown: https://www.revenuecat.com/docs/getting-started/entitlements/paddle-products.md To set up products for Paddle, start by logging into the Paddle Dashboard. This guide assumes basic knowledge of Paddle and the Paddle Dashboard. For more information, visit Paddle's [documentation and guides](https://paddle.com/docs). ## Create a new Product To create a new product, expand the **Catalog** section in the sidebar and click **Products**. On the top right corner of the page, click **New Product**. Enter the product name and any other optional details like a description then click **Save**. ![](https://www.revenuecat.com/docs_images/web/paddle/create-product.png) Then on the prices section, click **New Price**. Enter details like the base price, the type of pricing (recurring or one-time), the billing period, and any trial periods you are offering and click **Save**. ![](https://www.revenuecat.com/docs_images/web/paddle/create-price.png) :::info\[Product Mapping between RevenueCat and Paddle] A Price in Paddle maps to a Product in the RevenueCat system. So for example, if you create two prices under the same Paddle product, when you import or manually create the products in the RevenueCat dashboard, you'll notice two separate products. ![](https://www.revenuecat.com/docs_images/web/paddle/paddle_dashboard_prices.png) ![](https://www.revenuecat.com/docs_images/web/paddle/revenuecat_paddle_product_mapping.png) ::: You can read more about **products and prices** in [Paddle's official documentation](https://developer.paddle.com/build/products/create-products-prices). ## Integrate with RevenueCat If you're ready to integrate your new Paddle product with RevenueCat, continue our [product setup guide →](https://www.revenuecat.com/docs/getting-started/entitlements). --- # Roku Product Setup Source: https://www.revenuecat.com/docs/getting-started/entitlements/roku-products Markdown: https://www.revenuecat.com/docs/getting-started/entitlements/roku-products.md To set up in-channel products for Roku, start by logging into the [Roku dashboard](https://developer.roku.com/dev/landing). **This guide assumes basic knowledge of Roku and the Roku dashboard, as well as having a Roku channel set up and ready for products.** For more information, visit Roku's [documentation](https://developer.roku.com/docs/developer-program/getting-started/roku-dev-prog.md). ## Create a new Product This process is going to configure a subscription product, but the steps are similar for creating other products. To configure other types of products, select the appropriate 'Purchase Type'. To create a new in-channel product, click on products in the sidebar of the Roku Developer Dashboard, then click **Add a new product**. ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-products.png) ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-add-product.png) ### Product basics ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-product-basics.png) - **Channels**: From the channels list, select one or more channels where this product will be available for sale. The channels listed in this selection show the channels belonging to the root account. - **Product category**: Select a product category for the product you are creating. - *Video*: Primarily video content, includes music videos. - *Audio*: Primarily audio content without accompanying video, such as streaming music services or audio-only podcasts. - *Game*: Primarily functions as a game. - *App/Utility*: Application or utility. Examples include screensavers, weather apps, etc. - **Product name**: Enter a 30-character maximum product name in English. The product name will be disaplued to your customers in their purchasing workflow, as well as emails sent by Roku. Roku recommends the following syntax: "channelName - planName". :::warning The product name must clearly identify the service being offered. Product names may not include the name "Roku", text related to a trial or discount offer, or any misleading language. ::: - **Localization**: Optionally, you can also provide a localized product name by selecting 'Add product name in another language', selecting a language, and entering the product localized name. Repeat this to create another localized name. - **Product identifier**: The product identifier is a unique ID that is used for accessing your product in development and syncing with RevenueCat. After you use a Product ID for one product within a Roku Channel Store, it can’t be used again. It helps to be a little organized here from the beginning - we recommend using a consistent naming scheme across all of your product identifiers. ### Product pricing ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-product-pricing.png) - **Purchase type**: The purchase type list will allow you to select the following types for the product being created: - *Monthly subscription*: A product that will auto-renew monthly. - *Yearly subscription*: A product that will auto-renew annually. - *One-time Purchase*: This product type may only be purchased a single time. - *One-time Purchase, Consumable - Quantity* This is a "packet" of identical items (e.g: number of viewings permitted). :::warning\[One-time and consumable product limitations] At the moment, RevenueCat does not support One-time Purchase and One-time Purchase, Consumable - Quantity products ::: - **Price tier**: Roku's price tiers enforce a 99 cent or 49 cent pricing tier. - One to three-digit tier numbers are used for 99 cent pricing. To calculate, you can subtract 1 cent from the tier to get the corresponding price. For example, Tier 100 is $99.99 (`$100 - $0.01 = $99.99`). - Four-digit tier numbers are used for 49 cent pricing. To calculate this, you can add 49 cents to the last two digits in the tier. For example, Tier 1030 is $30.49 (30 is the last 2 digits → `$30 + $0.49 = $30.49`). Once you select a price tier, a chart will appear that displays the purchase price, net price, and proceeds for each country the product is available for. - **Purchase price**: Reflects the amount your customer will pay. - **Net price**: This is the pre-tax price. - **Your proceeds**: This is the amount you will receive from Roku for the sale of the product. ### Trials and offers Roku subscription products support free trials and discounted offers. Note that the root account must be creating free trials, discounted offers, or limited-time offers for subscriptions. Under **Base offer**, select one of the following: ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-discounted-offer.png) - **Discounted price**: This will provide new customers a discounted introductory price. - *Discounted price range*: This is the discounted price you'd like to offer. The discounted price range must be lower than the base price. - *Discount duration*: Enter the number of months the discount will be until the customer renews at full price. ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-free-trial.png) - **Free trial**: This will provide new customers with a free trial of your product. - *Free trial duration*: Enter the number of days or months for the trial - Select the unit of time (**Days** or **Months**) ### Ready for sale Once the product is ready to be made available to customers for purchase, select the *"Cleared for sale"* checkbox. After selecting this checkbox, you will be able to [schedule limited-time free trials and discount offers](https://www.revenuecat.com/docs/getting-started/entitlements/roku-products#scheduling-offers) for the product. ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-product-save.png) Remember to select 'Save' ### Scheduling offers Once your product is cleared for sale, you can schedule limited-time free trials and discount offers on your subscription products. Within your product details, you can select 'Schedule offer' > 'Create new offer'. ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-schedule-offer.png) Please refer to the [Trials and offers](https://www.revenuecat.com/docs/getting-started/entitlements/roku-products#trials-and-offers) section of this documentation for additional instructions on creating an offer. When scheduling an offer, you must input a **Start date** and **End date**. Note that a customer can only receive one free trial or discount offer, regardless if it is a scheduled offer or part of your base product. For example, if you have a monthly subscription product with the following offers: - Time-limited offer: Two-month free trial - Base offer: Three-month 50% discount When your customer accepts a two-month free trial, once that trial is over the customer will be billed at full price. If your subscription product does not contain a time-limited offer, the customer would be billed for the first three months at 50% then convert to paying full price. ## Editing / deleting products ### Edit products You can edit a product by selecting the **Product name** in your **Manage In-Channel Products** index page. You may want to edit a product if you no longer wish to list a product for sale. :::warning\[Editing cleared for sale] Note that changing the **Cleared for Sale** to "No" will cancel all existing subscriptions of the product and will not renew at the end of the billing period. ::: ### Deleting products Deleted products cannot be recovered. :::warning\[Deleting products that are cleared for sale] Note that deleting a product without first changing its **Cleared for Sale** status to "No" will keep the current subscriptions of the product active and will prevent additional purchases of the subscription product. ::: ## Product groups Product groups are used for upgrade/downgrade functionality and to prevent double billing your customers. For more information regarding upgrades/downgrades, please visit our documentation on [*Upgrades, Downgrades, & Management*](https://www.revenuecat.com/docs/subscription-guidance/managing-subscriptions#roku). To set up a product group, navigate to your *'Manage In-Channel Products' > 'All product groups' > 'Add a new group'* ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-product-group.png) ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-create-product-group.png) - **Group name**: Enter a descriptive name for your group. For example, if you are creating a product group containing monthly and annual plans that will unlock the highest tier subscription, it could be named "High tier subscriptions" - **Channel list**: Select the channel that will use this product group Once you have selected your channel, your in-channel products will appear on the right-hand side. Select which products you'd like to include in your product group, then click **+ Add to group** on the right-hand side to add the product names to your group. To remove a product from your group, select the product name under **Remove from group** on the left-hand side and select **Remove from group** ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-add-product-to-group.png) Remember to select 'Save' ## Integrate with RevenueCat If you're ready to integrate your new Roku product with RevenueCat, continue our [product setup guide →](https://www.revenuecat.com/docs/getting-started/entitlements). --- # Stripe Product Setup Source: https://www.revenuecat.com/docs/getting-started/entitlements/stripe-products Markdown: https://www.revenuecat.com/docs/getting-started/entitlements/stripe-products.md To set up products for Stripe, start by logging into the Stripe Dashboard. This guide assumes basic knowledge of Stripe and the Stripe Dashboard. For more information, visit Stripe's [documentation and guides for the Dashboard](https://stripe.com/docs/dashboard). ## Create a new Product To create a new product, click on products in the sidebar of the Stripe Dashboard, then click **Add Product**. ![](https://www.revenuecat.com/docs_images/products/stripe/config/create-new-product-create.png "Screen_Shot_2020-06-26_at_12.53.37_PM.png") ![](https://www.revenuecat.com/docs_images/products/stripe/config/create-new-product-enter.png "Screen_Shot_2020-06-26_at_12.53.57_PM.png") Enter details about the product, including name and price information, and click 'Save'. ![](https://www.revenuecat.com/docs_images/products/stripe/config/create-new-product-enter-1.png "Screen_Shot_2020-07-01_at_3.59.51_PM.png") The ID of the new product is a unique identifier that is automatically generated by Stripe, prefixed with `prod_`. This is the identifier that you'll need later to [setup products in RevenueCat](https://www.revenuecat.com/docs/getting-started/entitlements). :::warning\[Important] RevenueCat Web purchase flows currently support flat-rate recurring and one-off Stripe prices. Package pricing can be synced for external purchases, but cannot be sold through RevenueCat Web purchase flows. Metered usage, tiered pricing, and customer-chooses pricing are not supported. See [Stripe Billing pricing model compatibility](https://www.revenuecat.com/docs/web/integrations/stripe#pricing-model-compatibility) for the current details. ::: ## Integrate with RevenueCat If you're ready to integrate your new Stripe product with RevenueCat, continue our [product setup guide →](https://www.revenuecat.com/docs/getting-started/entitlements). --- # Installing the SDK Source: https://www.revenuecat.com/docs/getting-started/installation Markdown: https://www.revenuecat.com/docs/getting-started/installation.md *Purchases* is our SDK that correctly implements purchases and subscriptions across platforms while syncing tokens with the RevenueCat server. Check out the install guides below integrate the SDK into all of your apps. - [iOS & Apple Platforms Installation →](https://www.revenuecat.com/docs/getting-started/installation/ios) - [Android Installation →](https://www.revenuecat.com/docs/getting-started/installation/android) - [React Native Installation →](https://www.revenuecat.com/docs/getting-started/installation/reactnative) - [Expo Installation →](https://www.revenuecat.com/docs/getting-started/installation/expo) - [Flutter Installation →](https://www.revenuecat.com/docs/getting-started/installation/flutter) - [Kotlin Multiplatform Installation →](https://www.revenuecat.com/docs/getting-started/installation/kotlin-multiplatform) - [Cordova Installation →](https://www.revenuecat.com/docs/getting-started/installation/cordova) - [Capacitor Installation →](https://www.revenuecat.com/docs/getting-started/installation/capacitor) - [Unity Installation →](https://www.revenuecat.com/docs/getting-started/installation/unity) - [Web Installation →](https://www.revenuecat.com/docs/getting-started/installation/web-sdk) - [Roku →](https://www.revenuecat.com/docs/getting-started/installation/roku) ## Next steps After you've installed the SDK, it's time to configure it with your API key. [Configure the SDK →](https://www.revenuecat.com/docs/getting-started/configuring-sdk) --- # Android Source: https://www.revenuecat.com/docs/getting-started/installation/android Markdown: https://www.revenuecat.com/docs/getting-started/installation/android.md ## What is RevenueCat? RevenueCat provides a backend and a wrapper around StoreKit and Google Play Billing to make implementing in-app purchases and subscriptions easy. With our SDK, you can build and manage your app business on any platform without having to maintain IAP infrastructure. You can read more about [how RevenueCat fits into your app](https://www.revenuecat.com/blog/growth/where-does-revenuecat-fit-in-your-app/) or you can [sign up free](https://app.revenuecat.com/signup) to start building. ## Android ### Installation Purchases for Android (Google Play and Amazon Appstore) is available on Maven and can be included via Gradle. You can find the latest version below, and for more details, visit the [Releases page](https://github.com/RevenueCat/purchases-android/releases). [![Release](https://img.shields.io/github/v/release/RevenueCat/purchases-android.svg?\&style=flat)](https://github.com/RevenueCat/purchases-android/releases) **Kotlin** ```kotlin implementation("com.revenuecat.purchases:purchases:9.23.1") ``` **Groovy** ```groovy implementation 'com.revenuecat.purchases:purchases:9.1.0' ``` ### Import Purchases You should now be able to import `Purchases`. **Kotlin** ```kotlin import com.revenuecat.purchases.CustomerInfo import com.revenuecat.purchases.Entitlement import com.revenuecat.purchases.Offering import com.revenuecat.purchases.Purchases import com.revenuecat.purchases.models.Period import com.revenuecat.purchases.models.Price import com.revenuecat.purchases.models.StoreProduct ``` **Java** ```java import com.revenuecat.purchases.CustomerInfo; import com.revenuecat.purchases.Entitlement; import com.revenuecat.purchases.Offering; import com.revenuecat.purchases.Purchases; import com.revenuecat.purchases.models.Period; import com.revenuecat.purchases.models.Price; import com.revenuecat.purchases.models.StoreProduct; ``` ### Configure Proguard (Optional) We are adding Proguard rules to the library so you don't need to do anything. If you have any issues finding classes in our SDK, try adding `-keep class com.revenuecat.purchases.** { *; }` to your Proguard configuration. :::warning Purchases uses AndroidX App Startup under the hood. Make sure you have not removed the `androidx.startup.InitializationProvider` completely in your manifest. If you need to remove specific initializers, such as `androidx.work.WorkManagerInitializer`, set `tools:node="merge"` on the provider, and `tools:node="remove"` on the meta-data of the initializer you want to remove. ```xml ``` ::: ### Set the correct launchMode Depending on your user's payment method, they may be asked by Google Play to verify their purchase in their (banking) app. This means they will have to background your app and go to another app to verify the purchase. If your Activity's `launchMode` is set to anything other than `standard` or `singleTop`, backgrounding your app can cause the purchase to get cancelled. To avoid this, set the `launchMode` of your Activity to `standard` or `singleTop` in your `AndroidManifest.xml` file, like so: ```xml ``` You can find Android's documentation on the various `launchMode` options [here](https://developer.android.com/guide/topics/manifest/activity-element#lmode). ## Amazon ### Additional Dependencies Add a new dependency to the `build.gradle` apart from the regular `purchases` dependency. These new dependencies have the classes needed to use Amazon IAP: You can find the latest version below, and for more details, visit the [Releases page](https://github.com/RevenueCat/purchases-android/releases). [![Release](https://img.shields.io/github/v/release/RevenueCat/purchases-android.svg?\&style=flat)](https://github.com/RevenueCat/purchases-android/releases) **Kotlin** ```kotlin implementation("com.revenuecat.purchases:purchases:9.23.1") implementation("com.revenuecat.purchases:purchases-store-amazon:9.23.1") ``` **Groovy** ```groovy implementation 'com.revenuecat.purchases:purchases:9.1.0' implementation 'com.revenuecat.purchases:purchases-store-amazon:9.1.0' ``` ### Add Amazon public key Adding support for Amazon requires adding a `.pem` public key to your project. You can configure this key by following Amazon's guide [here](https://developer.amazon.com/es/docs/in-app-purchasing/integrate-appstore-sdk.html#configure_key). Due to some limitations, RevenueCat will only validate purchases made in production or in Live App Testing and won't validate purchases made with the Amazon App Tester. ## Galaxy Store Galaxy Store support is available in Android SDK versions `10.7.0` and above, and React Native SDK versions `10.3.0` and above. Support for other hybrid SDKs is coming soon. ### Additional Dependencies In addition to the regular `purchases` dependency, the `purchases-store-galaxy` module must be added as well. You can find the latest version below, and for more details, visit the [Releases page](https://github.com/RevenueCat/purchases-android/releases). [![Release](https://img.shields.io/github/v/release/RevenueCat/purchases-android.svg?\&style=flat)](https://github.com/RevenueCat/purchases-android/releases) **Kotlin** ```kotlin implementation("com.revenuecat.purchases:purchases:10.7.0") implementation("com.revenuecat.purchases:purchases-store-galaxy:10.7.0") ``` **Groovy** ```groovy implementation 'com.revenuecat.purchases:purchases:10.7.0' implementation 'com.revenuecat.purchases:purchases-store-galaxy:10.7.0' ``` ### Configuring the SDK When you configure the RevenueCat SDK, configure it to use the Galaxy Store by using a `GalaxyConfiguration` object, like so: ```kotlin Purchases.configure( // This will configure the Galaxy Store to make production purchases. GalaxyConfiguration.Builder(applicationContext, "galx_XXXX") // Add your RevenueCat API key here .build() ) ``` ### SDK Usage Once your project is set up, you can use the RevenueCat SDK with the Galaxy Store in the same manner that you would for the Play Store, including fetching offerings, displaying paywalls, and making purchases. ### Making Test Purchases Test purchases for the Galaxy Store can only be made on a **physical Galaxy device** when signed in with a Samsung account—the Galaxy Store does not support making test purchases in emulators. When you want to make a test purchase, pass in either `GalaxyBillingMode.TEST` or `GalaxyBillingMode.ALWAYS_FAIL` when you call `Purchases.configure()`, like so: ```kotlin Purchases.configure( GalaxyConfiguration.Builder( applicationContext, "galx_XXXX", // Add your RevenueCat API key here GalaxyBillingMode.TEST ) .build() ) ``` Using `GalaxyBillingMode.TEST` will allow you to make test purchases without creating financial transactions, while `GalaxyBillingMode.ALWAYS_FAIL` allows you to test failure scenarios. For more information on the Galaxy Store's billing modes, refer to [their documentation](https://developer.samsung.com/iap/programming-guide/iap-helper-programming.html#Set-the-IAP-operation-mode). :::warning Only use `GalaxyBillingMode.PRODUCTION` when submitting your app for beta or production distribution! ::: To cancel a test subscription, go to the Galaxy Store on your phone, go to your Samsung account, and cancel the subscription. Test subscriptions will remain active until their current billing period ends. ## Next Steps - Now that you've installed the Purchases SDK in your Android app, get started by [configuring an instance of Purchases ](https://www.revenuecat.com/docs/getting-started/quickstart#3-using-revenuecats-purchases-sdk) --- # App Builders / No-Code Source: https://www.revenuecat.com/docs/getting-started/installation/app-builders Markdown: https://www.revenuecat.com/docs/getting-started/installation/app-builders.md The increase of no- and low-code app building solutions is on an upward trend. It's an exciting upgrade in technological accessibility and a promising look into possibility, and one that we look forward to being able to support in the future. In the meantime, we’ve partnered with a few app builders and development tools to bring you RevenueCat’s powerful in-app purchase server and backend without needing to start an app the traditional way. ## How do I get support? The answer to this depends on the question you have, or the area that you need support in. Since these aren’t official integrations, but rather partner-built solutions, the partners themselves will know best how to support the implementation and management of these tools. If your questions are in regards to configuring products, entitlements, or offerings on RevenueCat’s dashboard, or how to interact with any of our metrics and charts, please don’t hesitate to [contact RevenueCat support](https://app.revenuecat.com/settings/support). ## How do I get started? Use the links below to find the set up guide on each of our partner's sites. | Platform | Description | | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | | [Bravo Studios](https://docs.bravostudio.app/integrations/in-app-purchases-and-subscriptions-revenuecat) | No-code mobile app builder with native iOS and Android app creation | | [Flutterflow](https://docs.flutterflow.io/settings-and-integrations/in-app-purchases-and-subscriptions/revenuecat) | Visual development platform for building Flutter apps with a drag-and-drop interface | | [Natively](https://docs.buildnatively.com/guides/setup-revenuecat-app) | Low-code platform for building native iOS and Android apps | | [Teta](https://docs.teta.so/teta-docs/teta-introduction/dashboard/settings/integrations/revenuecat) | Visual app builder for Flutter with real-time collaboration features | | [Thunkable](https://docs.thunkable.com/blocks/app-features/in-app-purchase-blocks-with-revenuecat) | Drag-and-drop platform for creating mobile apps without coding | | [Median.co](https://docs.median.co/docs/revenue-cat) | Platform for turning websites into native iOS and Android apps | --- # Capacitor Source: https://www.revenuecat.com/docs/getting-started/installation/capacitor Markdown: https://www.revenuecat.com/docs/getting-started/installation/capacitor.md ## What is RevenueCat? RevenueCat provides a backend and a wrapper around StoreKit and Google Play Billing to make implementing in-app purchases and subscriptions easy. With our SDK, you can build and manage your app business on any platform without having to maintain IAP infrastructure. You can read more about [how RevenueCat fits into your app](https://www.revenuecat.com/blog/growth/where-does-revenuecat-fit-in-your-app/) or you can [sign up free](https://app.revenuecat.com/signup) to start building. ## Installation [![Release](https://img.shields.io/github/release/RevenueCat/purchases-capacitor.svg?filter=!*beta*\&style=flat)](https://github.com/RevenueCat/purchases-capacitor/releases) ```shell npm install @revenuecat/purchases-capacitor npx cap sync ``` ## Additional Android Setup ### Set the correct launchMode Depending on your user's payment method, they may be asked by Google Play to verify their purchase in their (banking) app. This means they will have to background your app and go to another app to verify the purchase. If your Activity's `launchMode` is set to anything other than `standard` or `singleTop`, backgrounding your app can cause the purchase to get cancelled. To avoid this, set the `launchMode` of your Activity to `standard` or `singleTop` in your Android app's `AndroidManifest.xml` file: ```xml ``` You can find Android's documentation on the various `launchMode` options [here](https://developer.android.com/guide/topics/manifest/activity-element#lmode). ## Additional iOS Setup :::info\[Enable In-App Purchase capability for iOS projects in Xcode] Don't forget to enable the In-App Purchase capability for your iOS project under `Project Target -> Capabilities -> In-App Purchase` ::: ### Set Swift Language Version You have to make sure that the `SWIFT_LANGUAGE_VERSION` is set if it's not already. `purchases-capacitor` needs Swift >= 5.0. You can either set it in the project yourself, or use an external plugin. In order to set it yourself: 1. In Xcode, in project manager, select your app target. 2. Open the `Build Settings` tab 3. Look for the `Swift Language Version` setting. 4. Set it to 5.0. ## Import Purchases ### TypeScript The types are shipped inside the npm package. You can import them like this: ```ts import { Purchases, PurchasesOfferings, // Types for TypeScript } from '@revenuecat/purchases-capacitor'; ``` ### Angular Wait for the Platform to be ready, then configure the plugin in your `src/app/app.component.ts`: ```jsx import { Platform } from "@ionic/angular"; // TS typings for the plugin import { Purchases, LOG_LEVEL } from '@revenuecat/purchases-capacitor'; constructor(platform: Platform) { platform.ready().then(async () => { await Purchases.setLogLevel({ level: LOG_LEVEL.DEBUG }); // Enable to get debug logs await Purchases.configure({ apiKey: "my_api_key", appUserID: "my_app_user_id" // Optional }); }); } ``` ### React Import the plugin object then use its static methods: ```jsx import { Purchases, LOG_LEVEL } from '@revenuecat/purchases-capacitor'; const Tab1: React.FC = () => { useEffect(() => { (async function () { await Purchases.setLogLevel({ level: LOG_LEVEL.DEBUG }); // Enable to get debug logs await Purchases.configure({ apiKey: "my_api_key", appUserID: "my_app_user_id" // Optional }); })(); }, []); return ( My App Subscribe now ); }; ``` ### Vue.js :::warning\[Important note if using Vue.js reactivity wrappers] If using Vue.js and its Reactivity API wrappers like [reactive](https://vuejs.org/api/reactivity-core.html#reactive) or [readonly](https://vuejs.org/api/reactivity-core.html#readonly), make sure you pass the raw objects (rather than `Proxy` objects) to the Capacitor plugin methods. You can use the [toRaw](https://vuejs.org/api/reactivity-advanced.html#toraw) method to convert to the raw object. ::: Import the plugin object then use its static methods: ```jsx import {LOG_LEVEL, Purchases} from "@revenuecat/purchases-capacitor"; const app = createApp(App) .use(IonicVue) .use(router); const configure = async () => { await Purchases.setLogLevel({ level: LOG_LEVEL.DEBUG }); // Enable to get debug logs await Purchases.configure({ apiKey: "my_api_key", appUserID: "my_app_user_id" // Optional }); }; router.isReady().then(() => { app.mount('#app'); configure().then(() => { "RevenueCat SDK configured!" }); }); ``` ## Next Steps - Now that you've installed the Purchases SDK in your Capacitor app, get started by [configuring an instance of Purchases →](https://www.revenuecat.com/docs/getting-started/quickstart#3-using-revenuecats-purchases-sdk) --- # Cordova Source: https://www.revenuecat.com/docs/getting-started/installation/cordova Markdown: https://www.revenuecat.com/docs/getting-started/installation/cordova.md ## What is RevenueCat? RevenueCat provides a backend and a wrapper around StoreKit and Google Play Billing to make implementing in-app purchases and subscriptions easy. With our SDK, you can build and manage your app business on any platform without having to maintain IAP infrastructure. You can read more about [how RevenueCat fits into your app](https://www.revenuecat.com/blog/growth/where-does-revenuecat-fit-in-your-app/) or you can [sign up free](https://app.revenuecat.com/signup) to start building. :::danger\[The Cordova SDK is deprecated] The Cordova SDK is now deprecated. We suggest using our [Capacitor SDK](https://www.revenuecat.com/docs/getting-started/installation/capacitor) instead. The Cordova SDK will receive maintenance updates, but new RevenueCat features and new major versions will not be made available. Billing Client v7 will be the latest version this SDK will ever support (it won't be updated to v8), which means that Google will not allow updates to your app after August 31st, 2026 [Read more about Google's Billing Client deprecation schedule](https://developer.android.com/google/play/billing/deprecation-faq) ::: ## Installation ```shell cordova plugin add cordova-plugin-purchases --save ``` ## Additional Android Setup ### Set the correct launchMode Depending on your user's payment method, they may be asked by Google Play to verify their purchase in their (banking) app. This means they will have to background your app and go to another app to verify the purchase. If your Activity's `launchMode` is set to anything other than `standard` or `singleTop`, backgrounding your app can cause the purchase to get cancelled. To avoid this, set the `launchMode` of your Activity to `standard` or `singleTop` in your Android app's `AndroidManifest.xml` file: ```xml ``` You can find Android's documentation on the various `launchMode` options [here](https://developer.android.com/guide/topics/manifest/activity-element#lmode). ## Additional iOS Setup :::info\[Enable In-App Purchase capability for iOS projects in Xcode] Don't forget to enable the In-App Purchase capability for your iOS project under `Project Target -> Capabilities -> In-App Purchase` ::: ### Add Strip Frameworks Phase if using cordova-plugin-purchases 1.1.0 or lower The App Store, in its infinite wisdom, still rejects fat frameworks, so we need to strip our framework before it is deployed. To do this, add the following script phase to your build. 1. In Xcode, in project manager, select your app target. 2. Open the `Build Phases` tab 3. Add a new `Run Script`, name it `Strip Frameworks` 4. Add the following command `"${PROJECT_DIR}/../../node_modules/cordova-plugin-purchases/src/ios/strip-frameworks.sh"` (quotes included) ![](https://www.revenuecat.com/docs_images/sdk/cordova/strip-frameworks.gif) ### Set `SWIFT_LANGUAGE_VERSION` You have to make sure that the `SWIFT_LANGUAGE_VERSION` is set if it's not already. `cordova-plugin-purchases` needs Swift >= 5.0. You can either set it in the project yourself, or use an external plugin like https://www.npmjs.com/package/cordova-plugin-add-swift-support. In order to set it yourself: 1. In Xcode, in project manager, select your app target. 2. Open the `Build Settings` tab 3. Look for the `Swift Language Version` setting. 4. Set it to 5.0. ## Import Purchases ## TypeScript The types are shipped inside the npm package. You can import them like this: ```jsx import Purchases, { PurchasesOfferings, // Types for TypeScript } from 'cordova-plugin-purchases/www/plugin'; ``` ## Angular Wait for the Platform to be ready, then configure the plugin in your `src/app/app.component.ts`: ```jsx import { Platform } from "@ionic/angular"; // TS typings for the plugin import Purchases, { LOG_LEVEL } from 'cordova-plugin-purchases/www/plugin'; constructor(platform: Platform) { platform.ready().then(() => { Purchases.setLogLevel(LOG_LEVEL.DEBUG); // Enable to get debug logs Purchases.configureWith({ apiKey: "my_api_key", appUserID: "my_app_user_id" }); }); } ``` ## React Import the plugin object then use its static methods: ```jsx import Purchases, { LOG_LEVEL } from 'cordova-plugin-purchases/www/plugin'; const Tab1: React.FC = () => { Purchases.setLogLevel(LOG_LEVEL.DEBUG); // Enable to get debug logs Purchases.purchases.configureWith({ apiKey: "my_api_key", appUserID: "my_app_user_id" }); return ( My App Subscribe now ); }; ``` ## Next Steps - Now that you've installed the Purchases SDK in your Cordova app, get started by [initializing an instance of Purchases →](https://www.revenuecat.com/docs/getting-started/quickstart#3-using-revenuecats-purchases-sdk) --- # Expo Source: https://www.revenuecat.com/docs/getting-started/installation/expo Markdown: https://www.revenuecat.com/docs/getting-started/installation/expo.md ## What is RevenueCat? RevenueCat provides a backend and SDKs that wrap StoreKit, Google Play Billing, and [RevenueCat Billing](https://www.revenuecat.com/docs/web/overview) to make implementing in-app and web purchases and subscriptions easy. With our SDK, you can build and manage your app business on any platform without having to maintain IAP infrastructure. You can read more about [how RevenueCat fits into your app](https://www.revenuecat.com/blog/where-does-revenuecat-fit-in-your-app) or you can [sign up free](https://app.revenuecat.com/signup) to start building. ## Introduction Expo is a framework for building React Native apps. It's a popular choice for rapidly iterating on your app, while letting Expo take care of all the platform-specific code. To use and test RevenueCat with Expo, you'll need to create an Expo development build. Follow the instructions below and learn more about Expo development builds [here](https://docs.expo.dev/develop/development-builds/introduction/). :::info This guide is specific to Expo, but you may also find our [React Native guide](https://www.revenuecat.com/docs/getting-started/installation/reactnative) useful. ::: ## Create an Expo development build ### Set up the Expo project You can use an existing Expo project, or [create a new one](https://docs.expo.dev/get-started/create-a-project/). This command will create a default project with example code, and install the Expo CLI as a dependency: ```sh npx create-expo-app@latest ``` Change to the project directory: ```sh cd ``` Install the [expo-dev-client](https://docs.expo.dev/versions/latest/sdk/dev-client/): ```sh npx expo install expo-dev-client ``` ### Install RevenueCat's SDKs Install RevenueCat's `react-native-purchases` for core functionality and `react-native-purchases-ui` for UI components like [Paywalls](https://www.revenuecat.com/docs/tools/paywalls), [Customer Center](https://www.revenuecat.com/docs/tools/customer-center), and more. Either run: ```sh npx expo install react-native-purchases react-native-purchases-ui ``` or update your package.json with the [latest package versions](https://github.com/RevenueCat/react-native-purchases/releases): ```json { "dependencies": { "react-native-purchases": "latest_version", "react-native-purchases-ui": "latest_version" } } ``` :::info After installing RevenueCat's SDKs, you **must** run the full build process as described below in the `Testing your app` section to ensure all dependencies are installed. Hot reloading without building will result in errors, such as: ``` Invariant Violation: `new NativeEventEmitter()` requires a non-null argument. ``` ::: ### RevenueCat Dashboard Configuration #### Configure a new project RevenueCat projects are top-level containers for your apps, products, entitlements, paywalls, and more. If you don't already have a RevenueCat project for your app, [create one here](https://www.revenuecat.com/docs/projects/overview). #### Connect to a Store (Apple, Google, Web, etc.) Depending on which platform you're building for, you'll need to connect your RevenueCat project to one, or multiple, stores. Set up your project's supported stores [here](https://www.revenuecat.com/docs/projects/connect-a-store). #### Add Products For each store you're supporting, you'll need to add the products you plan on offering to your customers. Set up your products for each store [here](https://www.revenuecat.com/offerings/products/setup-index). #### Create an Entitlement An entitlement represents a level of access, features, or content that a customer is "entitled" to. When customers purchase a product, they're granted an entitlement. Create an entitlement [here](https://www.revenuecat.com/docs/getting-started/entitlements). Then, [attach your products](https://www.revenuecat.com/docs/getting-started/entitlements#attaching-products-to-entitlements) to your new entitlement. #### Create an Offering An offering is a collection of products that are "offered" to your customers on your paywall. Create an offering for your products [here](https://www.revenuecat.com/docs/offerings/overview). #### Configure a Paywall A paywall is where your customers can purchase your products. RevenueCat's Paywalls allow you to remotely build and configure your paywall without any code changes or app updates. Create a paywall [here](https://www.revenuecat.com/docs/tools/paywalls). ### RevenueCat SDK Configuration #### Initialize the SDK Once you've installed the RevenueCat SDK, you'll need to configure it. Add the following code to the entry point of your app and be sure to replace `` with your [project's API keys](https://www.revenuecat.com/docs/projects/authentication). More information about configuring the SDK can be found [here](https://www.revenuecat.com/docs/getting-started/configuring-sdk). ```ts import { Platform } from 'react-native'; import { useEffect } from 'react'; import Purchases, { LOG_LEVEL } from 'react-native-purchases'; import { GALAXY_BILLING_MODE } from 'react-native-purchases-store-galaxy'; //... export default function App() { useEffect(() => { Purchases.setLogLevel(LOG_LEVEL.VERBOSE); if (Platform.OS === 'ios') { Purchases.configure({ apiKey: }); } else if (Platform.OS === 'android') { Purchases.configure({ apiKey: }); // OR: if building for Amazon, be sure to follow the installation instructions then: Purchases.configure({ apiKey: , useAmazon: true }); // OR: if building for Galaxy Store, install react-native-purchases-store-galaxy, then: Purchases.configure({ apiKey: , store: 'GALAXY', galaxyBillingMode: GALAXY_BILLING_MODE.TEST, }); } }, []); } ``` #### Identify a user and check subscription status RevenueCat is the single source of truth for your customer's subscription status across all platforms. Learn more about the different ways to identify your customers to RevenueCat [here](https://www.revenuecat.com/docs/customers/identifying-customers). Then, [check the customer's subscription status](https://www.revenuecat.com/docs/customers/customer-info) by fetching the [CustomerInfo object](https://www.revenuecat.com/docs/customers/customer-info#reference): ```ts try { const customerInfo = await Purchases.getCustomerInfo(); // access latest customerInfo } catch (e) { // Error fetching customer info } ``` and inspecting the `entitlements` object to see if the customer is subscribed to your entitlement: ```ts if(typeof customerInfo.entitlements.active[] !== "undefined") { // Grant user "pro" access } ``` #### Present a paywall If the customer is not subscribed to your entitlement, you can present a paywall to them where they can purchase your products. There are several ways to present a paywall in Expo, each with different use cases, so please review the [React Native Paywalls documentation](https://www.revenuecat.com/docs/tools/paywalls/displaying-paywalls#react-native). ### Testing your app To test, we'll use EAS to build the app for the simulator. You'll need to sign up at [expo.dev](https://expo.dev) and use the account below. For more information about EAS, see the [EAS docs](https://docs.expo.dev/tutorial/eas/introduction/). :::info You can also follow these instructions on Expo's docs: https://docs.expo.dev/tutorial/eas/configure-development-build/#initialize-a-development-build ::: Get started by installing the EAS-CLI: ```sh npm install -g eas-cli ``` Then login to EAS: ```sh eas login ``` After logging in, initialize the EAS configuration: ```sh eas init ``` Then run the following command, which will prompt you to select the platforms you'd like to configure for EAS Build. ``` eas build:configure ``` #### Testing on iOS simulator Next, you'll need to update `eas.json` with the simulator build profile [as described here](https://docs.expo.dev/tutorial/eas/ios-development-build-for-simulators/). Your `eas.json` file might look like this: ```json { "cli": { "version": ">= 7.3.0" }, "build": { "development": { "developmentClient": true, "distribution": "internal" }, "preview": { "distribution": "internal" }, "production": {}, "ios-simulator": { "extends": "development", "ios": { "simulator": true } } }, "submit": { "production": {} } } ``` Next, build the app for the simulator: ``` eas build --platform ios --profile ios-simulator ``` Building creates a container app with your installed dependencies. Once the build completes and you run it on the device (or simulator, in this case), the app will hot reload with your local changes during development. Enter your app's bundle ID, matching your RevenueCat config and App Store Connect. Once the build completes, Expo will ask if you want to open the app in the simulator. Choose yes, and it'll launch the simulator with your app. After your app is running, you'll need to start the Expo server: ```sh npx expo start ``` Finally, choose the local development server in the iOS simulator. #### Testing on Android device or emulator To test on an Android device, you'll need to build the app for a physical device or Android emulator as described [here](https://docs.expo.dev/tutorial/eas/android-development-build/). Please ensure that `developmentClient` in your `eas.json` file is set to true under the `build.development` profile. Then, build the app: ```sh eas build --platform android --profile development ``` Enter your app's application ID matches your RevenueCat config and Google Play Console. Choose "Yes" when asked if you want to create a new Android Keystore. Once the build completes, you can run the application on the device or emulator. To run the app on an [Android device](https://docs.expo.dev/tutorial/eas/android-development-build/#android-device), install [Expo Orbit](https://expo.dev/orbit), connect your device to your computer, and [select your device](https://docs.expo.dev/tutorial/eas/android-development-build/#android-device:~:text=and%20Install%20button.-,Expo%20Orbit,-allows%20for%20seamless) from the Orbit menu. Alternatively, use the [provided QR code method](https://docs.expo.dev/tutorial/eas/android-development-build/#android-device:~:text=and%20Install%20button.-,Expo%20Orbit,-allows%20for%20seamless). To run the app on an [Android emulator](https://docs.expo.dev/tutorial/eas/android-development-build/#android-emulator), choose "Yes" in the terminal after the build completes. After the app is running, you'll need to start the Expo server: ```sh npx expo start ``` ## Expo Go [Expo Go](https://expo.dev/go) is a sandbox that allows you to rapidly prototype your app. While it doesn’t support running custom native code—such as the native modules required for in-app purchases—`react-native-purchases` includes a built-in **Preview API Mode** specifically for Expo Go. When your app runs inside Expo Go, `react-native-purchases` automatically detects the environment and replaces native calls with JavaScript-level mock APIs. This allows your app to load and execute all subscription-related logic without errors, even though real purchases will not function in this mode. This means you can still preview subscription UIs, test integration flows, and continue development without needing to build a custom development client immediately. However, to fully test in-app purchases and access real RevenueCat functionality, you must use a [development build](https://www.revenuecat.com/docs/getting-started/installation/expo#create-an-expo-development-build). ## React Native Web Configuration RevenueCat's React Native SDK supports web platforms, allowing you to manage subscriptions across React Native web, mobile, and desktop apps using the same SDK. This also applies to Expo projects that target web platforms. ### Web Product Configuration To enable web purchases in your Expo app, you'll need to configure products using a [RevenueCat Billing app](https://www.revenuecat.com/docs/web/overview). 1. **Create a RevenueCat Billing App** in your RevenueCat project dashboard 2. **Configure your products** for web purchases For detailed instructions on setting up web products and configuring RevenueCat Billing, see the [RevenueCat Billing Overview](https://www.revenuecat.com/docs/web/overview). :::info\[RevenueCat Billing vs In-App Purchases] RevenueCat Billing is RevenueCat's billing engine for web purchases, which uses Stripe as the payment processor. This is separate from iOS/Android in-app purchases but integrates with the same RevenueCat entitlements system, allowing unified subscription management across platforms. ::: ### Current Limitations When using the React Native SDK on web with Expo, keep in mind the following: - **RevenueCat Billing Required**: Web purchases require RevenueCat Billing setup. Native iOS/Android in-app purchases cannot be processed through the web platform. - **Payment Processing**: RevenueCat Billing purchases use Stripe as the payment processor through RevenueCat Billing. - **Customer Portal**: Users can manage their web subscriptions through the RevenueCat-provided customer portal. - **Platform Separation**: Web products must be configured separately from iOS/Android products in the RevenueCat dashboard, though entitlements can be shared across platforms. - **User Identity**: For unified cross-platform subscriptions, ensure you're using the same `appUserID` across web and mobile platforms. - **Unsupported operations**: There are some unsupported operations. Mainly operations `getProducts`, `purchaseProduct` or `restorePurchases` won't work on web environments. --- # Flutter Source: https://www.revenuecat.com/docs/getting-started/installation/flutter Markdown: https://www.revenuecat.com/docs/getting-started/installation/flutter.md ## What is RevenueCat? RevenueCat provides a backend and SDKs that wrap StoreKit, Google Play Billing, and [RevenueCat Billing](https://www.revenuecat.com/docs/web/overview) to make implementing in-app and web purchases and subscriptions easy. With our SDK, you can build and manage your app business on any platform without having to maintain IAP infrastructure. You can read more about [how RevenueCat fits into your app](https://www.revenuecat.com/blog/where-does-revenuecat-fit-in-your-app) or you can [sign up free](https://app.revenuecat.com/signup) to start building. ## Requirements Xcode 13.3.1+ Minimum target: iOS 11.0+ ## Installation [![Release](https://img.shields.io/github/release/RevenueCat/purchases-flutter.svg?filter=!*beta*\&style=flat)](https://github.com/RevenueCat/purchases-flutter/releases) To use this plugin, add `purchases_flutter` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/) (and run an implicit dart pub get): ```yaml dependencies: purchases_flutter: // or 9.0.0-beta.3 for Flutter Web ``` Alternatively run this command: ``` $ flutter pub add purchases_flutter ``` ### iOS Deployment Target RevenueCat is compatible with iOS 11.0 or higher. Flutter does not automatically set the iOS deployment target for your project. You need to make sure that the deployment target is set to 11.0 or higher. To do that, simply edit `ios/Podfile` and add the following line if it's not already there: ``` platform :ios, '11.0' ``` Set it to 11.0 or a higher version for RevenueCat to work. ### iOS Swift Version RevenueCat requires Swift >= 5.0 to work. If the `Podfile` in your project's `ios` folder specifies a Swift version, make sure that it's at least 5.0, otherwise you may run into build issues. ### Set the correct launchMode for Android Depending on your user's payment method, they may be asked by Google Play to verify their purchase in their (banking) app. This means they will have to background your app and go to another app to verify the purchase. If your Activity's `launchMode` is set to anything other than `standard` or `singleTop`, backgrounding your app can cause the purchase to get cancelled. To avoid this, set the `launchMode` of your Activity to `standard` or `singleTop` in your Android app's `android/app/src/main/AndroidManifest.xml` file: ```xml ``` You can find Android's documentation on the various `launchMode` options [here](https://developer.android.com/guide/topics/manifest/activity-element#lmode). ### Optional: Change MainActivity subclass If you plan to use [RevenueCat Paywalls](https://www.revenuecat.com/docs/tools/paywalls), your `MainActivity` needs to subclass `FlutterFragmentActivity` instead of `FlutterActivity`. ```MainActivity.kt package com.your.package.name import io.flutter.embedding.android.FlutterFragmentActivity class MainActivity: FlutterFragmentActivity() ``` ## Import Purchases You should now be able to import `purchases_flutter`. ```dart import 'package:purchases_flutter/purchases_flutter.dart'; ``` :::info\[Enable In-App Purchase capability for iOS projects in Xcode] Don't forget to enable the In-App Purchase capability for your iOS project under `Project Target -> Capabilities -> In-App Purchase` ::: :::info\[Include BILLING permission for Android projects] Don't forget to include the `BILLING` permission in your AndroidManifest.xml file ::: ```xml ``` :::warning If you're using other plugins like [mobx](https://pub.dev/packages/flutter_mobx), you may run into conflicts with types from other plugins having the same name as those defined in `purchases_flutter`.
If this happens, you can resolve the ambiguity in the types by adding an import alias, for example: ```dart import 'package:purchases_flutter/purchases_flutter.dart' as purchases; ``` After that, you can reference the types from `purchases_flutter` as `purchases.Foo`, like `purchases.CustomerInfo`. ::: ## Flutter Web Configuration RevenueCat's Flutter SDK supports web platforms, allowing you to manage subscriptions across Flutter web, mobile, and desktop apps using the same SDK. ### Web Product Configuration On web, the Flutter SDK supports the same billing engines as the [Web SDK](https://www.revenuecat.com/docs/web/web-billing/web-sdk): RevenueCat Billing, Stripe Billing, and Paddle Billing. Your billing engine determines where products, taxes, emails, and subscription management are configured. To enable web purchases in your Flutter app, connect a billing engine and create a web config for it: - **RevenueCat Billing**: Create a RevenueCat Billing config in your RevenueCat project dashboard, choosing your connected Stripe account as the payment gateway, and [configure your products](https://www.revenuecat.com/docs/web/web-billing/configuring-overview). See the [RevenueCat Billing Overview](https://www.revenuecat.com/docs/web/overview) for details. - **Stripe Billing**: [Connect your Stripe account and create a Stripe web config](https://www.revenuecat.com/docs/web/integrations/stripe). Products and subscriptions are managed in Stripe. To have Stripe act as the merchant of record, enable [Stripe Managed Payments](https://www.revenuecat.com/docs/web/integrations/stripe/stripe-managed-payments). - **Paddle Billing**: [Connect your Paddle account and create a Paddle web config](https://www.revenuecat.com/docs/web/integrations/paddle). Products and subscriptions are managed in Paddle, and Paddle acts as the merchant of record. Then configure the SDK in your Flutter app using the public API key of the web config you created. :::info\[Web purchases vs In-App Purchases] Web purchases are separate from iOS/Android in-app purchases, but integrate with the same RevenueCat entitlements system, allowing unified subscription management across platforms. ::: ### Current Limitations When using the Flutter SDK on web, keep in mind the following: - **Billing Engine Required**: Web purchases require a RevenueCat Billing, Stripe Billing, or Paddle Billing setup. Native iOS/Android in-app purchases cannot be processed through the web platform. - **Payment Processing**: RevenueCat Billing uses Stripe as the payment processor. With Stripe Billing, payments go through Stripe Checkout, and with Paddle Billing through Paddle's checkout. - **Subscription Management**: RevenueCat Billing subscriptions can be managed through the RevenueCat-provided [Customer Portal](https://www.revenuecat.com/docs/web/web-billing/customer-portal). Stripe Billing and Paddle Billing subscriptions are managed in Stripe and Paddle respectively. - **Platform Separation**: Web products must be configured separately from iOS/Android products, though entitlements can be shared across platforms. - **User Identity**: For unified cross-platform subscriptions, ensure you're using the same `appUserID` across web and mobile platforms. - **RevenueCat Paywalls**: Presenting [RevenueCat Paywalls](https://www.revenuecat.com/docs/tools/paywalls) (`presentPaywall`) is not yet supported on web. - **Unsupported operations**: There are some unsupported operations. Mainly operations `getProducts`, `purchaseProduct` or `restorePurchases` won't work on web environments. ## Next Steps - Now that you've installed the Purchases SDK in Flutter, get started by [configuring an instance of Purchases →](https://www.revenuecat.com/docs/getting-started/quickstart#3-using-revenuecats-purchases-sdk) --- # iOS & Apple Platforms Source: https://www.revenuecat.com/docs/getting-started/installation/ios Markdown: https://www.revenuecat.com/docs/getting-started/installation/ios.md ## What is RevenueCat? RevenueCat provides a backend and a wrapper around StoreKit and Google Play Billing to make implementing in-app purchases and subscriptions easy. With our SDK, you can build and manage your app business on any platform without having to maintain IAP infrastructure. You can read more about [how RevenueCat fits into your app](https://www.revenuecat.com/blog/growth/where-does-revenuecat-fit-in-your-app/) or you can [sign up free](https://app.revenuecat.com/signup) to start building. ## Installation [![Release](https://img.shields.io/github/release/RevenueCat/purchases-ios.svg?filter=!*beta*\&style=flat)](https://github.com/RevenueCat/purchases-ios/releases) RevenueCat for iOS can be installed either via [CocoaPods](https://www.revenuecat.com/docs/getting-started/installation/ios#section-install-via-cocoapods), [Carthage](ios#section-install-via-carthage), or [Swift Package Manager](https://www.revenuecat.com/docs/getting-started/installation/ios#section-install-via-swift-package-manager). :::info Already have 4.x installed? View our [migration guide to 5.x →](https://www.revenuecat.com/docs/sdk-guides/ios-native-4x-to-5x-migration) ::: **Video:** [Set up RevenueCat Purchases SDK for iOS](https://www.youtube.com/watch?v=QS7BTorY4-U) ### Install via Swift Package Manager You can use Swift Package Manager to add RevenueCat to your Xcode project. :::tip\[Speed up the Swift Package Manager installation] Use a mirror of the main repository by selecting `File » Add Packages Dependencies...` and entering the repository URL (`https://github.com/RevenueCat/purchases-ios-spm.git`) into the search bar (top right). This will integrate far more quickly than using the main repository directly. ::: Set the Dependency Rule to `Up to next major`, and the version number to `5.0.0 < 6.0.0`. When "Choose Package Products for purchases-ios" appears, only select `RevenueCat` and `RevenueCatUI` and click "Add Package". ![SPM integration](https://www.revenuecat.com/docs_images/sdk/spm-integration.png) Click "Add Package". The library should have been added to the Package Dependencies section and you should now be able to `import RevenueCat` into your source files. ### Install via CocoaPods To always use the latest release, add the following to your Podfile: ```ruby pod 'RevenueCat' ``` Alternatively, pin to a specific minor version: ```ruby pod 'RevenueCat', '~> 5.2' ``` And then run: ```ruby pod install ``` This will add `RevenueCat.framework` to your workspace. ### Install via Carthage To always use the latest release, add the following to your Cartfile: ```text github "revenuecat/purchases-ios" ``` Alternatively, pin to a specific minor version: ```text github "revenuecat/purchases-ios" ~> 5.2.3 ``` #### Carthage with XCFrameworks If you're using Carthage version >= 0.37, you can use RevenueCat as an XCFramework instead of a Universal Framework. This makes setup easier, since you don't have to set up build phases at all. To use XCFrameworks with Carthage, you need to pass in `--use-xcframeworks`. ```shell carthage update --use-xcframeworks ``` More information on using XCFrameworks with Carthage is available at https://github.com/carthage/Carthage/#building-platform-independent-xcframeworks-xcode-12-and-above :::warning Under certain configurations, when debugging your app, using `po` to print objects to the console might result in an error `\"Couldn't IRGen Expression\"`. If you run into this, one workaround is to add a single, empty Objective-C file to your project, and create a bridging header. There's more information on this issue [here](https://steipete.me/posts/2020/couldnt-irgen-expression/) ::: #### Carthage with regular frameworks Run: ```text carthage update ``` ## Import the SDK :::info\[Objective-C Only Projects] You may need to add an empty Swift file and a bridging header to your project before compiling. ::: You should now be able to `import RevenueCat`. **Swift** ```swift import RevenueCat ``` **Objective-C** ```objectivec @import RevenueCat; // or #import "Purchases.h" ``` :::info\[Enable In-App Purchase capability for your project] Don't forget to enable the In-App Purchase capability for your project under `Project Target -> Capabilities -> In-App Purchase` ::: ## Next Steps - Now that you've installed the SDK in your iOS app, get started by [configuring an instance of Purchases →](https://www.revenuecat.com/docs/getting-started/quickstart#3-using-revenuecats-purchases-sdk) --- # Kotlin Multiplatform Source: https://www.revenuecat.com/docs/getting-started/installation/kotlin-multiplatform Markdown: https://www.revenuecat.com/docs/getting-started/installation/kotlin-multiplatform.md ## What is RevenueCat? RevenueCat provides a backend and a wrapper around StoreKit and Google Play Billing to make implementing in-app purchases and subscriptions easy. With our SDK, you can build and manage your app business on any platform without having to maintain IAP infrastructure. You can read more about [how RevenueCat fits into your app](https://www.revenuecat.com/blog/growth/where-does-revenuecat-fit-in-your-app/) or you can [sign up free](https://app.revenuecat.com/signup) to start building. ## Requirements Android 5.0+ (API 21+)\ iOS 13.0+ ## Installation ### Adding the dependency Purchases for Kotlin Multiplatform (Google Play and iOS App Store) is available on Maven Central and can be included via Gradle. [![Release](https://img.shields.io/github/release/RevenueCat/purchases-kmp.svg?filter=!*beta*\&style=flat)](https://github.com/RevenueCat/purchases-kmp/releases) Add the following coordinates to your `libs.versions.toml`. ``` [versions] purchases-kmp = "" [libraries] # Required purchases-core = { module = "com.revenuecat.purchases:purchases-kmp-core", version.ref = "purchases-kmp" } # Optional: adds suspending functions that return Arrow's Either to indicate success / failure. purchases-either = { module = "com.revenuecat.purchases:purchases-kmp-either", version.ref = "purchases-kmp" } # Optional: adds suspending functions that return kotlin.Result to indicate success / failure. purchases-result = { module = "com.revenuecat.purchases:purchases-kmp-result", version.ref = "purchases-kmp" } ``` You can now add the dependency to your `commonMain` source set in your module's `build.gradle.kts`. ```kotlin kotlin { // ... sourceSets { // ... commonMain.dependencies { // Add the purchases-kmp dependencies. implementation(libs.purchases.core) implementation(libs.purchases.either) // Optional implementation(libs.purchases.result) // Optional } } } ``` ### Opt in to ExperimentalForeignApi Since the SDK uses generated Kotlin bindings for native code on iOS, you will need to opt in to `ExperimentalForeignApi` in your iOS source sets. To do so, add the following to your module's `build.gradle.kts`. ```kotlin kotlin { // ... sourceSets { // ... named { it.lowercase().startsWith("ios") }.configureEach { languageSettings { optIn("kotlinx.cinterop.ExperimentalForeignApi") } } } } ``` ### Set the correct launchMode for Android Depending on your user's payment method, they may be asked by Google Play to verify their purchase in their (banking) app. This means they will have to background your app and go to another app to verify the purchase. If your Activity's `launchMode` is set to anything other than `standard` or `singleTop`, backgrounding your app can cause the purchase to get cancelled. To avoid this, set the `launchMode` of your Activity to `standard` or `singleTop` in your Android app's `AndroidManifest.xml` file: ```xml ``` You can find Android's documentation on the various `launchMode` options [here](https://developer.android.com/guide/topics/manifest/activity-element#lmode). ### Build static binaries for iOS targets In some cases it may be required to configure your iOS targets to build static binaries, in order for your project to compile. Make sure `isStatic` is set to `true` in your iOS targets' binary output settings in your `build.gradle.kts`: ```kotlin kotlin { // ... listOf( iosX64(), iosArm64(), iosSimulatorArm64() ).forEach { iosTarget -> iosTarget.binaries.framework { baseName = "ComposeApp" isStatic = true } } } ``` ## Import Purchases You should now be able to import `Purchases`. ```kotlin import com.revenuecat.purchases.kmp.Purchases import com.revenuecat.purchases.kmp.models.CustomerInfo import com.revenuecat.purchases.kmp.models.EntitlementInfo import com.revenuecat.purchases.kmp.models.Offering import com.revenuecat.purchases.kmp.models.Period import com.revenuecat.purchases.kmp.models.Price import com.revenuecat.purchases.kmp.models.StoreProduct ``` :::warning On Android, Purchases uses AndroidX App Startup under the hood. Make sure you have not removed the `androidx.startup.InitializationProvider` completely in your manifest. If you need to remove specific initializers, such as `androidx.work.WorkManagerInitializer`, set `tools:node="merge"` on the provider, and `tools:node="remove"` on the meta-data of the initializer you want to remove. ```xml ``` ::: ## Next Steps - Now that you've installed the Purchases SDK in Kotlin Multiplatform, get started by [configuring an instance of Purchases →](https://www.revenuecat.com/docs/getting-started/quickstart#3-using-revenuecats-purchases-sdk) --- # React Native Source: https://www.revenuecat.com/docs/getting-started/installation/reactnative Markdown: https://www.revenuecat.com/docs/getting-started/installation/reactnative.md ## What is RevenueCat? RevenueCat provides a backend and SDKs that wrap StoreKit, Google Play Billing, the Amazon Appstore, the Samsung Galaxy Store, and [RevenueCat Billing](https://www.revenuecat.com/docs/web/overview) to make implementing in-app and web purchases and subscriptions easy. With our SDK, you can build and manage your app business on any platform without having to maintain IAP infrastructure. You can read more about [how RevenueCat fits into your app](https://www.revenuecat.com/blog/where-does-revenuecat-fit-in-your-app) or you can [sign up free](https://app.revenuecat.com/signup) to start building. ## Installation [![Release](https://img.shields.io/github/release/RevenueCat/react-native-purchases.svg?filter=!*beta*\&style=flat)](https://github.com/RevenueCat/react-native-purchases/releases) Make sure that the deployment target for iOS is at least 13.4 and Android is at least 6.0 (API 23) [as defined here](https://github.com/facebook/react-native#-requirements). ### React Native package Purchases for React Native can be installed either via npm or yarn. We recommend using the latest version of React Native, or making sure that the version is at least greater than 0.64. #### Option 1.1: Using auto-linking Recent versions of React Native will automatically link the SDK, so all that's needed is to install the library. **npm** ```shell npm install --save react-native-purchases ``` **yarn** ```shell yarn add react-native-purchases ``` #### Option 1.2: Manual linking **npm** ```shell npm install --save react-native-purchases ``` **yarn** ```shell yarn add react-native-purchases ``` After that, you should link the library to the native projects by doing: ```shell react-native link react-native-purchases ``` ### Using Expo Use Expo to rapidly iterate on your app by using JavaScript/TypeScript exclusively, while letting Expo take care of everything else. See [Using RevenueCat with Expo](https://www.revenuecat.com/docs/getting-started/installation/expo) to get started. ### Set the correct launchMode for Android Depending on your user's payment method, they may be asked by Google Play to verify their purchase in their (banking) app. This means they will have to background your app and go to another app to verify the purchase. If your Activity's `launchMode` is set to anything other than `standard` or `singleTop`, backgrounding your app can cause the purchase to get cancelled. To avoid this, set the `launchMode` of your Activity to `standard` or `singleTop` in your Android app's `AndroidManifest.xml` file, like so: ```xml ``` You can find Android's documentation on the various `launchMode` options [here](https://developer.android.com/guide/topics/manifest/activity-element#lmode). ### Amazon Appstore for Android To build a React Native Android app for the Amazon Appstore, configure the SDK with your Amazon Appstore API key and set `useAmazon` to `true`: ```jsx import Purchases from 'react-native-purchases'; Purchases.configure({ apiKey: , useAmazon: true, }); ``` Adding support for Amazon also requires adding a `.pem` public key to your project. You can configure this key by following Amazon's guide [here](https://developer.amazon.com/es/docs/in-app-purchasing/integrate-appstore-sdk.html#configure_key). Due to some limitations, RevenueCat will only validate purchases made in production or in Live App Testing and won't validate purchases made with the Amazon App Tester. ### Galaxy Store for Android Galaxy Store support is available in React Native SDK versions `10.3.0` and above. To build a React Native Android app for the Galaxy Store, install the Galaxy Store add-on package in addition to `react-native-purchases`. **npm** ```shell npm install --save react-native-purchases-store-galaxy ``` **yarn** ```shell yarn add react-native-purchases-store-galaxy ``` Configure the SDK with your Galaxy Store API key and set `store` to `GALAXY`: ```jsx import Purchases from 'react-native-purchases'; import { GALAXY_BILLING_MODE } from 'react-native-purchases-store-galaxy'; Purchases.configure({ apiKey: , store: 'GALAXY', // Optional. Defaults to PRODUCTION. galaxyBillingMode: GALAXY_BILLING_MODE.TEST, }); ``` `galaxyBillingMode` is optional and defaults to `GALAXY_BILLING_MODE.PRODUCTION`. Use `GALAXY_BILLING_MODE.TEST` for test purchases or `GALAXY_BILLING_MODE.ALWAYS_FAIL` to test failure scenarios. Only use production mode when submitting your app for beta or production distribution. Test purchases for the Galaxy Store can only be made on a physical Galaxy device signed in with a Samsung account. The Galaxy Store does not support making test purchases in emulators. Before testing purchases, make sure you have also [set up your Galaxy Store app](https://www.revenuecat.com/docs/platform-resources/galaxy-platform-resources/galaxy-setup-guide) and [create Galaxy Store products](https://www.revenuecat.com/docs/getting-started/entitlements/galaxy-products). ## Import Purchases You should now be able to import `Purchases`. ```jsx import Purchases from 'react-native-purchases'; ``` :::info\[Include BILLING permission for Android projects] Don't forget to include the `BILLING` permission in your AndroidManifest.xml file ::: ```xml ``` :::info\[Enable In-App Purchase capability for your iOS project] Don't forget to enable the In-App Purchase capability for your project under `Project Target -> Capabilities -> In-App Purchase` ::: ## Android Build Issues ### R8 Dependencies Conflict If you encounter build failures related to R8 (Android's code shrinker) when using `react-native-purchases-ui`, you may see errors like: ``` Execution failed for task ':app:mergeExtDexDevDebug'. > Could not resolve all files for configuration ':app:devDebugRuntimeClasspath'. ``` This issue occurs due to a bug in earlier versions of Android Gradle Plugin (AGP) that affects R8 dependency resolution. To fix this, add the following to your project-level `build.gradle` file (not `app/build.gradle`): ```gradle buildscript { repositories { mavenCentral() maven { url = uri("https://storage.googleapis.com/r8-releases/raw") } } dependencies { classpath("com.android.tools:r8:8.1.44") } } ``` This solution forces the use of a specific R8 version that resolves the dependency conflicts. For more details, see the [Google Issue Tracker](https://issuetracker.google.com/issues/342522142#comment8). ## React Native Web Configuration RevenueCat's React Native SDK supports web platforms, allowing you to manage subscriptions across React Native web, mobile, and desktop apps using the same SDK. ### Web Product Configuration To enable web purchases in your React Native app, you'll need to configure products using a [RevenueCat Billing app](https://www.revenuecat.com/docs/web/overview). 1. **Create a RevenueCat Billing App** in your RevenueCat project dashboard 2. **Configure your products** for web purchases For detailed instructions on setting up web products and configuring RevenueCat Billing, see the [RevenueCat Billing Overview](https://www.revenuecat.com/docs/web/overview). :::info\[RevenueCat Billing vs In-App Purchases] RevenueCat Billing is RevenueCat's billing engine for web purchases, which uses Stripe as the payment processor. This is separate from iOS/Android in-app purchases but integrates with the same RevenueCat entitlements system, allowing unified subscription management across platforms. ::: ### Current Limitations When using the React Native SDK on web, keep in mind the following: - **RevenueCat Billing Required**: Web purchases require RevenueCat Billing setup. Native iOS/Android in-app purchases cannot be processed through the web platform. - **Payment Processing**: RevenueCat Billing purchases use Stripe as the payment processor through RevenueCat Billing. - **Customer Portal**: Users can manage their web subscriptions through the RevenueCat-provided customer portal. - **Platform Separation**: Web products must be configured separately from iOS/Android products in the RevenueCat dashboard, though entitlements can be shared across platforms. - **User Identity**: For unified cross-platform subscriptions, ensure you're using the same `appUserID` across web and mobile platforms. - **Unsupported operations**: There are some unsupported operations. Mainly operations `getProducts`, `purchaseProduct` or `restorePurchases` won't work on web environments. ## Next Steps - Now that you've installed the Purchases SDK in your React Native app, get started by [initializing an instance of Purchases →](https://www.revenuecat.com/docs/getting-started/quickstart#3-using-revenuecats-purchases-sdk) --- # Roku Source: https://www.revenuecat.com/docs/getting-started/installation/roku Markdown: https://www.revenuecat.com/docs/getting-started/installation/roku.md ## General setup ### Prerequisites #### Setting up your Roku developer account Follow the [First Steps](https://developer.roku.com/en-gb/docs/developer-program/getting-started/first-steps.md) guide to create a Roku developer account, log in to your Roku device and enable developer mode on your Roku device. #### Setting up your Roku channel Once you have your developer account created, head to the [dashboard](https://developer.roku.com/dev/dashboard) 1. First, [create a new Channel](https://developer.roku.com/en-gb/docs/developer-program/publishing/channel-publishing-guide.md#create-a-channel). 2. If you want to use this channel for testing, make sure the channel is [enabled for billing testing](https://developer.roku.com/en-gb/docs/developer-program/roku-pay/testing/billing-testing.md). 3. Under "Monetization" -> "Test users", [add a test user](https://developer.roku.com/en-gb/docs/developer-program/roku-pay/quickstart/test-users.md) with the email associated to your Roku device. 4. Under "Monetization" -> "Product", follow the process to submit the tax documents, and after you're approved, you can [create in-channel products](https://www.revenuecat.com/docs/getting-started/entitlements/roku-products). ### App configuration 1. Make sure your project has been enabled to create Roku apps. If you're not sure, talk to your RevenueCat contact. 2. Open the RevenueCat dashboard, select your project, and go to **Apps** to add a **Roku Channel Store** app config. ![](https://www.revenuecat.com/docs_images/projects/add-app-platform.png) 3. Navigate to the [Roku Pay Web Services page](https://developer.roku.com/rpay-web-services) to copy your Roku Pay API key and paste this to the RevenueCat dashboard under 'Roku Pay API Key'. The Roku Pay API key must be set before being able to select 'SAVE CHANGES'. ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-api-key.png) ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-configuration.png) 4. Once the app is created, click on the "Roku Server to Server notifications settings", copy the "Roku Push Notification URL" which starts with `https://api.revenuecat.com/v1/incoming-webhooks/roku-pay-jwt-notification`. Navigate back to your [Roku Pay Web Services page](https://developer.roku.com/rpay-web-services) (where you found the Roku API key) and scroll down until you see a section called 'Push Notifications' and paste RevenueCat's Push notifications URL in the text box. ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-notification-url.png) ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-notifications.png) Remember to select 'SAVE CHANGES' 5. Back to the RevenueCat dashboard, click on "Public API Key" and copy over the value which should start with "roku\_XXXXXX". You will need it to configure the SDK later. ![](https://www.revenuecat.com/docs_images/platform-resources/roku/revenuecat-api-key.png) ### Multi-channel support The Roku channel ID is required for supporting multiple channels on a single Roku account. If your Roku account has more than one Roku Channel, you will need to enter your Channel ID for each Roku app on the RevenueCat dashboard. 1. Navigate to your [Roku Developer Dashboard](https://developer.roku.com/dev/dashboard). 2. On the left-hand side of the Developer Dashboard, you'll see the main navigation panel. Under 'Channel', navigate to where your channel is located (e.g: public or beta channels) and select the channel. 3. Find your Roku Channel ID and copy this value. ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-channel-id.png) 4. Back in the RevenueCat app settings page, enter your Roku Channel ID the 'Multi-channel support' expanded section. ![](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-multi-channel.png) [](https://www.revenuecat.com/docs_images/platform-resources/roku/roku-multi-channel.png) Remember to select 'SAVE CHANGES' ### Product configuration After you have configured the Roku Store app on RevenueCat, you should [create your in-channel products](https://www.revenuecat.com/docs/getting-started/entitlements/roku-products) and then follow RevenueCat's regular setup of [entitlements, products, and offerings](https://www.revenuecat.com/docs/getting-started/entitlements). ## Installing the SDK [![Release](https://img.shields.io/github/release/RevenueCat/purchases-roku.svg?filter=!*beta*\&style=flat)](https://github.com/RevenueCat/purchases-roku/releases) 1. Clone the repository: ```shell git clone https://github.com/RevenueCat/purchases-roku ``` 2. Copy the `components/purchases` folder into your app's `components` folder. 3. Copy the `source/Purchases.brs` file into your app's `source` folder. 4. Import the SDK in the .xml file of the component where you want to use it: ```xml