Skip to content

fix(billing): point self-hosted upgrade CTAs at the hosted app - #6003

Open
TheodoreSpeaks wants to merge 1 commit into
stagingfrom
fix/self-hosted-upgrade
Open

fix(billing): point self-hosted upgrade CTAs at the hosted app#6003
TheodoreSpeaks wants to merge 1 commit into
stagingfrom
fix/self-hosted-upgrade

Conversation

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator

Summary

  • Self-hosted Chat bills against the sim.ai account behind COPILOT_API_KEY, but the 402 upgrade card linked to local billing settings a self-hosted deployment doesn't have. Those CTAs now point at the hosted app, and no longer apply the local workspace-role gate — which could hide the CTA from the only person able to act on it, since local roles say nothing about the hosted account.
  • Adds /upgrade, an account-scoped entry for callers that can't know a workspace id (a self-hosted deployment, an email). It delegates to /workspace?redirect=upgrade instead of re-deriving workspace resolution, so it inherits local recency, stale-session recovery, and the no-workspace creation policy.
  • /workspace/[workspaceId]/upgrade resolves the self-hosted and billing-disabled cases in the Server Component. Both are build-time constants, so the old client-effect redirect made every self-hosted visitor mount the full plan-picker graph just to navigate away.
  • External link now uses SquareArrowUpRight + an aria-label, so a self-hoster gets a signal they're leaving their deployment.

Type of Change

  • Bug fix

Testing

type-check 19/19, all 11 CI audits pass, 42 tests green across the touched files. Redirect chain verification in progress locally.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Self-hosted Chat bills against the sim.ai account behind COPILOT_API_KEY, but
its 402 upgrade card linked to local billing settings that a self-hosted
deployment does not have. Point those CTAs at the hosted app instead, and drop
the local workspace-role gate that could hide the CTA from the only person able
to act on it.

Adds /upgrade, an account-scoped entry for callers that cannot know a workspace
id. It delegates to /workspace?redirect=upgrade rather than re-deriving workspace
resolution, inheriting local recency, stale-session recovery, and the
no-workspace creation policy.
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jul 28, 2026 4:01pm

Request Review

@cursor

cursor Bot commented Jul 28, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes billing and upgrade navigation across hosted vs self-hosted builds and login redirect chains; mistakes could send users to wrong destinations or drop upgrade intent, but scope is routing/CTA logic rather than payment processing.

Overview
Self-hosted Chat bills against the user’s sim.ai account, but usage-limit CTAs still sent people to local billing settings and could hide the link behind workspace roles that don’t apply on the hosted side.

Usage upgrade cards on self-hosted now open hosted URLs (buildHostedUpgradeUrl / HOSTED_BILLING_SETTINGS_URL) in a new tab with external-link affordances, and the billing-permission gate only runs when isHosted is true so the CTA stays visible for whoever can act on the hosted account.

A new account-scoped /upgrade route preserves optional reason, sends signed-out users to login with callbackUrl, and delegates workspace picking to /workspace?redirect=upgrade, which resolves the user’s workspace and routes to buildUpgradeHref. The workspace /upgrade page now server-redirects self-hosted visitors to the hosted upgrade URL and skips mounting the plan picker when billing is disabled; the client upgrade UI drops the old reactive redirects for those build-time cases.

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

Comment on lines +1064 to +1066
: data.action === 'upgrade_plan'
? buildHostedUpgradeUrl()
: HOSTED_BILLING_SETTINGS_URL

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.

P2 Upgrade CTA drops reason param

On self-hosted, the upgrade_plan CTA calls buildHostedUpgradeUrl() without data.reason, so the hosted /upgrade page never receives the reason query used for limit-specific header copy; other upgrade paths forward reason through buildUpgradeHref / buildHostedUpgradeUrl.

Suggested change
: data.action === 'upgrade_plan'
? buildHostedUpgradeUrl()
: HOSTED_BILLING_SETTINGS_URL
: data.action === 'upgrade_plan'
? buildHostedUpgradeUrl(isUpgradeReason(data.reason) ? data.reason : undefined)
: HOSTED_BILLING_SETTINGS_URL

@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Points self-hosted usage-upgrade CTAs at the hosted app, adds an account-scoped /upgrade entry that delegates through /workspace?redirect=upgrade, and moves self-hosted/billing-disabled upgrade redirects into the Server Component so the plan picker never mounts just to navigate away.

  • Self-hosted chat upgrade/limit CTAs open hosted sim.ai URLs and skip the local workspace billing role gate
  • New /upgrade preserves reason and login callback, then resolves workspace via existing /workspace logic
  • Workspace upgrade page redirects externally when not hosted, or home when billing is disabled, before mounting the plan picker

Confidence Score: 4/5

Safe to merge from a blocking-defect perspective; only a small self-hosted CTA UX gap remains (upgrade reason not forwarded).

Redirect chain, login callback encoding, billing settings path, and Next external redirect behavior check out; the remaining issue is that self-hosted Upgrade Plan links drop the tag reason query used for header copy.

Files Needing Attention: apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx

Important Files Changed

Filename Overview
apps/sim/app/upgrade/page.tsx New account-scoped upgrade entry preserves reason and login callback, then delegates workspace resolution to /workspace.
apps/sim/app/workspace/[workspaceId]/upgrade/page.tsx Server-side redirects for self-hosted (external hosted upgrade) and billing-disabled (home) before mounting Upgrade.
apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx Self-hosted CTAs link off-site and skip local billing roles; upgrade_plan omits reason on the hosted URL.
apps/sim/app/workspace/page.tsx Handles ?redirect=upgrade after workspace selection and forwards optional reason into buildUpgradeHref.
apps/sim/lib/billing/upgrade-reasons.ts Adds SITE_URL-based hosted upgrade and billing settings URL helpers with tests.

Sequence Diagram

sequenceDiagram
  participant SH as Self-hosted UI
  participant Local as Local Next app
  participant Hosted as www.sim.ai
  Note over SH: usage_upgrade CTA
  SH->>Hosted: open /upgrade or /account/settings/billing
  Note over Local: /workspace/.../upgrade when !isHosted
  Local->>Hosted: redirect(buildHostedUpgradeUrl)
  Note over Hosted: /upgrade entry
  Hosted->>Hosted: "login callback or /workspace?redirect=upgrade"
  Hosted->>Hosted: resolve workspace then plan picker
Loading

Reviews (1): Last reviewed commit: "fix(billing): point self-hosted upgrade ..." | Re-trigger Greptile

@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 high effort and found 1 potential issue.

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 e1ac4a8. Configure here.

? getSettingsHref({ section: 'billing' })
: data.action === 'upgrade_plan'
? buildHostedUpgradeUrl()
: HOSTED_BILLING_SETTINGS_URL

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Billing CTA drops login destination

Medium Severity

Self-hosted increase_limit CTAs open hosted /account/settings/billing directly. That route sends signed-out visitors to /login with no callbackUrl, so after auth they land on the default workspace instead of billing — unlike the /upgrade path, which was written to preserve intent.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e1ac4a8. Configure here.

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