RevenueCat

Build With RevenueCat

Build a customized mobile subscription business with RevenueCat. We do the heavy lifting of normalizing subscribers from any source and maintain a single source of truth for subscription status, so you can get back to building your app.

RevenueCat is a powerful, secure, reliable, and free to use in-app purchase server with global support. All you need to get started is an API key.

Get Started    API Reference

Making Purchases

Process a transaction with Apple or Google

When it comes time to make a purchase, Purchases SDK has a simple method, purchasePackage, that takes a package from the fetched Offering and purchases the underlying product with Apple or Google.

Purchases.shared.purchasePackage(package) { (transaction, purchaserInfo, error, userCancelled) in
  if purchaserInfo.entitlements["your_entitlement_id"]?.isActive == true {
    // Unlock that great "pro" content              
  }
}
[[RCPurchases sharedPurchases] purchasePackage:package withCompletionBlock:^(SKPaymentTransaction *transaction, RCPurchaserInfo *purchaserInfo, NSError *error, BOOL cancelled) {
  if (purchaserInfo.entitlements[@"your_entitlement_id"].isActive) {
    // Unlock that great "pro" content
  }
}];
Purchases.sharedInstance.purchasePackageWith(
  this,
  aPackage,
  onError = { error, userCancelled -> /* No purchase */ },
  onSuccess = { product, purchaserInfo ->
    if (purchaserInfo.entitlements["my_entitlement_identifier"]?.isActive == true) {
    // Unlock that great "pro" content
  }
})
Purchases.getSharedInstance().purchasePackage(
    this,
    aPackage,
    new MakePurchaseListener() {
        @Override
        public void onCompleted(@NonNull Purchase purchase, @NonNull PurchaserInfo purchaserInfo) {
            if (purchaserInfo.getEntitlements().get("my_entitlement_identifier").isActive()) {
              // Unlock that great "pro" content
            }
        }

        @Override
        public void onError(@NonNull PurchasesError error, Boolean userCancelled) {
          // No purchase
        }
    }
);
try {
  PurchaserInfo purchaserInfo = await Purchases.purchasePackage(package);
  if (purchaserInfo.entitlements.all["my_entitlement_identifier"].isActive) {
    // Unlock that great "pro" content
  }
} on PlatformException catch (e) {
  var errorCode = PurchasesErrorHelper.getErrorCode(e);
  if (errorCode != PurchasesErrorCode.purchaseCancelledError) {
    showError(e);             
  }
}
// Using packages
try {
  const {purchaserInfo, productIdentifier} = await Purchases.purchasePackage(package);
  if (typeof purchaserInfo.entitlements.active.my_entitlement_identifier !== "undefined") {
    // Unlock that great "pro" content
  }
} catch (e) {
  if (!e.userCancelled) {
    showError(e);
  }
}

// Note: if you are using purchaseProduct to purchase Android In-app products, an optional third parameter needs to be provided when calling purchaseProduct. You can use the package system to avoid this
await Purchases.purchaseProduct("product_id", null, Purchases.PURCHASE_TYPE.INAPP);
Purchases.purchasePackage(package, ({ productIdentifier, purchaserInfo }) => {
    if (typeof purchaserInfo.entitlements.active.my_entitlement_identifier !== "undefined") {
      // Unlock that great "pro" content
    }
  },
  ({error, userCancelled}) => {
    // Error making purchase
  }
);

// Note: if you are using purchaseProduct to purchase Android In-app products, an optional third parameter needs to be provided when calling purchaseProduct. You can use the package system to avoid this.

Purchases.purchaseProduct("product_id", ({ productIdentifier, purchaserInfo }) => {
}, ({error, userCancelled}) => {
    // Error making purchase
}, null, Purchases.PURCHASE_TYPE.INAPP);
Purchases purchases = GetComponent<Purchases>();
purchases.PurchasePackage(package, (productIdentifier, purchaserInfo, userCancelled, error) =>
{
  if (purchaserInfo.Entitlements.Active.ContainsKey("my_entitlement_identifier")) {
    // Unlock that great "pro" content
  }
});

The purchasePackage completion block will contain an updated PurchaserInfo object if successful, along with some details about the transaction.

If the error object is present, then the purchase failed. See our guide on Error Handling for the specific error types.

The userCancelled boolean is a helper for handling user cancellation errors. There will still be an error object if the user cancels, but you can optionally check the boolean instead of unwrapping the error completely.

Note: transactions will be automatically finished (acknowledged in Android), and will be made available through the RevenueCat SDK / Dashboard / ETL Exports. You can use observer mode if you don't wish to have transactions finished automatically, but you will have to make sure that you finish them yourself.

Restoring Purchases

Restoring purchases is a mechanism by which your user can restore their in-app purchases, reactivating any content that had previously been purchased from the same store account (Apple or Google).

If two different App User IDs try to restore transactions from the same underlying store account (Apple or Google) RevenueCat will create an alias between the two App User IDs and count them as the same user going forward.

Purchases.shared.restoreTransactions { (purchaserInfo, error) in
    //... check purchaserInfo to see if entitlement is now active
}
[[RCPurchases sharedPurchases] restoreTransactionsWithCompletionBlock:^(RCPurchaserInfo *purchaserInfo, NSError *error) {
    //... check purchaserInfo to see if entitlement is now active
}];
Purchases.sharedInstance.restorePurchasesWith(::showError) { purchaserInfo ->
    //... check purchaserInfo to see if entitlement is now active
}
Purchases.getSharedInstance().restorePurchases(new ReceivePurchaserInfoListener() {
    @Override
    public void onReceived(@android.support.annotation.Nullable PurchaserInfo purchaserInfo, @android.support.annotation.Nullable PurchasesError error) {
    //... check purchaserInfo to see if entitlement is now active   
  }
});
try {
  PurchaserInfo restoredInfo = await Purchases.restoreTransactions();
  // ... check restored purchaserInfo to see if entitlement is now active
} on PlatformException catch (e) {
  // Error restoring purchases
}
try {
  const restore = await Purchases.restoreTransactions();
  // ... check restored purchaserInfo to see if entitlement is now active
} catch (e) {

}
Purchases.restoreTransactions(
  info => {
    //... check purchaserInfo to see if entitlement is now active
  },
  error => {
    // Error restoring purchases
  }
);
var purchases = GetComponent<Purchases>();
purchases.RestoreTransactions((info, error) =>
{
    //... check purchaserInfo to see if entitlement is now active
});

It is recommended that all apps contain some way for users to trigger the restoreTransactions method, even if you have account based restore functionality.

Checking if Purchases are available

A phone can be restricted from accessing the App Store. For example, parents can restrict their children’s ability to purchase additional content, or Play Services might not be available in the case of Android. Your application should confirm that the user is allowed to authorize payments before making a purchase. Your application may also want to alter its behavior or appearance when the user is not allowed to authorize payments.

This check can be done before configuring the Purchases SDK.

if Purchases.canMakePayments() {
    // User is authorized to make payments 
}
if (RCPurchases.canMakePayments) {
  // User is authorized to make payments
}
Purchases.isBillingSupported(context, object : Callback<Boolean> {
  override fun onReceived(isSupported: Boolean) {
    if (isSupported) {
      // Billing Services are available
    } else {
      // Billing Services are unavailable
    }
  }
})
  
// To check if subscriptions (or other types) are supported do:
Purchases.isFeatureSupported(BillingClient.FeatureType.SUBSCRIPTIONS, context, object : Callback<Boolean> {
   override fun onReceived(subscriptionsAreSupported: Boolean) {

   }
 })
Purchases.isBillingSupported(context, new Callback<Boolean>() {
  @Override
  public void onReceived(Boolean isSupported) {
    if (isSupported) {
      // Billing Services are available
    } else {
      // Billing Services are unavailable
    }
  }
});

// To check if subscriptions (or other types) are supported do:

Purchases.isFeatureSupported(BillingClient.FeatureType.SUBSCRIPTIONS, context, new Callback<Boolean>() {
  @Override
  public void onReceived(Boolean subscriptionsAreSupported) {

  }
})

Next Steps

Updated about a month ago


Making Purchases


Process a transaction with Apple or Google

Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.