Skip to main content
Skip to navigation

App-to-web purchases

Sell web products from a paywall inside your mobile app, with checkout in the browser or in a sheet inside the app

AIAsk AIChatGPTClaude

App-to-web purchases let a purchase button on your paywall complete the purchase on the web instead of through the app store. Use them to sell through a RevenueCat Web billing engine (RevenueCat Billing, Stripe Billing, or Paddle Billing) or through your own web checkout, while the Customer keeps the same Entitlements in your app. Whether you can offer app-to-web purchases, and what the store charges for them, is decided by Apple and Google for each storefront, not by RevenueCat.

This feature was previously called the Web Purchase Button. Read the launch blog post for background.

πŸ“˜Web checkout (in-app) is in private beta

Web checkout (in-app) is available to a limited number of apps while it's in private beta. Sign up for early access: Join the waiting list.

How app-to-web purchases work​

A paywall Package can contain both an app store Product and a web Product. When a Customer taps a purchase button, the button's Open option decides which one is sold:

  • Standard checkout starts the store's native purchase flow for the app Product.
  • Web modes start a RevenueCat Web purchase flow, or your custom checkout, for the web Product.

The web modes differ in where the checkout appears. Web checkout (browser) and Web paywall leave the app and open the device's browser. Web checkout (in-app) keeps the Customer in your app and presents the checkout in a sheet. Custom checkout opens your own URL, in the browser or through a deep link.

On Apple platforms, the RevenueCat SDK can present Apple's external purchase disclosure notice before browser checkout or Web checkout (in-app) opens. If the Customer cancels the notice, no checkout opens. See Enable Apple external purchase custom links.

When the purchase completes, RevenueCat grants the Entitlement to the same App User ID that opened the paywall. The SDK refreshes Customer Info when the Customer returns to the app, so the paywall can dismiss and your app can unlock content. See Configure the purchase button.

Before you start​

Work through these three decisions before you change anything in your app or paywall. They decide which store programs apply to you, which purchase button mode to use, and which billing engine the checkout runs on.

Check store rules and fees​

Whether you may offer app-to-web purchases, how you must present them, and what the store charges depend on the storefront and the program you're enrolled in. Work through these questions for each store you ship on:

  • Which storefront is the Customer buying from? Rules and rates follow the App Store or Google Play storefront of the sale, not where your company is based.
  • Which program covers you? Each store runs several programs, such as Apple's unified terms for EU storefronts, Apple's external purchase links in the US, and Google's user choice billing and external offers programs. Some require applying for an entitlement or registering with the store before you can use them.
  • Where does the checkout complete? Stores can charge a different commission for a checkout that completes in the browser than for one that completes inside the app. This is why Web checkout (browser) and Web checkout (in-app) can carry different fees on the same storefront.
  • Do in-app purchases have to stay available? Several programs require store in-app purchases alongside the web option, and some set rules on how prominently each is shown.

Our app-to-web purchase guidelines summarize the programs, rules, and commission rates for each store and region, with links to Apple's and Google's own documentation. Treat the store's documentation as the source of truth. Rates and eligibility change, and we update the guidelines page rather than this one.

Choose a purchase button mode​

Each web mode opens the checkout in a different place, and availability is decided per storefront. The SDK falls back to standard checkout when the SDK version doesn't support the selected Open option. For runtime eligibility checks on Apple platforms, including browser checkout and Web checkout (in-app), see Enable Apple external purchase custom links. To show different paywalls in different countries or on different platforms, use Targeting.

Purchase button modeWhere the checkout opensApp Store (iOS)Google Play (Android)
Web checkout (browser)The device's browserSupported in eligible storefrontsSupported in eligible storefronts
Web paywallThe device's browser, with product selectionSupported in eligible storefrontsSupported in eligible storefronts
Web checkout (in-app)A sheet inside your appSupported in eligible EU storefrontsNot supported at launch
Custom checkoutYour own URL, in the browser or via a deep linkSupported in eligible storefrontsSupported in eligible storefronts

Choose a billing engine​

The RevenueCat-hosted web modes work with every RevenueCat Web billing engine, except Web checkout (in-app), which supports Stripe Billing and Paddle Billing but not yet RevenueCat Billing. Custom checkout uses your own checkout URL instead of a billing engine.

Purchase button modeRevenueCat BillingStripe BillingPaddle BillingYour own checkout URL
Web checkout (browser)βœ… Supportedβœ… Supportedβœ… Supportedn/a
Web paywallβœ… Supportedβœ… Supportedβœ… Supportedn/a
Web checkout (in-app)Not yet supportedβœ… Supportedβœ… Supportedn/a
Custom checkoutn/an/an/aβœ… Supported
πŸ“˜Web checkout (in-app) is in private beta

Web checkout (in-app) is available to a limited number of apps while it's in private beta. Sign up for early access: Join the waiting list.

With Web checkout (in-app), the sheet loads a Stripe-hosted or Paddle-hosted checkout for the selected Package. The checkout settings from your Stripe Billing or Paddle Billing configuration apply to it, and Customers can pay with Apple Pay or Google Pay where available. Payment methods that send the Customer to another page or app to approve the payment aren't supported with Stripe or Paddle Billing. The sheet follows your configured branding and doesn't switch to a separate dark mode theme. Product selection happens on the paywall, so use Web paywall instead when you want Customers to choose a product on the web.

Set up app-to-web purchases​

Follow these steps in order. Steps 1 to 3 prepare your web products and app. Step 4 applies to Apple platforms only. Step 5 turns the feature on in your paywall.

1. Set up your web billing engine​

Configure the billing engine you chose above and create the web Products you want to sell:

For Web checkout (browser) and Web paywall, also create a Web Purchase Link for the Offering your paywall uses. The purchase button opens it for the selected Package. If you're using Custom checkout, skip this step and see Using a custom checkout instead.

2. Add web Products to your Packages​

Add the web Product to each Package in the Offering alongside the app store Product. The Open option only controls which checkout flow to prioritize if a selected Package has both an app Product and a web Product. If a selected Package only has either an app or a web Product, the purchase button starts the standard checkout flow regardless of the Open option selected.

πŸ“˜Packages must include products for the app being used

Even if you're only looking to direct customers to a web checkout flow, the packages on your paywall must include products for the app that the paywall is being viewed on in order to be displayed properly.

3. Update the RevenueCat SDK​

Each purchase button mode requires a minimum SDK version. Older, unsupported SDK versions default to the standard checkout flow when a purchase button is tapped. See Supported SDK versions for the version your platform and mode need.

Skip this step on Android. On Apple platforms, Apple requires an eligibility check, a disclosure notice, and an external purchase token before a Customer leaves for an external purchase in most storefronts. The RevenueCat SDK handles those steps when you enable useExternalPurchaseCustomLinks.

With useExternalPurchaseCustomLinks enabled, the RevenueCat SDK runs Apple's external purchase steps before browser checkout or Web checkout (in-app) opens. It does this on every Apple platform that supports RevenueCat paywalls. It checks that the Customer is eligible, presents the appropriate disclosure notice, and requests an external purchase token, which it registers with RevenueCat. For how RevenueCat uses the token, see How store fees appear in RevenueCat.

The setting is off by default. It applies to Web checkout (in-app) and links that open in the device's external browser: Web checkout (browser), Web paywall, Custom checkout set to open in the external browser, and the legacy Web purchase option on the Button component. It doesn't affect ordinary links opened in the in-app browser, deep links, or Android. If a deep link leads your app to a web checkout, running Apple's steps is up to you. The SDK doesn't offer a public API for them.

⚠️Experimental

These options are experimental and may change in a future SDK release.

Before you enable it:

  • Apple's entitlement: Follow Apple's ExternalPurchaseCustomLink documentation to configure the required entitlement for your app.
  • App Store distribution: Enable the setting for production builds you distribute through the App Store, for example with a build configuration flag. Leave it off for releases distributed outside the App Store, such as Mac apps signed with Developer ID. Otherwise, a failed eligibility check can block web purchases that don't need Apple's external purchase APIs. You can also enable it in development builds installed from Xcode to test on a physical device.
  • OS and Xcode versions: See Apple's ExternalPurchaseCustomLink documentation for the minimum required OS versions and entitlement-specific requirements. Below those minimums, the SDK treats every Customer as not eligible. Apple's EU entitlement guidance directs you to contact Apple for support on earlier OS versions.

Set useExternalPurchaseCustomLinks when you configure the SDK. Both useExternalPurchaseCustomLinks and enableExternalPurchasesInSimulator are experimental in all the SDKs shown below. The cross-platform SDKs ignore the options on Android.

@_spi(Experimental) import RevenueCat

Purchases.configure(
with: Configuration.Builder(withAPIKey: "<public_apple_api_key>")
.with(useExternalPurchaseCustomLinks: true)
// To test blocked checkout in the simulator:
// .with(useExternalPurchaseCustomLinks: true, enableExternalPurchasesInSimulator: false)
.build()
)

Objective-C has no equivalent while the options are experimental. cordova-plugin-purchases doesn't support RevenueCat paywalls, so these options aren't available there.

With the setting on, browser checkout and Web checkout (in-app) go through these steps when the Customer taps the button:

  1. Payments check: If the device can't make payments, nothing opens and no alert is shown.
  2. Eligibility check: The SDK checks StoreKit's ExternalPurchaseCustomLink.isEligible. If the Customer isn't eligible, the paywall shows a purchase-unavailable alert and checkout doesn't open. See How Apple decides eligibility for eligibility requirements and testing observations. US storefront exception: Checkout can proceed without Apple's notice or an external purchase token. This is built into the SDK, so you can keep useExternalPurchaseCustomLinks enabled without special handling for the US.
  3. Disclosure notice: The SDK calls Apple's notice API, using the browser notice for browser flows and the within-app notice for Web checkout (in-app). The sheet can be skipped if the Customer previously chose not to see it again. If the Customer cancels, or the API returns an error, nothing opens.
  4. Token registration: The SDK requests an external purchase token and registers it with RevenueCat.
  5. Web checkout: The browser or in-app checkout session opens and is associated with the token registered in step 4, when available.

Configuring useExternalPurchaseCustomLinks alone doesn't make a Customer eligible. The RevenueCat SDK automatically handles Apple's required eligibility, payments, and notice APIs for browser checkout and Web checkout (in-app). After configuring Apple's required entitlement and enabling the setting, you don't need to call those StoreKit APIs yourself for these flows.

5. Configure the purchase button​

Select a purchase button in the paywall editor. Purchase buttons have an Open option that allows you to select the type of checkout flow to start. All four web modes below require the selected package to have both an app product and a web product. If it has only one, the purchase button starts the standard checkout flow instead.

OptionBehavior description
Standard checkoutStarts the standard checkout flow for the platform the paywall is being viewed on (in-app or web).
Web checkout (browser)Opens a web checkout for the selected package using the device's browser.
Web checkout (in-app)Opens a web checkout for the selected package in a sheet inside your app, without leaving for the browser. Currently supported with Stripe Billing and Paddle Billing.
Web paywallOpens your web paywall in the device's browser, allowing customers to choose the web product to purchase. Use this when you want to offer product selection on the web, rather than in the app.
Custom checkoutOpens your custom checkout URL, appending the selected package ID, App User ID, and environment as URL parameters when enabled.
πŸ“˜Hardcode web product details instead of using variables

If you're using a web or custom checkout flow, we recommend hardcoding the web product details such as its price, duration, or introductory offer instead of using variables. This is because variables will always reflect the app product details, which may differ from the web product you're offering. If they don't, you can safely use variables, but as you experiment with prices and offers please keep this in mind.

Then decide what the paywall does when the Customer comes back. When a user taps a purchase button that directs them to a web browser to complete the checkout flow, the RevenueCat SDK will automatically invalidate the cached customer info so that new info is fetched from the server when the user returns to the app. You can additionally choose whether to have the app paywall be dismissed when the user returns to the app, or whether to keep the paywall open, using the Auto dismiss setting on your paywall.

With Web checkout (in-app), the Customer never leaves the app. When the purchase completes, the sheet closes and the SDK refreshes Customer Info so the Entitlement is available right away.

Using a custom checkout​

If you chose Custom checkout, your own checkout page completes the purchase instead of a RevenueCat-hosted flow. You'll have the option of passing the following parameters in the URL:

  1. Package - The selected package ID, appended as rc_package
  2. App User ID - The RevenueCat app user ID, appended as rc_app_user_id
  3. Environment - Whether the paywall is being viewed in a sandbox or production environment, appended as rc_env

On Apple platforms, when Apple external purchase custom links are on, Custom checkout URLs that open in the external browser also get rc_external_purchase_token_id. The SDK appends it whenever it registers an external purchase token, and you can't turn it off.

Forward this value with the purchase so RevenueCat can report the purchase to Apple:

  • Web SDK: pass it as externalPurchaseTokenId to purchase() or presentPaywall(), using purchases-js 1.56.0 or later
  • Stripe Checkout: set rc_external_purchase_token_id in the Checkout Session's metadata, and in subscription_data.metadata for subscriptions
  • Paddle Checkout: set rc_external_purchase_token_id in the transaction's custom_data
  • Your server: send rc_external_purchase_token_id in the POST /receipts request body

If RevenueCat never receives the value, the purchase still grants the Entitlement, but RevenueCat can't report it to Apple.

In addition, custom purchase buttons can be set to open a URL via external browser, or via deep link if your app is set up to do additional processing before opening the URL.

Test app-to-web purchases​

To test web purchases without real charges, configure sandbox purchases for your web config. See Test RevenueCat Web purchase flows. On Apple platforms, testing the eligibility check and disclosure notice needs a physical device, as described below.

How Apple decides eligibility​

See Apple's ExternalPurchaseCustomLink.isEligible documentation for how Apple determines eligibility.

Our tests also showed that eligibility followed the production account signed in to Media & Purchases and depended on physical location. These are observations from our tests, in addition to Apple's documented conditions:

  • Production account: Changing only the Sandbox Apple Account didn't override eligibility. Changing the production App Store account (Media & Purchases on iOS) changed the result without changing the iCloud account.
  • Physical location: A production account from an eligible region on a device in an ineligible region wasn't enough to make it eligible. Simulating different locations from Xcode didn't change the isEligible result on a physical device.
  • Simulator: Apple's eligibility check returned false in our simulator tests. RevenueCat's simulator behavior is a testing convenience, not a way to make Apple's check return true. See Test external purchase custom links.

Our physical-device tests used a signed app with the eligible region among its permitted regions:

Device locationMedia & Purchases account countrySandbox Apple Account countryisEligible
Eligible regionEligible regionNot signed intrue
Eligible regionEligible regionIneligible regiontrue
Eligible regionIneligible regionEligible regionfalse
Ineligible regionEligible regionEligible regionfalse

To test the eligibility check and Apple's notice, you can run a development build installed from Xcode on a physical device. Verify these prerequisites:

  • App configuration: Enable useExternalPurchaseCustomLinks and use a supported SDK, OS, and Xcode version. The entitlement and its permitted regions must be present in the signed app, not just the project's entitlements file.
  • Production account: Use a production App Store account (Media & Purchases on iOS) from a region permitted for your app. Keep this account signed in, even if you also use a Sandbox Apple Account.
  • Device location: Use a physical device in an eligible region. A production account from an eligible region isn't enough if the device is in an ineligible region.

To check that the entitlement made it into your build, inspect the signed app. The output must include your app's external purchase entitlement, for example com.apple.developer.storekit.custom-purchase-link.allowed-regions with the intended regions:

codesign -d --entitlements - "/path/to/YourApp.app"

In our experience, Xcode shows the entitlement's region list in Signing & Capabilities when you're signed in with an Apple account on the team that registered the bundle ID. A missing region list doesn't prove that the entitlement is missing from the signed app. Inspect the signed app to verify it.

Apple also supports testing custom link tokens in sandbox, but sandbox testing doesn't bypass eligibility.

Because the simulator isn't eligible, the SDK skips Apple's steps there by default. Both browser checkout and Web checkout (in-app) open in any storefront, with no notice and no token. To test the blocked-checkout path, set enableExternalPurchasesInSimulator to false. The paywall then shows the unavailable-purchase alert and doesn't open checkout, regardless of storefront. This doesn't test Apple's notice or tokens, and the option has no effect on a physical device.

How store fees appear in RevenueCat​

RevenueCat reflects store commissions in transaction proceeds and in Charts. If your App Store app is enrolled in Apple's Alternative EU Terms Addendum, set your addendum dates in your app settings so RevenueCat applies the right rates. See Apple Alternative EU Terms Addendum.

When you enable Apple external purchase custom links, the SDK registers an Apple external purchase token with RevenueCat before the checkout opens. RevenueCat reports the purchase, and its later renewals, refunds, and product changes, to Apple on your behalf, and applies Apple's commission to the transaction's proceeds. RevenueCat also reports tokens that don't lead to a purchase, as Apple requires.

This covers purchases through RevenueCat Billing, Stripe Billing, and Paddle Billing, and custom checkouts that forward rc_external_purchase_token_id. In the US storefront, the SDK doesn't request a token, so RevenueCat doesn't report those purchases to Apple.

Supported SDK versions​

App Store​

RevenueCat SDKRequired SDK version
purchases-ios5.24.0 and up
react-native-purchases8.10.1 and up
purchases-flutter8.8.1 and up
purchases-kmp1.7.8+13.32.0 and up
purchases-capacitor10.3.3 and up
purchases-unity8.4.0 and up

Play Store​

RevenueCat SDKRequired SDK version
purchases-android9.12.0 and up
react-native-purchases9.6.1 and up
purchases-flutter9.9.1 and up
purchases-kmp2.2.4+17.2.0 and up
purchases-capacitor11.2.9 and up
purchases-unity8.4.1 and up

Older, unsupported SDK versions will default to the standard checkout flow when a purchase button is tapped.

Web checkout (in-app)​

Web checkout (in-app) requires newer SDK versions than the browser modes. On SDK versions that don't support it, a purchase button set to Web checkout (in-app) starts the standard checkout flow instead.

πŸ“˜Web checkout (in-app) is in private beta

Web checkout (in-app) is available to a limited number of apps while it's in private beta. Sign up for early access: Join the waiting list.

RevenueCat SDKRequired SDK version
purchases-iosComing soon
Other SDKsNot supported at launch

The Apple external purchase custom links options require these SDK versions.

RevenueCat SDKRequired SDK version
purchases-ios5.92.0 and up
react-native-purchases10.11.0 and up
purchases-flutter10.14.0 and up
purchases-kmp3.11.0 and up
purchases-capacitor13.7.0 and up
purchases-unity9.12.0 and up

Legacy option on the Button component​

This legacy option lives on the separate Button component, and is unrelated to the Open option on the purchase button described above.

When the US court ruling was first announced to allow web checkout flows in-app in the App Store, we released an option on the Button component to navigate to a Web purchase flow. This is still supported, but not recommended for new paywalls, since it does not support navigating directly to the checkout experience for a selected package. However, if you're already using this option, or would like to, you may continue to do so on the following supported SDK versions.

RevenueCat SDKVersion required for the legacy Web purchase option
purchases-ios5.22.2 and up
react-native-purchases8.9.6 and up
purchases-flutter8.7.5 and up
purchases-kmp1.7.7+13.29.1 and up
Other SDKsNot supported

Next steps​

Was this page helpful?