Skip to content

Add the checkout web view host - #7623

Open
ajpallares wants to merge 7 commits into
mainfrom
pallares/SDK-4479-checkout-web-view-host
Open

Add the checkout web view host#7623
ajpallares wants to merge 7 commits into
mainfrom
pallares/SDK-4479-checkout-web-view-host

Conversation

@ajpallares

@ajpallares ajpallares commented Sep 4, 2026

Copy link
Copy Markdown
Member

Checklist

  • If applicable, unit tests
  • If applicable, create follow-up issues for purchases-android and hybrids

Motivation

Groundwork for presenting a web checkout in-app. The provider's page offers no JavaScript bridge, so something has to host it and recognise the one signal it gives: a redirect to one of two return URLs (success or cancel).

Description

Nothing is presented yet.

  • Navigation is unrestricted, unlike in the paywall custom component. The checkout URL comes from our backend, and the page it opens legitimately reaches the provider's domains and any bank's 3DS challenge.
  • The return URL is matched on origin and path, and an unrecognised query parameter is reported as a cancel. The caller should confirm the outcome against the backend anyway.
  • target="_blank" reaches WKUIDelegate and goes to the browser.
  • Handling the failure is managed by the caller, so the view only exposes the state and a reload.

Notes

There are no user scripts here: on iOS 15 a WKUserScript disables Apple Pay for every document the web view loads.


Note

Medium Risk
Introduces purchase-adjacent WebKit hosting and return-URL detection logic where mis-matches could end checkout early or mis-report outcome hints, though success is not asserted without backend confirmation and presentation is not hooked up yet.

Overview
Adds groundwork for hosting a provider web checkout in RevenueCatUI on iOS (not wired into paywall presentation yet). New WebCheckoutView / WebCheckoutViewModel own a long-lived WKWebView that can start loading before the sheet appears, show a spinner until first paint, and hide the web content on load failure while exposing load/finished state to the caller.

Return URL handling is centralized in WebCheckoutReturnURL: main-frame navigations to configured success/cancel URLs are cancelled (never loaded), checkout ends, and a WebCheckoutReturnStatus hint is delivered once; ambiguous returns default to cancel. Navigation is otherwise unrestricted (provider domains / 3DS), terminal HTTP errors and process termination mark failure, and target="_blank" HTTPS links open externally. The checkout web view deliberately installs no WKUserScript (Apple Pay on iOS 15).

Also extracts WebViewNavigationFailure.isCancellation for shared filtering of policy-cancelled loads (paywall WebViewComponentView now uses it) and adds dedicated logging strings plus unit tests for return URL matching and view-model behavior.

Reviewed by Cursor Bugbot for commit cd7370b. Bugbot is set up for automated code reviews on this repo. Configure here.

@emerge-tools

emerge-tools Bot commented Sep 4, 2026

Copy link
Copy Markdown

4 builds increased size

Name Version Download Change Install Change Approval
RevenueCat
com.revenuecat.PaywallsTester
1.0 (1) 20.6 MB ⬆️ 46.3 kB (0.23%) 75.2 MB ⬆️ 152.8 kB (0.2%) N/A
BinarySizeTest
com.revenuecat.binary-size-test.local-source
1.0 (1) 5.0 MB ⬆️ 8.1 kB (0.16%) 14.8 MB ⬆️ 19.3 kB (0.13%) N/A
BinarySizeTest
com.revenuecat.binary-size-test.cocoapods
1.0 (1) 7.4 MB ⬆️ 9.9 kB (0.13%) 32.4 MB ⬆️ 40.1 kB (0.12%) ⏳ Needs approval
BinarySizeTest
com.revenuecat.binary-size-test.spm
1.0 (1) 5.0 MB ⬆️ 7.7 kB (0.15%) 12.9 MB ⬆️ 18.6 kB (0.15%) N/A

RevenueCat 1.0 (1)
com.revenuecat.PaywallsTester

⚖️ Compare build
⏱️ Analyze build performance

Total install size change: ⬆️ 152.8 kB (0.2%)
Total download size change: ⬆️ 46.3 kB (0.23%)

Largest size changes

Item Install Size Change
DYLD.String Table ⬆️ 50.6 kB
📝 RevenueCatUI.WebCheckoutViewModel.Objc Metadata ⬆️ 17.8 kB
📝 RevenueCatUI.WebCheckoutViewModel.WebCheckoutViewModel ⬆️ 10.7 kB
Code Signature ⬆️ 4.6 kB
DYLD.Exports ⬆️ 2.9 kB
View Treemap

Image of diff

BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.local-source

⚖️ Compare build
📦 Install build
⏱️ Analyze build performance

Total install size change: ⬆️ 19.3 kB (0.13%)
Total download size change: ⬆️ 8.1 kB (0.16%)

Largest size changes

Item Install Size Change
📝 WKUIDelegate.Objc Metadata ⬆️ 3.2 kB
DYLD.String Table ⬆️ 1.6 kB
📝 RevenueCatUI.WebCheckoutViewModel.webView(decidePolicyFor,decisio... ⬆️ 1.4 kB
📝 RevenueCatUI.WebCheckoutViewModel.webView(createWebViewWith,for,w... ⬆️ 904 B
📝 RevenueCatUI.WebCheckoutViewModel.Objc Metadata ⬆️ 832 B
View Treemap

Image of diff

BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.cocoapods

⚖️ Compare build
📦 Install build
⏱️ Analyze build performance

Total install size change: ⬆️ 40.1 kB (0.12%)
Total download size change: ⬆️ 9.9 kB (0.13%)

Largest size changes

Item Install Size Change
DYLD.String Table ⬆️ 12.4 kB
📝 WKUIDelegate.Objc Metadata ⬆️ 3.2 kB
📝 RevenueCatUI.WebCheckoutViewModel.webView(decidePolicyFor,decisio... ⬆️ 1.6 kB
Code Signature ⬆️ 936 B
📝 RevenueCatUI.WebCheckoutViewModel.Objc Metadata ⬆️ 840 B
View Treemap

Image of diff

BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.spm

⚖️ Compare build
📦 Install build
⏱️ Analyze build performance

Total install size change: ⬆️ 18.6 kB (0.15%)
Total download size change: ⬆️ 7.7 kB (0.15%)

Largest size changes

Item Install Size Change
📝 WKUIDelegate.Objc Metadata ⬆️ 3.1 kB
📝 RevenueCatUI.WebCheckoutViewModel.webView(decidePolicyFor,decisio... ⬆️ 1.4 kB
📝 RevenueCatUI.WebCheckoutViewModel.webView(createWebViewWith,for,w... ⬆️ 904 B
📝 RevenueCatUI.WebCheckoutReturnURL.Target.matches ⬆️ 900 B
📝 RevenueCatUI.WebCheckoutViewModel.Objc Metadata ⬆️ 832 B
View Treemap

Image of diff


🛸 Powered by Emerge Tools

Comment trigger: Size diff threshold of 100.00kB exceeded

@RevenueCat-Danger-Bot

RevenueCat-Danger-Bot commented Sep 4, 2026

Copy link
Copy Markdown

⚠️ Warnings

This PR changes 538 lines of production Swift (limit 300); skipped via skip-pr-lines-changed-check label.

Comment on lines +42 to +43
/// Called once, when the page navigates to the return URL.
var onFinished: ((WebCheckoutReturnStatus) -> Void)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set in #7642

@ajpallares
ajpallares marked this pull request as ready for review September 8, 2026 08:24
@ajpallares
ajpallares requested review from a team as code owners September 8, 2026 08:24
@ajpallares
ajpallares requested review from a team and james-od September 8, 2026 08:24

@rickvdl rickvdl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this looks good!

Comment thread RevenueCatUI/WebCheckout/WebCheckoutViewModel.swift Outdated
@ajpallares
ajpallares force-pushed the pallares/SDK-4479-checkout-web-view-host branch from 7028e64 to a073edb Compare September 8, 2026 13:57

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 81be304. Configure here.

Comment thread RevenueCatUI/WebCheckout/WebCheckoutViewModel.swift
@ajpallares
ajpallares force-pushed the pallares/SDK-4479-checkout-web-view-host branch 2 times, most recently from 77a7cd3 to a86113a Compare September 14, 2026 08:28
ajpallares and others added 7 commits September 14, 2026 12:02
Owns the WKWebView that presents a payment provider's checkout page, detects the
return URL that ends the flow, and reports success or cancellation to its host.

Navigations are not restricted: the checkout URL comes from RevenueCat's backend
and the page it opens is free to reach the provider's own domains and any bank's
3DS challenge.

Co-authored-by: Cursor <cursoragent@cursor.com>
The backend now returns success_url and cancel_url alongside checkout_url, so
the outcome is told by which one the page navigates to rather than by a query
parameter the SDK knows the name of.

Co-authored-by: Cursor <cursoragent@cursor.com>
URL.path strips trailing slashes for every path but the root, so the length
guard was the only thing keeping https://host/ and https://host from matching.

Co-authored-by: Cursor <cursoragent@cursor.com>
Nothing calls it until something presents a retry, so leave it to whichever
change needs it.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
The three booleans tracking the web view's progress could combine into
states that cannot happen, and `hasFinished` guarded only the callback:
a navigation still in flight after the return URL could set `.failed`
and blank the page while the host was dismissing the sheet.

Co-authored-by: Cursor <cursoragent@cursor.com>
Stopping above the home indicator leaves a strip of the host's
background under a page that fills its sheet. The web view insets its
own content by the safe area, so nothing the page puts at the bottom
goes out of reach.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares
ajpallares force-pushed the pallares/SDK-4479-checkout-web-view-host branch from a86113a to cd7370b Compare September 14, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants