As the landscape continues to evolve with the Apple v Epic ruling, Superwall continues to respond and offer you the best tools for monetization. We recently announced that Stripe checkout for iOS apps is here, and in this post — I'll walk you through setup.
Get setup with web checkout
Your first stop is to get setup with Superwall's web checkout feature. In addition to getting you ready for app to web flows, this has another benefit, too. Once you've done this, you can present web paywalls. This means you can convert users on your website, via email campaigns and more — all by sending them a web checkout link. This is an important part to drive home, because with all of the recent industry news, it's easy to forget. Superwall offers app to web flows (which is our focus here) and web to app.
Continuing on, the first three steps here are what you'll have to complete to continue:

Once you've done that, you should have a Stripe app to accompany your existing iOS app within Superwall. It'll look something like this in your project:

Notice how there is both a Stripe and iOS app in the screenshot above. That's where you need to be! If you follow those three steps, you will have:
Created a Stripe app in Superwall.
Configured it with your iOS app.
And, will have created Stripe products to offer.
Add a Stripe product to an iOS paywall
Now, you'll either use an existing iOS paywall or create a new one. Then, we'll choose a Stripe product from the product picker and add it to our paywall:

You can easily differentiate between App Store products and Stripe ones in the product selector:
App Store products are prepended with "app-store"
And, Stripe ones will have "stripe-".
Another quick note on Stripe products. Sandbox products include "test:" after the price, whereas production products will say "live:" — you can see the difference here:

After that, you're all set. Optionally, you can choose one of two flows for how this will work. Under "Web Checkout Location", you can choose either:
In App Browser: Here, Stripe checkout opens in a Safari view within your app. It dismisses (along with your paywall) once a transaction completes.
External: It's the same flow, only Superwall opens up the Safari app to complete the transaction. Once it finishes, you're taken back into the app. If you've setup universal links, no redirect page will be shown — your app will open right back up immediately.
Create a campaign for U.S. customers
Since the ruling only applies to customers in the United States, you can easily create a campaign filter that will match to those customers. Just create a filter where deviceLocale
matches en_US
, like this:

Here, I'm using two paywalls. The normal, in-app purchase based paywall and the same paywall which uses Stripe checkout. This will produce quality data, and it's where we recommend you start before testing app to web flows in more places.
Also notice on the left-hand side how I have another audience filter below the web checkout one (called "All Users"). This means that if someone doesn't match my filter for U.S. customers, Superwall will check if they match the next audience below it. In that one, there are no filters — so it acts as a "catch all" audience, ensuring one of my paywalls from that audience filter still shows up.
Test it out
First off, make sure you're on iOS SDK version 4.3.4 or above. Then, as we showed earlier, add a sandbox product to your iOS paywall. From there, it's just a matter of running the app either in the simulator, or on device, and going through the checkout flow. A quick tip — Stripe's test card number is 4242 4242 4242 4242
, so be sure to use that during testing. If you get stuck along the way, check out this doc.
In your iOS app, a few things will happen once a transaction completes:
In your Superwall delegate, two functions are invoked:
willRedeemLink
anddidRedeemLink(result:)
. Those are two great places to update your interface if you need too.Then, Superwall's entitlements will be updated via
func subscriptionStatusDidChange(from oldValue: SubscriptionStatus, to newValue: SubscriptionStatus)
. They are now a paying customer.
Here's an example of how this might look:
@Observable
class SWDelegate: SuperwallDelegate {
enum WebCheckoutStatus: Int, Identifiable {
case redeeming, redeemed, failed, idle
var id: Self { self }
}
private(set) var isPro: Bool = false
func subscriptionStatusDidChange(from oldValue: SubscriptionStatus, to newValue: SubscriptionStatus) {
isPro = newValue.isActive
}
// Web Checkout
func willRedeemLink() {
withAnimation {
self.webCheckoutStatus = .redeeming
}
}
func didRedeemLink(result: RedemptionResult) {
switch result {
case .success(_, _):
withAnimation { self.webCheckoutStatus = .redeemed }
case .error(let code, let error):
print("Error: \(code) - \(error.message)")
case .expiredCode(let code, _):
print("Expired code \(code)")
case .invalidCode(let code):
print("Invalid code: \(code)")
case .expiredSubscription(let code, _):
print("Expired \(code)")
}
}
}
import SuperwallKit
@main
struct Caffeine_PalApp: App {
@State private var delegate: SWDelegate = .init()
init() {
Superwall.configure(apiKey: "YOUR_API_KEY")
Superwall.shared.delegate = delegate
}
var body: some Scene {
WindowGroup {
YourView()
}
}
}
swift
And that's it — now you can offer app to web checkout:

Wrapping up
Offering app to web flows in Superwall with Stripe checkout is basically a matter of simply adding a Stripe product to an iOS paywall! At Superwall, we want flows like this to be as easy as possible to setup from the developer's perspective, and seamless and understandable from a user perspective. We think this flow strikes a wonderful balance on both ends.
Remember, though, you need to test, test, test! Our resident growth expert Nick recommends simply testing your existing paywall with a web checkout variant. See how that performs, and then you can make more decisions from there. Happy monetizing!