RevenueCat

Documentation

RevenueCat helps you build payments and subscriptions in your mobile apps - from simple in-app-purchases to complex multi-channel subscriptions. Follow our development quickstart to start building your integration.

Get Started

Quickstart

Get up and running with mobile subscriptions

This guide will walk you through a practical example to get you up and running with subscriptions and the Purchases SDK with only a few lines of code.

Register your app in RevenueCat

Navigate to the the RevenueCat dashboard and add a new app from the dropdown.

One app for all platforms

A single app in RevenueCat supports all platforms.

Setup your Entitlements

Entitlements are a simple way for you to organize your in-app products and configure them remotely. This simplifies the client side code and enables you to change products without an app update.

See Understanding Entitlements for how to configure and leverage entitlements.

Configure Products First

Before configuring entitlements, you should setup your in-app products in either App Store Connect or Play Developer Console.

A common and simple setup example is one entitlement with identifier pro, one offering monthly, with one product.

Install the Purchases SDK

Purchases is our SDK that correctly implements purchases and subscriptions across platforms while syncing tokens with the RevenueCat server.

Initialize Purchases

You should only initialize Purchases once (usually on app launch) and share the same instance throughout your app by setting the shared instance in the SDK - we will take care of making a singleton for you.

import Purchases

let purchases = RCPurchases.configure(withAPIKey: "my_api_key")
#import "RCPurchases.h"
...
RCPurchases *purchases = [RCPurchases configureWithAPIKey:@"my_app_api_key"];
Purchases purchases = new Purchases.Builder(this, 
                                            "my_app_api_key").build();
Purchases.setSharedInstance(purchases);
const purchaserInfoHandler = (purchaserInfo, error) => {
  if (purchaserInfo)
  {
    this.receivePurchaserInfo(purchaserInfo);
  }
}

Purchases.addPurchaseListener((productIdentifier, purchaserInfo, error) => {
  if (error) {
    console.warn(error);
  } else {
    this.setState({
      purchasedProduct: productIdentifier
    });
    this.receivePurchaserInfo(purchaserInfo);
  }
});
Purchases.addPurchaserInfoUpdateListener(purchaserInfoHandler);
Purchases.addRestoreTransactionsListener(purchaserInfoHandler);

Purchases.setup(REVENUECAT_API_KEY);
Purchases.sharedInstance = Purchases.Builder(this, "my_app_api_key").build()

You can get your API key from app settings in the dashboard.

Get Subscription Status

Purchases makes it easy to check what active subscriptions the current user has. This can be done two ways within the receivedUpdatedPurchaserInfo listener/delegate:

  1. Checking active Entitlements - this lets you see what entitlements (from RevenueCat dashboard) are active for the user.
  2. Checking the active subscriptions - this lets you see what product ids (from iTunes Connect or Play Store) are active for the user.
func purchases(_ purchases: RCPurchases, receivedUpdatedPurchaserInfo purchaserInfo: RCPurchaserInfo) {
		let entitlements = purchaserInfo.activeEntitlements
    let subscriptions = purchaserInfo.activeSubscriptions

    if entitlements.contains("my_entitlement_name") {
        // print("user is a pro")
    }

    if subscriptions.contains("my_subscription_id") {
        // print("user is a pro")
    }
}
override fun onReceiveUpdatedPurchaserInfo(purchaserInfo: PurchaserInfo) {
  val entitlements = purchaserInfo.activeEntitlements
  val subscriptions = purchaserInfo.activeSubscriptions
  if (entitlements.contains("my_entitlement_name")) {
    // print("user is a pro")
  }

  if (subscriptions.contains("my_subscription_id")) {
    // print("user is a pro")
  }
}
Purchases.addPurchaserInfoUpdateListener((purchaserInfo, error) => {
  if (purchaserInfo) {
    if (purchaserInfo.activeEntitlements.includes("my_entitlement_name")) {
      console.log("user is a pro")
    }
    if (purchaserInfo.activeSubscriptions.includes("my_subscription_id")) {
      console.log("user is a pro")
    }
  }
});

Displaying Available Products

Purchases will automatically fetch the latest active entitlements and get the product information from Apple or Google. This means when users launch your purchase screen, products will already be loaded.

func displayUpsellScreen() {
    purchases?.entitlements({ (ents) in
        let vc = UpsellController()
        vc.entitlements = ents
        presentViewController(vc, animated: true, completion: nil)
    })
}
[self.purchases entitlements:^(NSDictionary<NSString *, RCEntitlement *> *entitlements) {
  UpsellViewController *vc = [[UpsellViewController alloc] init];
  vc.entitlements = entitlements;
  [self presentViewController:vc animated:YES completion:nil];
}];
fun displayUpsellScreen() {
        purchases?.getEntitlements(object : Purchases.GetEntitlementsHandler {
          override fun onReceiveEntitlements(entitlementMap: Map<String, Entitlement>) {
            // Display entitlements
          }

          override fun onReceiveEntitlementsError(
            domain: Purchases.ErrorDomains,
            code: Int,
            message: String
          ) {
            // Display error
          }

        })
}
let entitlements = await Purchases.getEntitlements();
console.log(entitlements);

Since the SDK pre-fetches the available products, the completion block to get entitlements won't need to make a network request in most cases. This creates a seamless onboarding experience that results in less drop-off, happier users, and more revenue for you.

Make a Purchase

When it comes time to make a purchase, Purchases has a simple method, makePurchase. The code sample below shows the process of fetching entitlements (which should be pre-loaded and super fast) and purchasing the "monthly" product offering.

purchases.entitlements { entitlements in
    guard let pro = entitlements?["my_entitlement_name"] else { return }
    guard let monthly = pro.offerings["my_offering_name"] else { return }
    guard let product = monthly.activeProduct else { return }
    purchases.makePurchase(product)
}
 [purchases entitlements:^(NSDictionary<NSString *, RCEntitlement *> *entitlements) {
     SKProduct *product = entitlements[@"my_entitlement_name"].offerings[@"my_offering_name"].activeProduct;
     [purchases makePurchase:product];
 }];
purchases.getEntitlements(new Purchases.GetEntitlementsHandler() {
            @Override
            public void onReceiveEntitlements(Map<String, Entitlement> entitlementMap) {
                Entitlement pro = entitlementMap.get("my_entitlement_name");
                Offering monthly = pro.getOfferings().get("my_offering_name");
              	SkuDetails details = monthly.getSkuDetails();
              	purchases.makePurchase(activity, details.getSku(), details.getType());
            }
        });
let entitlements = await Purchases.getEntitlements()
Purchases.makePurchase(entitlements.pro.monthly.identifier)
purchases.getEntitlements(object : Purchases.GetEntitlementsHandler {

            override fun onReceiveEntitlements(entitlementMap: Map<String, Entitlement>) {
              Entitlement pro = entitlementMap.get("my_entitlement_name");
                Offering monthly = pro.getOfferings().get("my_offering_name");
              	SkuDetails details = monthly.getSkuDetails();
              	purchases.makePurchase(this, details.getSku(), details.getType());

                entitlementMap["pro"]
                    ?.offerings?.get("monthly")
                    ?.skuDetails?.let { purchases.makePurchase(activity, it.sku, it.type); }
				
            }
        })

makePurchase handles the underlying framework interaction and automatically validates purchases with Apple or Google through our secure servers. This helps reduce in-app-purchase fraud and decreases the complexity of your app. Receipt tokens are stored remotely and always kept up-to-date by RevenueCat.

Unlock Content

Once the purchase is made, verified, and stored, RevenueCat will send you the latest version of a purchaser's available entitlements - this is done via the Purchases listener/delegate. It is on you to unlock appropriate content or features in response to this.

func purchases(_ purchases: RCPurchases, completedTransaction transaction: SKPaymentTransaction, withUpdatedInfo purchaserInfo: RCPurchaserInfo) {
    if purchaserInfo.activeEntitlements.contains("my_entitlement_name") {
        // Unlock that great "pro" content.
    }
}
- (void)purchases:(RCPurchases *)purchases completedTransaction:(SKPaymentTransaction*)transaction withUpdatedInfo:(RCPurchaserInfo *)purchaserInfo {
    [purchaserInfo.activeEntitlements containsObject:@"my_entitlement_name"];
}
@Override
public void onCompletedPurchase(String sku, PurchaserInfo purchaserInfo) {
  if (purchaserInfo.getActiveEntitlements().contains("my_entitlement_name")) {
    // Unlock pro content
  }
}
let purchasesHandle = (productIdentifier, purchaserInfo, error) => {
  if (purchaserInfo.activeEntitlements.includes("my_entitlement_name")) {
    // Unlock pro content
  }
}
override fun onCompletedPurchase(sku: String, purchaserInfo: PurchaserInfo) {
  if (purchaserInfo.activeEntitlements.contains("my_entitlement_name")) {
    // Unlock pro content
  }
}

You've done it!

That's all there is to it! You have now implemented a fully featured subscription purchasing system without spending a month writing server code. Congrats!

Next Steps