Web paywall
Before you begin, make sure you have installed Adapty SDK version 3.6.1 or later.
With Adapty, you can create a paywall with a button that redirects your iOS app users to Safari for payment. Then, when they return to your app after a successful purchase, the subscription activates. This allows you to bypass App Store fees while effectively tracking user payments.

The App Store allows external payment options only in the USA.
To use a paywall exclusively for the US market, duplicate your current paywall and set up a web paywall. This way, you will have two almost identical paywalls in use: one for the US and another for everybody else.
How it works
Web paywall is a unique URL for each of your in-app paywalls. It allows users to go to the browser and pay there. It works with different payment providers (Stripe, Paddle, and others) and allows you to have either a single page with an Apple Pay button or more complex flows with upsells.

Web paywalls work in the following way:
- You configure how the web paywall page will look and work in the editor.
- You link the web paywall in the paywall settings.
- In your app paywall, you add a button redirecting users to the browser.
- Once users tap the button, Adapty SDK generates a unique URL.
- Users go to the web paywall page and pay for a subscription using an external payment method.
- When they return to the app, Adapty SDK starts tracking whether the profile has been updated because of the purchases made.
- Adapty gets information about the purchase event, records it as an analytics event, and monitors it for any updates.
Step 1. Create a web paywall
- If you want to enable external payments for an existing paywall, you need to duplicate it so you can show it only to your U.S. user segment and show the old one to all the other users. If you want to start from scratch, create a new paywall.
- In the Paywall, switch to the Web paywall tab and click Create web paywall. You will be redirected to a new page.
- Set up the web paywall itself and connect a payment method.
Use our quickstart guide that will help you to launch a working web paywall.
- Return to the Web paywall page and paste the paywall link.
When launching your paywall to the production environment, it is crucial to ensure you use the correct link generated after publishing your web paywall. The link format is paywalls-....fnlfx.com
.
- Click Save.
Step 2. Trigger the paywall
To use your web paywall, you need to trigger it, and the way you do it depends on your setup:
- If you are using the Paywall created in the Builder, you only need to add a new button that will use the link you've provided to track purchases and send the data back to Adapty.
- If you are using the SDK, you must set up the
openWebPaywall
method to handle web paywalls.
Step 2a. Add a web purchase button
If you are using the paywall from the Builder, you need to add a web paywall button. The button will use the link you've provided to track purchases and send the data back to Adapty.
-
Open the paywall and switch to the Builder tab.
-
Click Add element and select Web paywall button.
If you are using a template or an existing/duplicated paywall, add the web paywall button you just added to the previous purchase button. You can set up the web paywall button just as you would the purchase button.

Step 2b. Call the SDK method
If you are working with a paywall you developed yourself, you need to handle web paywalls using the SDK method. The .openWebPaywall
method:
- Generates a unique URL allowing Adapty to link a specific paywall shown to a particular user to the web page they are redirected to.
- Tracks when your users return to the app and then requests
.getProfile
at short intervals to determine whether the profile access rights have been updated.
This way, if the payment has been successful and access rights have been updated, the subscription activates in the app almost immediately.
- Swift
- React Native
- Flutter
do {
try await Adapty.openWebPaywall(for: product)
} catch {
print("Failed to open web paywall: \(error)")
}
try {
await adapty.openWebPaywall(product);
// The web paywall for the product is now opened
} catch (error) {
// handle the error
}
try {
await Adapty().openWebPaywall(product: <YOUR_PRODUCT>);
// The web paywall will be opened
} on AdaptyError catch (adaptyError) {
// handle the error
} catch (e) {
// handle other errors
}
There are two versions of the openWebPaywall
method:
openWebPaywall(product)
that generates URLs by paywall and adds the product data to URLs as well.openWebPaywall(paywall)
that generates URLs by paywall without adding the product data to URLs. Use it when your products in the Adapty paywall differ from those in the web paywall.
Handle errors
Error | Description | Recommended action |
---|---|---|
AdaptyError.paywallWithoutPurchaseUrl | The paywall doesn't have a web purchase URL configured | Check if the paywall has been properly configured in the Adapty Dashboard |
AdaptyError.productWithoutPurchaseUrl | The product doesn't have a web purchase URL | Verify the product configuration in the Adapty Dashboard |
AdaptyError.failedOpeningWebPaywallUrl | Failed to open the URL in the browser | Check device settings or provide an alternative purchase method |
AdaptyError.failedDecodingWebPaywallUrl | Failed to properly encode parameters in the URL | Verify URL parameters are valid and properly formatted |
Implementation example
class SubscriptionViewController: UIViewController {
var paywall: AdaptyPaywall?
@IBAction func purchaseButtonTapped(_ sender: UIButton) {
guard let paywall = paywall, let product = paywall.products.first else { return }
Task {
await offerWebPurchase(for: product)
}
}
func offerWebPurchase(for paywallProduct: AdaptyPaywallProduct) async {
do {
// Attempt to open web paywall
try await Adapty.openWebPaywall(for: product)
} catch let error as AdaptyError {
switch error {
case .paywallWithoutPurchaseUrl, .productWithoutPurchaseUrl:
showAlert(message: "Web purchase is not available for this product.")
case .failedOpeningWebPaywallUrl:
showAlert(message: "Could not open web browser. Please try again.")
default:
showAlert(message: "An error occurred: \(error.localizedDescription)")
}
} catch {
showAlert(message: "An unexpected error occurred.")
}
}
// Helper methods
private func showAlert(message: String) { /* ... */ }
}
After users return to the app, refresh the UI to reflect the profile updates. AdaptyDelegate
will receive and process profile update events.
Step 3. Set up a placement
Since web paywalls are only allowed for iOS apps in the USA, add a separate user segment for the USA and set up a placement to target different paywalls at different segments:
- Create a new segment that will have the following attributes:
- Country from store account: United States
- Platform: iOS and iPadOS
- App version: The latest one that uses our SDK version 3.6.0 or later.
- Create a placement or edit an existing one. Add a new audience with the web paywall and the segment created.