Making Purchases
The SDK has a simple method, purchase(package:)
, that takes a package from the fetched Offering and purchases the underlying product with Apple, Google, or Amazon.
- Swift
- Obj-C
- Kotlin
- Kotlin Multiplatform
- Java
- Flutter
- React Native
- Cordova
- Capacitor
- Unity
- Web (JS/TS)
Purchases.shared.purchase(package: package) { (transaction, customerInfo, error, userCancelled) in
if customerInfo.entitlements["your_entitlement_id"]?.isActive == true {
// Unlock that great "pro" content
}
}
[[RCPurchases sharedPurchases] purchasePackage:package withCompletion:^(RCStoreTransaction *transaction, RCCustomerInfo *customerInfo, NSError *error, BOOL cancelled) {
if (customerInfo.entitlements[@"your_entitlement_id"].isActive) {
// Unlock that great "pro" content
}
}];
Purchases.sharedInstance.purchaseWith(
PurchaseParams.Builder(this, aPackage).build(),
onError = { error, userCancelled -> /* No purchase */ },
onSuccess = { storeTransaction, customerInfo ->
if (customerInfo.entitlements["my_entitlement_identifier"]?.isActive == true) {
// Unlock that great "pro" content
}
}
)
Purchases.sharedInstance.purchase(
packageToPurchase = aPackage,
onError = { error, userCancelled ->
// No purchase
},
onSuccess = { storeTransaction, customerInfo ->
if (customerInfo.entitlements["my_entitlement_identifier"]?.isActive == true) {
// Unlock that great "pro" content
}
}
)
Purchases.getSharedInstance().purchase(
new PurchaseParams.Builder(this, aPackage).build(),
new PurchaseCallback() {
@Override
public void onCompleted(@NonNull StoreTransaction storeTransaction, @NonNull CustomerInfo customerInfo) {
if (customerInfo.getEntitlements().get(<my_entitlement_identifier>).isActive()) {
// Unlock that great "pro" content
}
}
@Override
public void onError(@NonNull PurchasesError purchasesError, boolean b) {
// No purchase
}
}
);
// Using Offerings/Packages
try {
CustomerInfo customerInfo = await Purchases.purchasePackage(package);
if (customerInfo.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);
}
}
// Note: if you are not using offerings/packages to purchase In-app products, you can use purchaseStoreProduct and getProducts
try {
CustomerInfo customerInfo = await Purchases.purchaseStoreProduct(productToBuy);
if (customerInfo.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 Offerings/Packages
try {
const { customerInfo } = await Purchases.purchasePackage(package);
if (
typeof customerInfo.entitlements.active["my_entitlement_identifier"] !==
"undefined"
) {
// Unlock that great "pro" content
}
} catch (e) {
if (!e.userCancelled) {
showError(e);
}
}
// Note: if you are not using offerings/packages to purchase In-app products, you can use purchaseStoreProduct and getProducts
try {
const { customerInfo } = await Purchases.purchaseStoreProduct(productToBuy);
if (
typeof customerInfo.entitlements.active["my_entitlement_identifier"] !==
"undefined"
) {
// Unlock that great "pro" content
}
} catch (e) {
if (!e.userCancelled) {
showError(e);
}
}
Purchases.purchasePackage(package, ({ productIdentifier, customerInfo }) => {
if (typeof customerInfo.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, customerInfo }) => {
}, ({error, userCancelled}) => {
// Error making purchase
}, null, Purchases.PURCHASE_TYPE.INAPP);
try {
const purchaseResult = await Purchases.purchasePackage({ aPackage: packageToBuy });
if (typeof purchaseResult.customerInfo.entitlements.active['my_entitlement_identifier'] !== "undefined") {
// Unlock that great "pro" content
}
} catch (error: any) {
if (error.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {
// Purchase cancelled
} else {
// Error making purchase
}
}
// Note: if you are not using offerings/packages to purchase In-app products, you can use purchaseStoreProduct and getProducts
try {
const purchaseResult = await Purchases.purchaseStoreProduct({
product: productToBuy
});
if (typeof purchaseResult.customerInfo.entitlements.active['my_entitlement_identifier'] !== "undefined") {
// Unlock that great "pro" content
}
} catch (error: any) {
if (error.code === PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR) {
// Purchase cancelled
} else {
// Error making purchase
}
}
Purchases purchases = GetComponent<Purchases>();
purchases.PurchasePackage(package, (productIdentifier, customerInfo, userCancelled, error) =>
{
if (customerInfo.Entitlements.Active.ContainsKey("my_entitlement_identifier")) {
// Unlock that great "pro" content
}
});
try {
const { customerInfo } = await Purchases.getSharedInstance().purchase({
rcPackage: pkg
});
if (Object.keys(customerInfo.entitlements.active).includes("pro")) {
// Unlock that great "pro" content
}
} catch (e) {
if (e instanceof PurchasesError && e.errorCode == ErrorCode.UserCancelledError) {
// User cancelled the purchase process, don't do anything
} else {
// Handle errors
}
}
The purchase(package:)
completion block will contain an updated CustomerInfo 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.
Transactions (new and previous transactions that are synced) will be automatically completed (finished on iOS, acknowledged and consumed in Android), and will be made available through the RevenueCat SDK / Dashboard / ETL Exports.
If you are migrating an existing app to RevenueCat and want to continue using your own in-app purchase logic, you can tell the SDK that your app is completing transactions if you don't wish to have transactions completed automatically, but you will have to make sure that you complete them yourself.
Next Steps
- Don't forget to provide some way for customers to restore their purchases
- With purchases coming through, make sure they're linked to the correct app user ID
- If you're ready to test, start with our guides on sandbox testing