What You’ll Accomplish
By the end of this guide, you’ll have:- A Paddle account connected to Helium, with products synced and ready to add to paywalls.
- Paywalls that offer Paddle products — either on their own or alongside iOS IAPs.
- A hosted web checkout flow that lets users purchase in their browser and deep-links them back into your app.
- Entitlement tracking across Paddle, the Helium SDK, and (optionally) RevenueCat.
- Free trials with built-in abuse protection (one trial per customer, per lifetime).
- A “Manage Subscription” flow that routes users to Paddle’s customer portal.
Prerequisites
Before you start, make sure you have:- A Paddle account. If you don’t have one, start with Paddle’s setup checklist and onboarding overview.
- The Helium SDK (iOS native, React Native/Expo, or Flutter) already integrated in your app.
- A deep link scheme configured in your app — users need a way back into your app after purchase.
- Admin access to your Helium dashboard.
- (Optional) A RevenueCat account, if you want Paddle purchases to reflect in RevenueCat entitlements.
Implementation Steps
1. Set up your Paddle account
- Sign up for Paddle and complete their onboarding checklist (KYC, banking, tax info). Live payments are blocked until this is done.
- Add your products and prices in the Paddle dashboard. Paddle’s product creation guide is the canonical reference.
2. Connect Paddle to Helium
Create an API key in Paddle with the following permissions:
Add the API key to Helium. In the Helium dashboard, go to Settings → Integrations → Paddle and paste your key.
Configure the webhook. Helium will show you a webhook URL — create a matching webhook in Paddle, then copy the webhook’s signing secret back into Helium. This powers revenue reporting and entitlement updates.
3. Default payment link
- Go to Paddle website approval and add a new domain.
- Go to Paddle Checkout Settings and find the Default payment link section. Paste in the same domain you just added for Paddle website approval.
- At the top of the same page, make sure Apple Pay has been selected as a Payment Method
4. Sync your Paddle products
In the Helium dashboard, click Sync Products. Your Paddle products and prices will appear alongside your iOS products and can be added to paywalls.5. (Optional) Add free trials
To offer a free trial on a Paddle product, configure it on the product in Helium. A few things to know:- One trial per customer, ever. Paddle doesn’t natively prevent trial abuse (a user could start a trial, cancel, and start another), so Helium enforces this at the entitlement layer. Once a customer has held a trial — even if they cancel — they won’t be offered another.
- A payment method is required up front. Payment is deferred, not skipped.
6. Build your paywalls
For a short walkthrough, see Build an App2Web paywall. Paddle checkout requires two paywalls:- The original paywall, shown in-app, containing your Paddle products (and optionally iOS products).
- A hosted web paywall, which runs the actual Paddle checkout in the user’s browser on
clickthrough.to.
- Create the original paywall and add Paddle products to it.
- Create a hosted web paywall (the editor will prompt you).
- Link the two. The original paywall can’t be published without a linked hosted web paywall.
7. SDK integration
- iOS (native, Helium 4.6.0+)
- React Native / Expo
- Flutter (Helium 3.3.8+)
Enable external web checkout before initializing Helium:The Set your user ID on Helium before the user reaches a paywall. If you must set user ID after showing a paywall, set the The portal link opens Send that ID to your server and follow Paddle’s guide for generating pre-authenticated customer portal links.
redirectURL is set once, globally. Checkout redirects back to it whether the purchase succeeds, is canceled, or fails. Make sure it’s registered as a deep link in your app (via scheme or universal link).On older SDK versions that don’t have
redirectURL, use the equivalent two-URL form with the same URL for both: enableExternalWebCheckout(successURL:cancelURL:paymentProcessors:). That form still works, but is deprecated.allowWebCheckoutWithoutUserId option to true.Handle the checkout return. Whether checkout succeeds, is canceled, or fails, the user is deep-linked back to your app via the redirect URL. For a smoother handoff, call Helium.shared.handleURL(url) from your onOpenURL / application(_:open:) handler.Add a “Manage Subscription” button so users can cancel or update payment methods via Paddle’s customer portal:paddle.net, where the user enters the email they purchased with, receives a magic link, and lands in the customer portal. This is Paddle’s default, recommended flow: a secure email verification step in front of the portal.Want a pre-authenticated portal link? If you’d rather skip the paddle.net email verification step and drop users straight into the customer portal, generate the link from your backend instead. Look up the user’s Paddle customer ID with Helium:8. (Optional) Connect RevenueCat
If you use RevenueCat for entitlement management:- Create a Paddle web configuration in the RevenueCat dashboard.
- Select your project > Web > Add web provider > Paddle
- Create a new Paddle API Key for RevenueCat. See permissions below
- Paste in the new Paddle API key
- Purchase tracking should be Automatic
- Under App user ID matching select Use a custom field with
rc_user_idas the field key
- Go to Product catalog > Products and Import your Paddle products into RevenueCat.
- Attach those products to desired entitlement(s).
Permissions for Paddle API Key that RevenueCat needs
Permissions for Paddle API Key that RevenueCat needs
- Addresses
READ - Adjustments -
READ - Businesses -
READ - Client-side tokens -
WRITE - Customer portal sessions -
WRITE - Customers -
READ - Discounts -
READ - Notification settings -
WRITE - Notifications -
READ - Payment methods -
READ - Prices -
READ - Products -
READ - Subscriptions -
READ - Transactions -
WRITE
Troubleshooting RevenueCat Entitlements for Paddle Purchases
Troubleshooting RevenueCat Entitlements for Paddle Purchases
- Make sure you have done the above steps and include all of your Paddle products in your RevenueCat entitlements.
- Find your Paddle -> RevenueCat webhook at https://vendors.paddle.com/notifications-v2 and click on it to see that notifications are being sent successfully. If you see errors, you may need to adjust the permissions on the Paddle API Key you provided to RevenueCat.
- If you still have issues, delete the existing Paddle web provider in RevenueCat, revoke the Paddle API Key you created for RevenueCat (leave the Helium one), and run through the above steps again.
Testing
To test without affecting production traffic, create a test targeting group that includes:- Paddle eligible users
sandboxordebugenvironment
- Happy path purchase. Open an App2Web paywall in-app, tap a web product, and confirm the app correctly opens the browser. Complete the standard purchase flow, then confirm you’re deep-linked back with an active entitlement. Dismiss the paywall before purchasing as well, and confirm the app returns to the expected screen.
- Entitlement sync. After purchase, confirm
hasActivePaddleEntitlement()returnstrueimmediately, and that RevenueCat (if connected) updates within a few seconds. - Cancel flow. Open the customer portal from your app, cancel a subscription, and confirm the entitlement expires at period end.
- Canceled checkout. Back out of the checkout page without paying and confirm you’re deep-linked back to the app with no entitlement granted and no success state shown.
- Webhook delivery. In the Paddle dashboard, confirm webhooks are reaching Helium with 2xx responses.
- Pre-login purchase (if applicable). If your App2Web paywall can be shown before you set a durable
user_id, make a purchase before setting it. Set the durable user ID afterwards and confirm the entitlement attaches correctly.
App2Web paywall not appearing? Eligibility is checked server-side against the device’s locale country, App Store storefront country, and IP location — all three must be US, and there is no client-side error when a device is filtered out. If you’re testing from the US, disable VPNs. If you’re testing from outside the US: set your device region to the United States, sign in with a sandbox Apple account on the US storefront, and connect through a US VPN so your IP resolves to the US.
Appendix
How entitlements work
Testing sign-in, access, or RevenueCat delivery? See How to test App2Web entitlements. Helium maps youruser_id to Paddle’s customer_id. Paddle deduplicates customers strictly by email, so two Paddle customers can’t share an address. If the user isn’t logged in at purchase time, we use the helium_persistent_id as a stand-in and reconcile once a real user ID is set.
On successful purchase, the SDK’s entitlement cache updates immediately. The Helium backend processes Paddle’s webhook to update the customer mapping and forward events to your analytics/revenue pipeline. Entitlements are also checked server-side (which ultimately reflects Paddle’s transaction state), so they stay consistent across devices.
Why two paywalls?
The original in-app paywall presents your Paddle offerings. The hosted web paywall — served fromclickthrough.to — runs the actual Paddle checkout, which has to happen in a browser. The two can be customized independently and don’t need to show the same products.
Apple Pay readiness
Web checkout on iOS pays with Apple Pay. A small share of users open it from a browser that cannot complete an Apple Pay payment, and they come back to the app without buying. Apple Pay readiness keeps those users out of web checkout so you can route them to an in-app purchase paywall instead. How it works. On launch, the SDK checks whether Apple Pay can pay on the checkout domain and reports the result to Helium as thewebApplePayReadiness targeting variable. The Paddle A2W Eligible targeting preset excludes users whose result is notReady. Those users no longer match your App2Web audience, so give them an IAP-only rule later in the same workflow, or they get no paywall. Everyone else keeps seeing web checkout, including users on older SDKs and users whose result is unknown.
The result is an optimization signal, not a guarantee, since the user’s Wallet can change between the check and checkout. Presenting a paywall does not wait for it, apart from the first-launch wait described under Launch timing.
Versions. iOS only. Measurement is on by default from Helium iOS 4.13.0, expo-helium 3.6.3, @tryheliumai/paywall-sdk-react-native 3.1.13, and helium_flutter 3.3.14. Earlier versions report ready without measuring, unless you opted in during the beta.
Measurement runs only when external web checkout is already configured when the launch request is sent. Call enableExternalWebCheckout before you initialize Helium, as shown above. If you configure it later, users report unknown:notMeasured and the readiness rule excludes no one.
Turning it off. Set the flag before you initialize Helium. Users then report unknown:notMeasured and keep seeing web checkout.
- iOS (native)
- Expo
- React Native
- Flutter
true. A value fetched after initialization only takes effect on the next launch.
Launch timing. On the first launch with measurement on, after install or after updating to a version that measures by default, the SDK waits up to two seconds for the measurement before it requests your paywall configuration. Later launches send the stored result immediately and re-measure in the background. A launch into the background skips the measurement and the wait, and the SDK measures once the app becomes active.
Apps that declare WKAppBoundDomains. Add bundles.clickthrough.to to that list in your Info.plist. Without it, iOS blocks the check: users report unknown:probeFailed on that first launch and unknown:notMeasured afterwards. Checkout still works and users still see web checkout, but users who are not ready are no longer excluded. Apps that don’t declare WKAppBoundDomains need no change.
Targeting. webApplePayReadiness is ready, notReady, or unknown:<reason> when the measurement could not answer. The Paddle A2W Eligible preset already handles it. If you built a Paddle audience without the preset, add a rule that webApplePayReadiness is not notReady. If an audience uses the standalone Browser Apple Pay ready preset from the beta, keep it unless you also add the Paddle A2W Eligible preset to that audience, since it may be the only readiness rule there.
Known limitations and gotchas
- Availability: Paddle App2Web is currently iOS-only and supported for US storefronts only. iOS-only applies across all SDKs — in React Native and Flutter apps, the web-checkout APIs are safe no-ops on Android, so shared code can ship unchanged and Android users keep your regular in-app purchase flow.
- California compliance for subscription cancellation: guidance coming soon.
- Publish states: both the original paywall and its linked hosted web paywall must be published before the flow works end-to-end.
- Localizations: hosted web paywalls currently support one language.
- Testing: Paywall Previews and SDK requirements cover simulated purchases and real purchases on eligible devices. Before launch, also open the flow through a real trigger: previews bypass targeting and do not exercise paywall traits or post-purchase handlers.