Skip to content

Add checkout-only mode for web2app flows#440

Closed
KanishkKundu05 wants to merge 3 commits intosuperwall:developfrom
KanishkKundu05:feature/checkout-only-mode
Closed

Add checkout-only mode for web2app flows#440
KanishkKundu05 wants to merge 3 commits intosuperwall:developfrom
KanishkKundu05:feature/checkout-only-mode

Conversation

@KanishkKundu05
Copy link

@KanishkKundu05 KanishkKundu05 commented Feb 24, 2026

Summary

  • Adds openWebCheckout(forPlacement:) public convenience method for web2app flows that skip the intermediate paywall UI and go directly to the Stripe checkout sheet
  • Adds checkoutOnly option to PaywallOverrides (defaults to false, fully backward compatible)
  • The paywall webview still loads invisibly to generate the checkout URL, while a native loading spinner is shown to the user

File Changes

File Change Reason
PaywallOverrides.swift Added checkoutOnly: Bool property (default false) Opt-in flag for checkout-only mode
PublicPresentation.swift Added openWebCheckout() methods; threaded paywallOverrides through internallyRegister() Public API + plumbing overrides to presentation pipeline
PaywallViewController.swift Added checkout-only UI logic (hidden webview, spinner, auto-dismiss) Implements the invisible-webview + spinner UX
PaywallOverridesTests.swift 5 new unit tests Verifies checkoutOnly defaults and initialization
CHANGELOG.md Added entry Documents the new feature
docs/checkout-only-mode.md New documentation file Usage guide with code examples and architecture explanation

How It Works

  1. User calls Superwall.shared.openWebCheckout(forPlacement: "placement")
  2. The paywall loads invisibly (webview hidden, native spinner shown)
  3. Paywall JS fires openPaymentSheet → spinner removed, Stripe checkout sheet presented
  4. On checkout dismiss: if cancelled, invisible paywall auto-dismissed; if purchased, normal flow continues

Dashboard Requirement

The paywall must be configured to auto-trigger the purchase action on page load via custom JS in the paywall editor.

Test plan

  • Build passes (xcodebuild build)
  • Lint passes (scripts/lint.sh)
  • All 5 new unit tests pass
  • Manual test: Call openWebCheckout(forPlacement:) with an auto-trigger paywall and verify no intermediate paywall is shown

🤖 Generated with Claude Code

Greptile Summary

This PR introduces checkout-only mode for web2app flows, allowing users to skip the intermediate paywall UI and go directly to the Stripe checkout sheet.

  • Added openWebCheckout(forPlacement:) public convenience methods (with and without feature gates)
  • Added checkoutOnly: Bool property to PaywallOverrides (defaults to false for full backward compatibility)
  • Implemented invisible webview loading with native spinner UI in PaywallViewController
  • Auto-dismisses paywall when checkout is cancelled in checkout-only mode
  • Properly handles webview failures by cleaning up spinner and dismissing
  • Includes 5 unit tests verifying default behavior and initialization
  • Well-documented in CHANGELOG and comprehensive docs file

The implementation is clean and follows the existing architectural patterns. All initializers properly default checkoutOnly to false, ensuring no breaking changes.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The implementation is well-designed with proper backward compatibility (all defaults set correctly), comprehensive test coverage for the new property, clear documentation, and follows existing architectural patterns. The checkout-only mode is properly isolated with early returns to prevent interference with normal flows. Edge cases like webview failures and checkout cancellations are handled correctly.
  • No files require special attention

Important Files Changed

Filename Overview
Sources/SuperwallKit/Paywall/Presentation/Internal/Presentation Request/PaywallOverrides.swift Added checkoutOnly boolean property with proper defaults across all initializers
Sources/SuperwallKit/Paywall/Presentation/PublicPresentation.swift Added two openWebCheckout() convenience methods and threaded paywallOverrides parameter through
Sources/SuperwallKit/Paywall/View Controller/PaywallViewController.swift Implemented checkout-only mode with invisible webview, native spinner, and auto-dismiss logic

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User calls openWebCheckout] --> B[Create PaywallOverrides with checkoutOnly=true]
    B --> C[Paywall presents]
    C --> D{isCheckoutOnly?}
    D -->|Yes| E[setupCheckoutOnlyMode]
    D -->|No| F[Normal paywall flow]
    E --> G[webView.alpha = 0]
    E --> H[Show native spinner]
    E --> I[Hide exit/refresh buttons]
    G --> J[Webview loads invisibly]
    H --> J
    I --> J
    J --> K{Webview success?}
    K -->|No| L[handleWebViewFailure]
    L --> M[removeCheckoutOnlySpinner]
    M --> N[Dismiss paywall]
    K -->|Yes| O[JS fires openPaymentSheet]
    O --> P[removeCheckoutOnlySpinner]
    P --> Q[Show Stripe checkout sheet]
    Q --> R{User action?}
    R -->|Purchase| S[Normal purchase flow]
    R -->|Cancel| T[Auto-dismiss paywall]
    S --> U[didRedeemSucceedDuringCheckout=true]
    U --> V[Keep paywall for completion]
Loading

Last reviewed commit: 16a344e

(5/5) You can turn off certain types of comments like style here!

KanishkKundu05 and others added 3 commits February 24, 2026 17:08
Adds `openWebCheckout(forPlacement:)` convenience method and `checkoutOnly`
option on `PaywallOverrides` that skips the intermediate paywall UI and
presents only the Stripe checkout sheet. The paywall webview still loads
invisibly to generate the checkout URL, while a native loading spinner is
shown to the user.

This is fully opt-in and backward compatible — all existing behavior is
unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In checkout-only mode the Stripe checkout sheet is the only visible content,
so present it as .overFullScreen without animation instead of as a half-sheet
sliding over an invisible paywall.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds step-by-step setup instructions, dashboard JS example, error handling
patterns, and describes the full user-visible experience.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@yusuftor
Copy link
Collaborator

Closing as this is resolved on Slack

@yusuftor yusuftor closed this Feb 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants