feat(web): take Pro payments on the web billing page, link the mobile paywall to it
Why
The mobile paywall only had a Refresh button, and app.superset.sh/settings/billing told phones to open the desktop app. Building billing into the app is off the table, so the web page becomes the place a phone pays.
What
- Web billing page (
apps/web …/settings/billing): current plan, upgrade with a monthly/yearly switch into Stripe Checkout, cancel and restore, Manage billing into the Stripe portal for owners. Non-owners get "Only an organization owner can change the plan", since the server only authorizes owners. Past-due shows a banner into the portal's payment-method flow. ?organization=<id>: the page makes that organization the web session's active one before rendering. Mobile and web hold separate sessions, so without this the phone could send someone to pay for the wrong organization.- Mobile paywall: one button, "Upgrade on superset.sh". Refresh is gone; the screen refetches the session every 10s while showing and on foreground, so it clears itself after paying. Settings › Manage plan passes the organization id too.
- Sidebar: Billing entry on the web app.
- Shared:
resolveCurrentPlan/isPaidPlanTiermove from the desktop hook to@superset/shared/billingwith their tests. Desktop imports updated, no behaviour change. - i18n: 13 new strings translated in all 16 locales; identical desktop copy reused.
Sharing the desktop billing components was considered and rejected: they are welded to TanStack Link, Electron openUrl, settings search, and the per-window organization header, and packages/ui is deliberately prop-driven. A ~330-line phone-first page is the lighter shape.
Verified
Local stack, API on the Stripe sandbox with stripe listen forwarding webhooks, iOS simulator on Metro, Chrome at 390px and 1440px. Screenshots of every step: https://app.superset.sh/page/pay-for-pro-from-the-phone-d8469q
- Simulator paywall → Safari → sign-in (path and organization preserved) → billing page for the same organization.
- Checkout with a test card → success page → webhook wrote the Pro row → Pro view → portal cancel flow → "Cancels …" + Restore → restored → Manage billing portal.
- Paywall cleared into Home by itself ~10s after the plan flipped, no tap.
- Failed checkout shows a toast;
checkout_started/redirected/failedfire withsource: web_billing.
Not exercised: the past-due banner, and the mobile Settings alert with the id (one-line change).
Worth knowing
- The purchase link now sits on the paywall itself, a more visible form of the App Store 3.1.1 exposure accepted in #7375. Fallback if review objects: gate the button to the US storefront.
- Desktop still shows non-owners a live Upgrade button that fails server-side. Not touched here.
Summary by cubic
Moves Pro payments to the web billing page and links the mobile paywall to it, so phone users can pay without the desktop app.
- The web
settings/billingpage now handles current plan, upgrade (monthly/yearly), cancel/restore, and the Stripe portal for owners; non-owners see a message that only the owner can change the plan. - The page accepts
?organization=<id>to make that org the session's active one, since mobile and web sessions are separate. - The mobile paywall replaces Refresh with a single "Upgrade on superset.sh" button and polls the session every 10s to clear after payment.
resolveCurrentPlanandisPaidPlanTiermoved to@superset/shared/billingwith tests; desktop imports updated with no behavior change.
Summary by CodeRabbit
-
New Features
- Added a web billing experience for managing plans, subscriptions, payment methods, invoices, cancellations, and restorations.
- Added organization-aware billing links and plan management.
- Added Billing navigation to the web sidebar.
- Mobile paywalls now automatically refresh account status and provide organization-specific upgrade links.
- Added payment failure notices and clearer plan eligibility messaging.
-
Improvements
- Expanded billing and upgrade translations across supported languages.
- Replaced desktop-app billing prompts with web-based billing flows.