Skip to content

Remove legacy Stack billing entirely (checkout + recognition) - #7662

Merged
lawrencecchen merged 3 commits into
mainfrom
feat-remove-legacy-checkout
Jul 10, 2026
Merged

lawrencecchen merged 3 commits into
mainfrom
feat-remove-legacy-checkout

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jul 8, 2026 •

Copy link
Copy Markdown
Contributor

What

Rip out cmux's legacy Stack Auth-hosted billing path completely — both the hosted checkout AND recognition of legacy Stack subscriptions. After this, a user/team is Pro if and only if they have an active row in the stripe_subscriptions table (direct Stripe billing).

Motivation: the legacy path was already dead in production (both pro and team use direct Stripe when STRIPE_SECRET_KEY is set), but it still surfaced the dead-end "Your subscription is managed by our previous billing system. Contact support…" state for anyone whose subscription lived in Stack's product system.

Changes

  • Delete legacyStackCheckout and the /api/billing/confirm route. GET /api/billing/checkout with Stripe unconfigured now redirects to /pricing?billing=unavailable (no Stack fallback).
  • resolveProPlanStatus: Pro ⇔ active stripe_subscriptions row (user or team scope). Removed hasActiveProSubscription / customer.listProducts recognition. The VM entitlement check in app/api/vm/route.ts follows the same rule.
  • BillingManagementKind drops "external" → "stripe" | "none". The "previous billing system / contact support" message and its keys are removed from services, the plan API, every billing UI (pricing, app-pricing, dashboard billing, pro-welcome-banner, portal), and all four locale catalogs (en/ja/km/th).
  • Removed the now-orphaned legacy Stack reconcile in services/billing/purchase.ts.

Existing legacy-only subscribers become Free — intentional, per product decision; no migration/backfill.

Preserved

Direct-Stripe billing end to end: pro + team checkout, portal, /api/billing/complete, webhooks, and the stripe_subscriptions reconcile are untouched except for removing legacy references.

Verified

  • bun run typecheck clean; full sorted web suite 548 tests, 0 fail (tests assert the new rules behaviorally: a Stack-metadata-only Pro snapshot renders as Free with the upgrade CTA; a stripe_subscriptions row renders as Pro with Manage-billing; unconfigured checkout → /pricing?billing=unavailable).
  • Grep proof: zero remaining references to legacyStackCheckout, /api/billing/confirm, hasActiveProSubscription, billingExternal, or "external" billingManagement.
  • Localization audit: billingExternal removed from all four message catalogs, keys in sync.

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

High Risk
This intentionally revokes Pro for legacy Stack-only subscribers with no data migration, and tightens entitlement checks used at VM create and TestFlight—misconfigured Stripe env can block checkout and downgrade perceived access.

Overview
Removes the legacy Stack Auth billing path end-to-end — hosted checkout, post-purchase confirm polling, and any recognition of Stack listProducts grants.

Checkout requires Stripe: GET /api/billing/checkout no longer falls back to Stack; without Stripe config it redirects to /pricing?billing=unavailable. The /api/billing/confirm route is deleted, and pending-activation UI no longer links there (e.g. pending banner points at /pricing).

Pro and Team status are Stripe-only: resolveProPlanStatus, team plan API, VM create reconcile, and TestFlight eligibility (isTestflightEligible) treat an account as paid only when there is an active row in stripe_subscriptions (metadata may still sync via reconcile). BillingManagementKind drops external; users without a Stripe customer get portal redirects to billing=unavailable instead of “previous billing system” copy (removed from UI and locales).

Product impact: accounts that were Pro only via Stack products or stale cmuxPlan metadata show as Free and see upgrade CTAs; dashboard billing drops the legacy plan section in favor of Free when there is no Stripe subscription row.

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


Summary by cubic

Removed legacy Stack billing so Stripe is the only billing path for Pro and Team. Legacy Stack-only subscribers now render as Free.

  • Refactors

    • Deleted legacyStackCheckout and /api/billing/confirm; /api/billing/checkout now requires Stripe and otherwise redirects to /pricing?billing=unavailable. Plan resolution is Stripe-only; BillingManagementKind is "stripe" | "none". VM entitlements and TestFlight follow the same rule.
    • Billing portal without a Stripe customer redirects to billing=unavailable. App Store builds hide the Pro billing-portal link. Ensure Stripe is configured (STRIPE_SECRET_KEY and price IDs) in all environments.
  • Bug Fixes

    • Restored the post-checkout pending banner for async Stripe confirmation; “Check again” reloads /pricing (no confirm route).
    • /api/billing/plan now reports billingAvailable based on isStripeBillingConfigured().

Written for commit 6cfb25f. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Simplified billing and pricing behavior to show clearer Pro, Free, and billing-unavailable states.
    • Removed outdated pending/external billing messages and links, so users now see only supported billing actions.
    • Billing management for Pro now consistently opens the billing portal when available.
    • Users without an active Stripe subscription now fall back to the Free plan view.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@vercel

vercel Bot commented Jul 8, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jul 10, 2026 1:09am
cmux-staging Building Building Preview, Comment Jul 10, 2026 1:09am

@coderabbitai

coderabbitai Bot commented Jul 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR removes the legacy Stack-hosted checkout/confirm billing flow and the "external" billing-management state entirely, replacing it with Stripe-only checkout, portal, and plan resolution. The checkout route gates on Stripe configuration, the confirm route is deleted, pro.ts drops Stack product-pagination logic in favor of database-backed Stripe subscription checks, UI pages (pricing, app-pricing, dashboard billing) simplify billing-management rendering to Stripe-or-none, and associated translations, comments, and tests are updated accordingly.

Changes

Stripe-only billing migration

Layer / File(s) Summary
Remove confirm flow, simplify welcome banner
web/app/[locale]/components/pro-welcome-banner.tsx, web/app/api/billing/confirm/route.ts
The confirm route is deleted; the welcome banner drops "pending"/"external" states and now only reacts to /api/billing/checkout query params.
Stripe-only checkout route gating
web/app/api/billing/checkout/route.ts, web/tests/billing-checkout-route.test.ts
Checkout redirects to pricing?billing=unavailable when Stripe isn't configured, removes the legacy Stack checkout fallback and isAlreadyGrantedError helper, and repositions CheckoutTeamCustomer; tests cover the new unavailable redirects.
Plan/portal routes drop external state
web/app/api/billing/plan/route.ts, web/app/api/billing/portal/route.ts, web/tests/billing-plan-route.test.ts, web/tests/billing-portal-route.test.ts
resolveTeamPlanStatus no longer derives "external" from metadata; portal redirects to billing=unavailable instead of billing=external; tests assert Free/none outcomes.
Stripe-driven Pro/Team plan resolution
web/services/billing/pro.ts, web/tests/billing-pro.test.ts
Removes hasActiveProSubscription and Stack pagination; introduces plan-id constants and ACTIVE_STRIPE_PRO_STATUSES; narrows BillingManagementKind; reconciliation/status now driven by hasActiveStripeProSubscription.
Pricing/app-pricing UI always links to portal
web/app/[locale]/pricing/page.tsx, web/app/app-pricing/page.tsx, web/tests/pricing-page.test.tsx, web/tests/app-pricing-page.test.tsx
Pro card always shows a portal link instead of conditional Stripe/external UI; billingManagement type narrowed; welcome=pending/billing=external banner branches removed.
Dashboard billing page renders Free plan
web/app/[locale]/dashboard/billing/page.tsx, web/tests/dashboard-billing-page.test.tsx
LegacyPlan component removed; page renders FreePlan when Pro status lacks an active Stripe subscription.
Supporting comments, translations, env/scripts
web/app/api/vm/route.ts, web/app/env.ts, web/app/lib/feature-flags.ts, web/scripts/stripe/dev-reset.sh, web/messages/en.json, web/messages/ja.json
Comments and translation strings updated to reflect Stripe-only reconciliation; removes legacy/external translation keys.
Testflight and misc test cleanup
web/tests/testflight-route.test.ts, web/tests/helpers/testflight-user.ts, web/tests/vm-route-auth.test.ts
Testflight tests mock eligibility via testflightUserEligibility; helper simplifies update stub; stray fixture code removed.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • manaflow-ai/cmux#7143: Both PRs change the Pro pricing/checkout flow around the welcome banner and the confirm-based pending/success handling, with this PR removing the confirm route entirely.
  • manaflow-ai/cmux#7443: This PR always routes Pro users to /api/billing/portal, directly building on the portal endpoint/"Manage billing" behavior introduced there.
  • manaflow-ai/cmux#7506: Both PRs modify web/services/billing/pro.ts around Pro/team plan resolution logic used by isTestflightEligible.

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux Full Internationalization ❌ Error Billing UI/message changes only updated en.json and ja.json, but web/i18n/routing.ts lists 20 locales and the other catalogs weren’t updated. Add matching translated billing keys to every supported locale catalog (or a documented fallback) for all locales in web/i18n/routing.ts.
Description check ⚠️ Warning The description is detailed, but it misses required template sections like Demo Video, Review Trigger, and Checklist. Add the missing Summary/Testing/Demo Video/Review Trigger/Checklist sections, or explicitly mark unavailable items and include the requested links/block.
✅ Passed checks (23 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed No changed files are Swift, so the PR doesn’t introduce production Swift actor-isolation issues.
Cmux Swift Blocking Runtime ✅ Passed The PR commit touches only web TS/JS/JSON/sh files; no Swift files or blocking-runtime primitives were introduced.
Cmux Browser Automation Off-Main ✅ Passed PASS: The PR only changes billing/UI/i18n files; no diff touches browser.* automation routing, mainActor/processV2Command, or worker-lane browser state.
Cmux Expensive Synchronous Load ✅ Passed No .swift files were changed; the diff is web-only, so the Swift expensive-sync-load rule is not applicable.
Cmux Cache Substitution Correctness ✅ Passed The PR still uses direct Stripe subscription rows for plan status; clientReadOnlyMetadata is only synchronized, not trusted as the source of truth.
Cmux No Hacky Sleeps ✅ Passed No new fixed sleeps/polling were introduced; the only timer is the bounded VM reconcile deadline, and the timeout scaffolding is test-only.
Cmux Algorithmic Complexity ✅ Passed No new nested scans or repeated sorting/filtering; changes use fixed-size UI arrays and indexed limit(1) lookups, with no hot-path complexity regression.
Cmux Swift Concurrency ✅ Passed The diff contains only web/tests/scripts files; no cmux Swift code was changed, so the Swift concurrency rule doesn’t apply.
Cmux Swift @Concurrent ✅ Passed The diff contains no Swift files, so the swift-concurrent annotation rule is not applicable.
Cmux Swift File And Package Boundaries ✅ Passed No .swift files are changed in this PR, so the Swift file/package-boundary rule is not applicable.
Cmux Swiftpm Lockfiles ✅ Passed No SwiftPM/Xcode/.gitignore/workflow/dependency files changed in this PR, so the Package.resolved policy isn’t triggered.
Cmux Swift Logging ✅ Passed The diff touches no Swift files, so there are no production Swift logging changes to review.
Cmux User-Facing Error Privacy ✅ Passed Changed user-facing billing copy is generic; vendor/internal details only appear in internal logs/comments, not visible errors or recovery text.
Cmux Swiftui State Layout ✅ Passed The diff only touches TS/JSON/sh billing code and tests; no SwiftUI/AppKit files or state/layout patterns were introduced.
Cmux Architecture Rethink ✅ Passed PASS: The PR only changes web billing routes/UI/tests; the Swift architectural-rethink rule applies to Swift diffs, and no Swift files are touched.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed No Swift files or window-controller code were changed in this PR; the diff is entirely web/backend, so the auxiliary-window shortcut rule is not applicable.
Cmux Source Artifacts ✅ Passed All changed paths are source, tests, scripts, localization, or deletions; no logs, caches, temp dirs, build outputs, or other artifact paths appear.
Cmux No Test Or Debug Seam In Production Source ✅ Passed No production Swift Sources/ files were changed; the rule’s scope is not implicated by this PR.
Cmux No Ambient Global State ✅ Passed PR touches only TS/JSON/sh/test files; no production Swift changes or new ambient singletons/global vars were added.
Title check ✅ Passed The title clearly summarizes the main change: removing legacy Stack billing checkout and entitlement recognition.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-remove-legacy-checkout

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread web/app/[locale]/components/pro-welcome-banner.tsx Outdated
@greptile-apps

greptile-apps Bot commented Jul 8, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR rips out cmux's legacy Stack Auth-hosted billing path entirely — both the hosted checkout (legacyStackCheckout, /api/billing/confirm) and recognition of Stack product grants. After this change, a user or team is Pro if and only if there is an active row in the stripe_subscriptions table.

  • Plan resolution: resolveProPlanStatus and reconcileProPlanMetadata now call only hasActiveStripeProSubscription; the hasActiveProSubscription/listProducts Stack path and activeProSubscriptionState are deleted. BillingManagementKind drops "external", leaving "stripe" | "none".
  • Checkout & routes: /api/billing/checkout now requires Stripe and redirects to ?billing=unavailable when it isn't configured; /api/billing/confirm is deleted. /api/billing/portal redirects no-customer-row cases to ?billing=unavailable instead of ?billing=external. /api/billing/plan now surfaces billingAvailable: isStripeBillingConfigured() rather than hardcoded true.
  • UI & i18n: billingExternal and legacy.body keys removed from all locale files; Stack metadata-only Pro renders as Free with upgrade CTA across pricing, app-pricing, and dashboard billing. App Store distribution now correctly hides the portal link for Pro users (Apple 3.1.1 compliance).

Confidence Score: 5/5

Safe to merge — the cut is clean, all legacy paths are removed consistently across routes, plan resolution, UI, and all locale files.

The billing source of truth is now a single DB query against stripe_subscriptions with no fallback heuristics. The confirm-route polling loop (which was genuinely hacky) is gone. billingAvailable is now correctly derived rather than hardcoded. 548 tests cover the behavioral changes and no test regressions are reported. The only dead code left is the welcome=pending branch in ProWelcomeBanner, which is unreachable and harmless.

No files require special attention. The welcome=pending handling in pro-welcome-banner.tsx is unreachable dead code but does not affect correctness.

Important Files Changed

Filename Overview
web/services/billing/pro.ts Core billing logic simplified to Stripe-only: removed hasActiveProSubscription, ProductsCustomer, and activeProSubscriptionState; resolveProPlanStatus and reconcileProPlanMetadata now rely solely on hasActiveStripeProSubscription
web/app/api/billing/checkout/route.ts Removed legacyStackCheckout and isAlreadyGrantedError; Stripe-unconfigured paths now redirect to billing=unavailable rather than falling back to Stack hosted purchase
web/app/api/billing/portal/route.ts Changed no-customer-row redirect from billing=external to billing=unavailable; pricingRedirect type narrowed to remove "external" variant
web/app/api/billing/plan/route.ts billingAvailable now reflects isStripeBillingConfigured() instead of hardcoded true; team plan resolution drops metadataActive/external path
web/app/api/billing/confirm/route.ts File deleted entirely — the Stack-hosted confirm/polling route is no longer needed with direct Stripe billing
web/app/[locale]/components/pro-welcome-banner.tsx Removed billing=external case; welcome=pending action link updated from /api/billing/confirm to /pricing (dead state can no longer be reached)
web/app/app-pricing/page.tsx Pro billing management now hides portal link in App Store builds (was showing billingExternal message); welcome=pending and billing=external cases removed
web/app/[locale]/pricing/page.tsx Removed billingManagement=external branch; Pro users always see Manage billing portal link since isPro now only true for Stripe subscribers
web/messages/en.json Removed billingExternal and legacy.body keys; welcomePending and welcomePendingAction remain (used by unreachable but still-present welcome=pending banner branch)

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[GET /api/billing/checkout] --> B{Stripe configured?}
    B -- No --> C[Redirect: /pricing?billing=unavailable]
    B -- Yes --> D{plan=pro or team?}
    D -- pro --> E[stripeProCheckout]
    D -- team --> F[stripeTeamCheckout]
    D -- other --> G[Redirect: /pricing?billing=invalid_plan]

    E --> H[Stripe Checkout Session]
    F --> H
    H --> I[GET /api/billing/complete webhook]
    I --> J[stripe_subscriptions row created]

    K[resolveProPlanStatus] --> L{user.id present?}
    L -- Yes --> M[hasActiveStripeProSubscription\nquery stripe_subscriptions]
    L -- No --> N[isPro = false]
    M -- active row --> O[isPro=true, billingManagement=stripe]
    M -- no row --> P[isPro=false, billingManagement=none]

    Q[GET /api/billing/portal] --> R{Stripe customer row?}
    R -- Yes --> S[Stripe Billing Portal]
    R -- No --> T[Redirect: /pricing?billing=unavailable]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[GET /api/billing/checkout] --> B{Stripe configured?}
    B -- No --> C[Redirect: /pricing?billing=unavailable]
    B -- Yes --> D{plan=pro or team?}
    D -- pro --> E[stripeProCheckout]
    D -- team --> F[stripeTeamCheckout]
    D -- other --> G[Redirect: /pricing?billing=invalid_plan]

    E --> H[Stripe Checkout Session]
    F --> H
    H --> I[GET /api/billing/complete webhook]
    I --> J[stripe_subscriptions row created]

    K[resolveProPlanStatus] --> L{user.id present?}
    L -- Yes --> M[hasActiveStripeProSubscription\nquery stripe_subscriptions]
    L -- No --> N[isPro = false]
    M -- active row --> O[isPro=true, billingManagement=stripe]
    M -- no row --> P[isPro=false, billingManagement=none]

    Q[GET /api/billing/portal] --> R{Stripe customer row?}
    R -- Yes --> S[Stripe Billing Portal]
    R -- No --> T[Redirect: /pricing?billing=unavailable]
Loading

Reviews (3): Last reviewed commit: "billing: restore Stripe pending banner s..." | Re-trigger Greptile

cmux migrated from Stack Auth-hosted subscription products to direct
Stripe billing. This rips out the legacy path completely:

- Delete legacyStackCheckout and the /api/billing/confirm return route.
  Checkout with Stripe unconfigured now redirects to
  /pricing?billing=unavailable instead of the Stack hosted flow.
- resolveProPlanStatus: Pro is now true iff there is an active row in
  stripe_subscriptions (user or team scope). Stack product
  subscriptions (customer.listProducts) no longer grant Pro; the VM
  entitlement check follows the same rule.
- BillingManagementKind drops "external"; the "managed by our previous
  billing system, contact support" message and its keys are removed from
  services, the plan API, every billing UI, and all locale catalogs
  (en/ja/km/th).

Existing legacy-only subscribers become Free (intentional; no migration).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lawrencecchen
lawrencecchen force-pushed the feat-remove-legacy-checkout branch from c66cd47 to 4eff884 Compare July 9, 2026 22:50
Comment thread web/app/app-pricing/page.tsx Outdated
The legacy-billing rebase over #7645 dropped the App Store distribution gate
that hid the Stripe /api/billing/portal link for Pro users (Apple 3.1.1 bans
external purchase links in App Store builds). Restore the !appStorePaymentGated
gate and cover the Pro + App Store combination, which no test exercised.
return { planId: FREE_PLAN_ID, billingManagement: "none" };
}
const stripeActive = await hasActiveTeamSubscriptionForTeam(team.id);
const metadataActive = metadataPlanId(team.clientReadOnlyMetadata) === TEAM_PLAN_ID;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Team VM metadata entitlement drift

Medium Severity

Team plan status now depends only on an active stripe_subscriptions row, but Cloud VM billing still treats a team’s clientReadOnlyMetadata.cmuxPlan as authoritative. Legacy Stack-only teams can show as Free in billing APIs and UI while VM create still resolves paid team limits from stale metadata.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 526da14. Configure here.

… Stripe config

Two regressions from removing legacy Stack billing:

- /api/billing/complete (async payment) and /billing/success (subscription
  not yet active due to webhook race) still redirect to /pricing?welcome=pending,
  but the pending branch was dropped from ProWelcomeBanner, leaving paid users
  on a blank page. Restore the welcomePending message + a Check again link that
  reloads /pricing to re-check status (the deleted /api/billing/confirm route is
  not resurrected). Re-add welcomePending/welcomePendingAction to en + ja.

- Checkout now requires Stripe, but /api/billing/plan still reported
  billingAvailable:true whenever Stack was configured, so Stripe-less
  environments advertised an upgrade flow that dead-ends at billing=unavailable.
  Derive billingAvailable from isStripeBillingConfigured() for both the
  anonymous and authenticated responses.

@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 using default effort 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, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6cfb25f. Configure here.

};

export type ActiveStripeSubscriptionQuery = (stackUserId: string) => Promise<boolean>;
export type BillingManagementKind = "stripe" | "external" | "none";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale metadata keeps VM Pro access

High Severity

Pro is now determined only from active stripe_subscriptions rows, but Cloud VM limits still read cmuxPlan / cmuxVmPlan from Stack metadata on most requests. Legacy-only subscribers can keep paid VM entitlements until metadata is cleared by a billing reconcile or plan resolution call.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6cfb25f. Configure here.

@lawrencecchen
lawrencecchen merged commit 571742e into main Jul 10, 2026
29 checks passed
@lawrencecchen
lawrencecchen deleted the feat-remove-legacy-checkout branch July 10, 2026 03:29
lawrencecchen added a commit that referenced this pull request Jul 10, 2026
…O_PRODUCT_ID (#7808)

PR #7757 (account.me) and #7662 (legacy Stack billing removal) were green
independently but conflict once both merged: #7662 deleted PRO_PRODUCT_ID and
made resolveProPlanStatus Stripe-only (no more Stack listProducts), while
#7757's account-me-orpc.test.ts imported PRO_PRODUCT_ID and drove Pro via a fake
Stack product list. The squash-merge didn't re-run CI against the combined tree,
so main's web-typecheck/tests went red.

Rewrite the test for the post-#7662 world: no PRO_PRODUCT_ID, no Stack-product
fake. It drives the real (Stripe-only) resolveProPlanStatus with an id-less user
so the subscription lookup short-circuits to false with no database, and asserts
account.me maps the Free plan + email onto its response, plus the auth gate and
the OpenAPI spec/byte-identity checks. typecheck clean; full web suite green.
@lawrencecchen
lawrencecchen restored the feat-remove-legacy-checkout branch July 18, 2026 10:24

This branch was successfully deployed

1 active deployment
Preview – cmux — 6cfb25f6 Deployed Jul 10, 2026 by vercel[bot]
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.

1 participant