Skip to content

feat(paywalls): render web_view component (PWENG-98)#3651

Draft
alexrepty wants to merge 1 commit into
alexrepty/paywalls-web-view-schemafrom
alexrepty/paywalls-web-view-rendering
Draft

feat(paywalls): render web_view component (PWENG-98)#3651
alexrepty wants to merge 1 commit into
alexrepty/paywalls-web-view-schemafrom
alexrepty/paywalls-web-view-rendering

Conversation

@alexrepty

@alexrepty alexrepty commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Checklist

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

Motivation

With the web_view schema in place, the component needs to actually render its hosted content in a Paywalls V2 paywall.

Part of PWENG-98.

Description

  • Adds WebViewComponentView (a WebView hosted via AndroidView), WebViewComponentStyle, the StyleFactory mapping, and ComponentView dispatch.
  • Adds WebViewUrlResolver: resolves the URL template against paywall variables natively, then validates it is an HTTPS URL with a host. If it does not resolve, the component renders nothing (availability is gated by SDK version on the frontend).
  • No bridge or CSP yet — those land in later PRs.
  • Tested by StyleFactoryTests and WebViewUrlResolverTest.

iOS parity: mirrors purchases-ios (web-view-bridge-wiring).
Stack (merge bottom-up): schema → rendering → CSP → value types → message model → variables provider → JS bridge → wiring.


Note

Medium Risk
Introduces WebView with JavaScript enabled and external URL loading in the paywall surface; mitigations are HTTPS-only validation and navigation blocking, but CSP/bridge are still out of scope for this PR.

Overview
Paywalls V2 web_view components now render hosted HTTPS content instead of being dropped during style creation.

Style pipeline: StyleFactory maps WebViewComponent to WebViewComponentStyle (URL template, visibility, size), and ComponentView dispatches to the new composable.

Rendering: WebViewComponentView hosts an AndroidView WebView that resolves the URL via WebViewUrlResolver (paywall variables + locale), accepts only HTTPS URLs with a host, and renders nothing on failure. The WebView is keyed on the resolved URL so in-page navigation is not reset on recomposition; locale changes still refresh locale-dependent templates. Settings restrict file/content access, disallow mixed content, enable JS/DOM storage, and block non-HTTPS navigations in shouldOverrideUrlLoading.

Tests: StyleFactoryTests for style mapping and WebViewUrlResolverTest for URL resolution and rejection of non-HTTPS/malformed URLs.

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

@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.48%. Comparing base (40615bf) to head (4fb9844).

Additional details and impacted files
@@                         Coverage Diff                         @@
##           alexrepty/paywalls-web-view-schema    #3651   +/-   ##
===================================================================
  Coverage                               80.48%   80.48%           
===================================================================
  Files                                     404      404           
  Lines                                   16728    16728           
  Branches                                 2389     2389           
===================================================================
  Hits                                    13464    13464           
  Misses                                   2322     2322           
  Partials                                  942      942           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@alexrepty alexrepty force-pushed the alexrepty/paywalls-web-view-rendering branch from 38f5302 to 6c73e7c Compare June 26, 2026 14:20
@alexrepty alexrepty force-pushed the alexrepty/paywalls-web-view-schema branch from 9e93b6c to b911095 Compare June 26, 2026 14:20
@alexrepty alexrepty force-pushed the alexrepty/paywalls-web-view-rendering branch from 6c73e7c to 71e76e1 Compare June 26, 2026 14:26
@alexrepty alexrepty changed the title feat(paywalls): render web_view component feat(paywalls): render web_view component (PWENG-98) Jun 29, 2026
@alexrepty alexrepty force-pushed the alexrepty/paywalls-web-view-schema branch from b911095 to dc40011 Compare June 29, 2026 13:58
@alexrepty alexrepty force-pushed the alexrepty/paywalls-web-view-rendering branch 2 times, most recently from 88cb259 to fd0371b Compare June 29, 2026 14:14
@alexrepty alexrepty force-pushed the alexrepty/paywalls-web-view-schema branch from dc40011 to 003b8ee Compare June 29, 2026 14:14
@alexrepty alexrepty marked this pull request as ready for review June 29, 2026 14:42
@alexrepty alexrepty requested a review from a team as a code owner June 29, 2026 14:42
@alexrepty alexrepty force-pushed the alexrepty/paywalls-web-view-schema branch from 003b8ee to fa1fcdf Compare June 30, 2026 07:44
@alexrepty alexrepty force-pushed the alexrepty/paywalls-web-view-rendering branch from fd0371b to bc0f4e0 Compare June 30, 2026 07:44
dateLocale = locale,
customVariables = customVariables,
defaultCustomVariables = defaultCustomVariables,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

WebView URL variable resolution incomplete

Medium Severity

The PR resolves web_view URL templates against paywall variables, but WebViewUrlResolver only passes custom variables and locale into VariableProcessorV2, omitting selected package, subscription option, variableDataProvider, currency locale, and date context. createWebViewComponentStyle also does not capture enclosing rcPackage like text/video components. WebViewComponentView caches the URL in remember keyed by template, state identity, and locale, so it does not refresh when the user changes the selected package.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit bc0f4e0. Configure here.

@emerge-tools

emerge-tools Bot commented Jun 30, 2026

Copy link
Copy Markdown

📸 Snapshot Test

593 unchanged

Name Added Removed Modified Renamed Unchanged Errored Approval
TestPurchasesUIAndroidCompatibility
com.revenuecat.testpurchasesuiandroidcompatibility
0 0 0 0 335 0 N/A
TestPurchasesUIAndroidCompatibility Paparazzi
com.revenuecat.testpurchasesuiandroidcompatibility.paparazzi
0 0 0 0 258 0 N/A

🛸 Powered by Emerge Tools

@AlvaroBrey AlvaroBrey force-pushed the alexrepty/paywalls-web-view-schema branch from fa1fcdf to a46325f Compare July 6, 2026 08:12
@AlvaroBrey AlvaroBrey force-pushed the alexrepty/paywalls-web-view-rendering branch from bc0f4e0 to 2634d3b Compare July 6, 2026 08:12
@AlvaroBrey AlvaroBrey self-assigned this Jul 6, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

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.

There are 2 total unresolved issues (including 1 from previous review).

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 2634d3b. Configure here.

// URL. We deliberately do NOT reload on every recomposition: in-page navigation changes WebView.url,
// and reloading whenever it differs from resolvedUrl would reset a multi-step web flow. The WebView
// is only recreated when the SDK-resolved URL itself changes (e.g. a locale-dependent template).
key(resolvedUrl) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

java.net.URL as Compose key triggers blocking DNS

Medium Severity

key(resolvedUrl) uses a java.net.URL object as a Compose key. URL.equals() and URL.hashCode() perform blocking DNS resolution on the calling thread. Compose's key() calls equals() on every recomposition to check whether the composition identity changed, causing a synchronous network operation on the main/UI thread. This is a well-known Java pitfall flagged by Google's ErrorProne checker (URLEqualsHashCode). Using resolvedUrl.toString() as the key (or returning a URI/String from WebViewUrlResolver) avoids the blocking call entirely.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2634d3b. Configure here.

Adds the Compose rendering path for the Paywalls V2 `web_view` component: a `WebView`-backed `WebViewComponentView`, the `WebViewComponentStyle`, the `StyleFactory` mapping, `ComponentView` dispatch, and `WebViewUrlResolver` (template resolution + HTTPS/host validation). When the URL is missing or invalid the component renders its fallback stack.

No CSP, messaging, or pre-warming yet.

Recommended labels: pr:other, pr:RevenueCatUI, feat:Paywalls_V2
@AlvaroBrey AlvaroBrey force-pushed the alexrepty/paywalls-web-view-rendering branch from 2634d3b to 4fb9844 Compare July 6, 2026 13:57
@AlvaroBrey AlvaroBrey force-pushed the alexrepty/paywalls-web-view-schema branch from a46325f to 40615bf Compare July 6, 2026 13:57
@AlvaroBrey AlvaroBrey marked this pull request as draft July 6, 2026 14:42
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.

2 participants