Skip to main content

iOS Subscription Offers

πŸ“˜

This guide assumes you already have your iOS products set up in App Store Connect.

Subscription Offers allow developers to apply custom pricing and trials to new customers and to existing and lapsed subscriptions.

Subscription Offers are supported in the Purchases SDK, but require some additional setup first in App Store Connect and the RevenueCat dashboard.

Types of Subscription Offers​

Offer TypeApplies ToSubscription Key RequiredNotesAuto-Renewal
Introductory OffersNew UsersπŸ”‘ Required (in StoreKit2)Applied to eligible purchases automatically. How to check eligibility.βœ…
Promotional OffersExisting and Lapsed UsersπŸ”‘ RequiredNot applied automatically, see implementation guide belowβœ…
Offer CodesNew and Existing UsersπŸ”‘ RequiredRequires iOS SDK 3.8.0+, see implementation guide belowβœ…
Win-Back OffersLapsed UsersπŸ”‘ RequiredWin-Back Offers are only available to users on iOS18.0+.βœ…
⚠️ Not recommended
In-App Purchase Promo Codes
New and Existing UsersNot RequiredTreated as a regular purchase, revenue will not be accurate in Charts and Integrations due to Apple/StoreKit limitations. Codes don't auto-renew, aren't compatible with presentCodeRedemptionSheet, restricted to non-commercial use, and restricted to 1,000 codes every 6 months.❌

In-App Purchase Keys​

For RevenueCat to securely authenticate and validate a Subscription Offer request with Apple, you'll need to upload an In-App Purchase Key following our guide.

Promotional Offers​

In iOS 12.2, Apple announced a new feature for subscription developers called β€œPromotional Offers.”

1. Configure the Offer in App Store Connect​

Promotional Offers are created from within App Store Connect and are included as a pricing option to an existing subscription product. When you click the "+" option next to Subscription Prices, you'll see an option to Create Promotional Offer.

Subscription Offers are created as new pricing options in App Store Connect

To create the offer there are two fields that you need to specify: Reference Name, which is just used for your reference, and the Promotional Offer Product Code, which is what you will actually use to activate a specific offer in your app. On the next screen you'll select the type of offer you wish to provide. Just like introductory offers, there are three types of Promotional Offers:

  1. Pay-up-front β€” The customer pays once for a period of time, e.g. $0.99 for 3 months. Allowed durations are 1, 2, 3, 6 and 12 months.
  2. Pay-as-you-go β€” The customer pays a reduced rate, each period, for a number of periods, e.g. $0.99 per month for 3 months. Allowed durations are 1-12 months. Can only be specified in months.
  3. Free β€” This is analogous to a free trial, the user receives 1 of a specified period free. The allowed durations are 3 days, 1 week, 2 weeks, 1 month, 2 months, 3 months, 6 months, and 1 year.
πŸ“˜

Don't forget to click Save in the upper right after you configure the offer.

2. Show the Promotional Offer to Desired Users​

It's up to you to decide which users you want to present a Promotional Offer to. The only eligibility requirements are that the user had (or currently has) an active subscription. Apple automatically enforces this requirement for you - if it's not met users will be shown the regular product regardless of the offer you try to present.

Fetch the PromoOffer​

Before you can present a Promotional Offer to a user, you first need to fetch the PromoOffer. This is done by passing the StoreProduct and a StoreProductDiscount to the .getPromotionalOffer method, which uses the Subscription Key from above to validate the discount and to provide a valid PromoOffer:

if let discount = package.storeProduct.discounts.first {
Purchases.shared.getPromotionalOffer(forProductDiscount: discount,
product: package.storeProduct) { (promoOffer, error) in
if let promoOffer {
// Promotional Offer validated, show terms of your offer to your customers
} else {
// Promotional Offer was not validated, default to normal package terms
}
}
}

// OR: if using async/await
let promoOffers = await package.storeProduct.eligiblePromotionalOffers()

Purchase the Product with the Promotional Offer​

After successfully fetching the PromoOffer, you can now display the Promotional Offer to the user however you'd like. If the user chooses to purchase, pass a Package and PromoOffer to the .purchase(package:promotionalOffer:) method.

Purchases.shared.purchase(package: package,
promotionalOffer: promoOffer) { transaction, customerInfo, error, userCancelled in
if customerInfo?.entitlements.all[<your_entitlement_id>]?.isActive == true {
// Unlock that entitlements content
}
}

Offer Codes​

With iOS 14, Apple announced a new feature for subscription developers called β€œOffer Codes.” Offer Codes allow developers to offer custom pricing and trials, in the form of a redeemable code, to their customers.

1. Configuring an Offer Code​

Offer Codes are configured similarly to Subscription Offers in App Store Connect.

Screen Shot 2020-12-01 at 10.02.15 AM.png

2. Redeeming an Offer Code​

Option 1: In-app Redemption Sheet​

❗️

Since launch, Apple's in-app Offer Code redemption sheet has proven to be extremely unstable. For example, the sheet may not connect, may not dismiss after a successful redemption, and may not accept valid codes. Additionally, sandbox and TestFlight behavior has been seen to be inconsistent.

A workaround may be to instead redirect customers to the App Store app to redeem codes as described below.

To allow your users to redeem Offer Codes, you'll need to present the Offer Code redemption sheet. In Purchases SDK 3.8.0, you can call the presentCodeRedemptionSheet method.

Purchases.shared.presentCodeRedemptionSheet()

Apple does not provide a callback to determine if the code redemption was successful. Since the Purchases SDK will automatically pick up on new transactions that enter the underlying transaction queue, you should implement the receivedUpdated delegate or listener to respond to changes in CustomerInfo. Once we sync the Offer Code transaction, we'll automatically refresh CustomerInfo.

⚠️

The Offer Code redemption sheet may not display on a device if you haven't yet launched the App Store app and accepted the terms agreement.

Option 2: Redirect to App Store app​

You can link to the App Store with a prefilled code for redemption with the following URL format: https://apps.apple.com/redeem?ctx=offercodes&id={apple_app_id}&code={code}

You can find your Apple App ID in your app settings in App Store Connect (General -> App Information).

When users click your link within your app to redeem the offer code, it will take them outside of the app to complete the purchase. It is important to call syncPurchases when the user returns back to your app to retrieve their purchase. This may be done by recording when the user leaves the app due to the link, and calling syncPurchases when the user returns to the app. If not, the user may need to trigger a restore within your app when they come back.

Considerations​

  • In order for RevenueCat to accurately track revenue for offer codes, you will need to upload an in-app purchase key. See our guide on In-App Purchase Key Configuration for step-by-step instructions.

Win-Back Offers​

Apple introduced win-back offers with iOS 18, which allow developers to provide custom pricing and trials to subscribers who have canceled a subscription and meet specific eligibility criteria set by the developer.

1. Configuring a Win-Back Offer​

⚠️

Win-Back Offers can only be configured for subscription products that have been approved by App Review. Please ensure that your subscription product has been approved by App Review before proceeding.

Navigate to your subscription's page under Apps β†’ Your App β†’ Subscriptions (in the left sidebar) β†’ Your Subscription Group β†’ Your Subscription Product and click the "+" button by "Subscription Prices". If your subscription product has been approved by App Review, you'll see the option "Create Win-Back Offer". Select it.

configure_winback_offer_1.png

To create the win-back offer there are two fields that you need to specify: Reference Name, which is just used for your reference, and the Offer Identifier, which is a distinct ID for the win-back offer.

configure_winback_offer_2.png

Next, you’ll be asked to provide the specifics of your win-back offer, including:

  • Offer Publish Date: The date interval that the win-back offer will be available to churned subscribers. (More Info)
  • Offer Priority: Determines if this win-back offer takes precedence over other offers available for its product.
    • If a subscriber is eligible for multiple offers on the same subscription product, offers with a with a high priority will be displayed instead of offers with a normal priority in StoreKit views.
  • Customer Eligibility: Developer-provided eligibility criteria that determines whether a churned subscriber can purchase a win-back offer.
    • Minimum Paid Duration: The time that a subscriber must have been subscribed to that product to be eligible for the offer.
    • Time Since Last Subscribed: A time interval describing how long a subscriber must have been lapsed to be eligible for the offer.
    • Wait Between Offers: The required time a subscriber must wait after a win-back offer ends before redeeming the same offer again. Optional.

configure_winback_offer_3.png

2. Redeeming a Win-Back Offer​

Subscribers can redeem a win-back offer in several ways. We're currently developing support for redeeming win-back offers within our SDK, and we will update this documentation as more redemption options become available.

Win-Back Offer Purchasing MethodNotes
Through the App Store with Streamlined Purchasing EnabledShould not be used if you require a subscriber to perform an action before purchasing (e.g. signing in). Requires iOS SDK version 5.0.0+
Through Your App From a StoreKit MessageRequires iOS SDK version 5.6.0+
Through the App Store with Streamlined Purchasing DisabledRequires iOS SDK version 5.9.0+
Through Your PaywallRequires iOS SDK version 5.10.0+
Through a StoreKit StoreViewRequires iOS SDK version 5.0.0+
Through Your App with Your Own IAP CodeSupported, you can find more info on using your own IAP code here

Redeeming a Win-Back Offer Through the App Store with Streamlined Purchasing Enabled​

⚠️

Use this method only if you do not require subscribers to perform an action, like signing in before purchasing. If you do require such actions, disable Streamlined Purchasing (instructions here). By default, Streamlined Purchasing is enabled for all apps.

Purchasing win-back offers through the App Store uses Streamlined Purchasing by default, meaning that subscribers can complete the entire win-back offer redemption flow in the App Store. In addition to the steps mentioned above to configure your win-back offer, we recommend doing the following:

  1. Mandatory: Upload an image for your win-back offer in App Store Connect (instructions).
  2. Enable Apple App Store Notifications so that RevenueCat can be notified of win-back offers redeemed through the App Store, even if the user does not open your app.
Testing Win-Back Offers Redeemed Through the App Store with Streamlined Purchasing Enabled​

Testing win-back offers redeemed through the App Store with Streamlined Purchasing enabled can only be performed in the sandbox environment on a physical device. To test win-back offers redeemed this way:

  1. Create a sandbox test account
  2. Add the sandbox test account to your device
  3. Configure a win-back offer on a subscription product in App Store Connect as described above.
  4. Make note of the product ID and the win-back offer ID. You'll need it later!
  5. Cancel any subscriptions that are from the same subscription group as the win-back offer’s subscription.
  6. Open your app and purchase the subscription product that contains the win-back offer.
  7. Cancel the subscription. This can be done easily in the Settings app by navigating to your sandbox test account's page (Settings β†’ App Store β†’ Sandbox Account β†’ Manage β†’ Subscriptions β†’ Cancel Subscription)
  8. Close your app.
  9. Wait for your subscription to expire.
  10. In your sandbox test account's Account Settings page, turn off the "Display Win-back Offer Sheet" toggle

disable_winback_offer_sheet.jpg

  1. Select "Test Transactions"

select_test_transactions.jpg

  1. Enter the product ID of the product you're testing, your app's bundle ID, and the win-back offer's identifier. These values must be valid IDs entered into App Store Connect.

test_transactions.jpg

  1. Tap the "Test Transactions" button at the bottom of the page.
  2. The system presents the App Store [Sandbox] payment sheet for the sandbox environment. Confirm the purchase.
  3. Open your app.
  4. Check that your subscriber receives the proper entitlements from RevenueCat, and that the purchase appears in your RevenueCat dashboard with "Sandbox data" enabled.

Repeat steps 5-16 to test redeeming win-back offers multiple times.

Redeeming a Win-Back Offer In Your App from a StoreKit Message​

In iOS 18+, StoreKit will send your app a message when a subscriber is eligible to redeem a win-back offer. When this message is received, our SDK will automatically present the associated StoreKit win-back offer sheet to the subscriber, which allows them to redeem the win-back offer:

win_back_offer_sk_message.png

If you'd like to defer displaying the win-back offer sheet until a later time, you can do so in iOS SDK versions 5.6.0 and above by setting the showStoreMessagesAutomatically flag to false in the Purchases.configure function. When this flag is set to false, the win-back offer sheet will not be displayed automatically, and you will need to manually display the offer sheet by calling the showStoreMessages function when you'd like it to be displayed:

// Configure Purchases with showStoreMessagesAutomatically set to false
Purchases.configure(
with: .init(withAPIKey: "${YOUR_API_KEY}")
.with(showStoreMessagesAutomatically: false)
)

// Later, when you're ready to display the win-back offer sheet:
await Purchases.shared.showStoreMessages()

If you're running other win-back campaigns on non-iOS stores, we recommend deferring displaying the win-back offer message until after you've checked the subscriber's entitlements. If the subscriber has entitlements, then we'd recommend not displaying the message to avoid the subscriber signing up for multiple win-back offers at the same time.

Testing Win-Back Offers Redeemed In Your App from a StoreKit Message​
  1. Create a sandbox test account
  2. Add the sandbox test account to your device
  3. Configure a win-back offer on a subscription product in App Store Connect as described above.
  4. Make note of the product ID and the win-back offer ID. You'll need it later!
  5. Cancel any subscriptions that are from the same subscription group as the win-back offer’s subscription.
  6. Open your app and purchase the subscription product that contains the win-back offer.
  7. Cancel the subscription. This can be done easily in the Settings app by navigating to your sandbox test account's page (Settings β†’ App Store β†’ Sandbox Account β†’ Manage β†’ Subscriptions β†’ Cancel Subscription)
  8. Close your app.
  9. Wait for your subscription to expire.
  10. In your sandbox test account's Account Settings page, turn on the "Display Win-back Offer Sheet" toggle:

enable_winback_offer_sheet.jpg

  1. Select "Test Transactions":

select_test_transactions.jpg

  1. Enter the product ID of the product you're testing, your app's bundle ID, and the win-back offer's identifier. These values must be valid IDs entered into App Store Connect.

test_transactions.jpg

  1. DO NOT tap the "Test Transactions" button at the bottom of the page. Instead, background the Settings app (do not force close it).
  2. Open your app.
  3. If the Purchases SDK is configured to show StoreKit messages automatically, the win-back offer redemption sheet will be displayed. If not, you can call showStoreMessages() to display the offer sheet.

enable_winback_offer_sheet.jpg

  1. Redeem the win-back offer.
  2. Check that your subscriber receives the proper entitlements from RevenueCat, and that the purchase appears in your RevenueCat dashboard with "Sandbox data" enabled.

Repeat steps 5-17 to test redeeming win-back offers multiple times.

⚠️

In our testing, we've found that this method of testing does not work 100% of the time, and that sometimes there can be a noticeable delay between when you leave the Settings app and when the win-back offer redemption sheet is displayed.

Redeeming a Win-Back Offer Through the App Store with Streamlined Purchasing Disabled​

Purchasing win-back offers through the App Store uses Streamlined Purchasing by default, meaning that subscribers can complete the entire win-back offer redemption flow in the App Store. If you require users to perform an action in your app before making a purchase, like signing in, you can disable Streamlined Purchasing. When Streamlined Purchasing is disabled, subscribers will start the win-back redemption flow in the App Store, but will complete the flow in your app. Support for redeeming win-back offers from the App Store with Streamlined Purchasing disabled is available in the iOS SDK versions 5.9.0+.

You can disable Streamlined Purchasing in App Store Connect under Apps β†’ Your App β†’ Subscriptions β†’ Streamlined Purchasing.

disable_streamlined_purchasing.png

After a user starts a win-back redemption flow on the App Store and opens your app, the RevenueCat SDK will call the PurchasesDelegate.purchases(readyForPromotedProduct:startPurchase) delegate function to let your app know that the user has started a redemption flow. Your app should then perform any actions it needs to do before the redemption flow continues. When your app is ready for the user to complete the redemption flow, you should call the startPurchase callback provided in the readyForPromotedProduct function.

public class PurchasesDelegate: NSObject, PurchasesDelegate {

var promotedProduct: StoreProduct?
var completePurchaseFlow: StartPurchaseBlock?

public func purchases(_ purchases: Purchases,
readyForPromotedProduct product: StoreProduct,
purchase startPurchase: @escaping StartPurchaseBlock) {
self.promotedProduct = product
self.completePurchaseFlow = startPurchase

// Do what you need to do before letting the user complete the redemption flow
// Once that is done, call self.completePurchaseFlow() to complete the redemption
}
}

// Don't forget to register your PurchasesDelegate with the SDK, like so:
Purchases.shared.delegate = PurchasesDelegate()

In addition to the steps mentioned above to configure your win-back offer, we recommend doing the following:

  1. Mandatory: Upload an image for your win-back offer in App Store Connect (instructions).
  2. Enable Apple App Store Notifications so that RevenueCat can be notified of win-back offers redeemed through the App Store, even if the user does not open your app.
Redeeming a Win-Back Offer on your Paywall​

You can display win-back offers to subscribers on your paywall for users to redeem using iOS SDK versions 5.10+. After configuring your win-back offers, you can use our SDK to fetch the win-back offers that a subscriber is eligible for for a given product:

let eligibleWinBackOffers: [WinBackOffer] = try await Purchases.shared.eligibleWinBackOffers(
forProduct: product
)

// Now, display these eligible win-back offers on your paywall
πŸ“˜β˜οΈ

Note: The eligibleWinBackOffers function only returns the win-back offers that the current subscriber is eligible for, not all of the win-back offers that you've created for a product. It will return an empty array when the subscriber is not eligible for any win-back offers.

Once the user has selected a win-back offer that they'd like to redeem, you can purchase it with the SDK:

let purchaseParams = PurchaseParams.Builder(product: purchasedPackage.storeProduct)
.with(winBackOffer: offer)
.build()

try await Purchases.shared.purchase(purchaseParams)

For more information on displaying and purchasing products on your paywall, check out our Displaying Products and Making Purchases guides.

Redeeming a Win-Back Offer in a StoreKit StoreView​

You can also redeem win-back offers in a StoreKit StoreView with the following SwiftUI code:

StoreView.forOffering(
offering, // The offering you'd like to use
icon: { product in
// Return your custom icon view for each product
Image(systemName: "dollarsign.circle")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50, height: 50)
.padding()
},
placeholderIcon: {
// Return a custom placeholder icon while loading
ProgressView()
.progressViewStyle(.circular)
}
)

Considerations​

  • Since win-back offers use StoreKit 2 under the hood, you must upload an in-app purchase key to RevenueCat to use win-back offers. See our guide on In-App Purchase Key Configuration for step-by-step instructions.

Next Steps​