Part of the plan-limit upgrade lifecycle epic (#4635). This issue is the self-serve upgrade mechanism — the "way to upgrade" a customer needs once they're notified they're over their plan. (Detection + notification is a sibling issue; see the epic.)
Consent constraint: we must NOT auto-move a customer to a higher-priced plan — that charges them without consent. The customer must choose to upgrade (or reduce their traffic). So this flow is customer-initiated.
Current state (verified in code)
- Checkout hard-blocks an active subscriber.
convex/payments/checkout.ts returns ACTIVE_SUBSCRIPTION_EXISTS → "use Manage Billing to update it." No upgrade/plan-change branch.
- "Manage Billing" = a default Dodo customer-portal session.
convex/payments/billing.ts:114 → customerPortal.create(id, { send_email:false }) — no plan-change params. Plan-switching is Dodo-dashboard config, not enabled/handled in code.
- In-app "Upgrade" CTAs are all Free → Pro (
DEFAULT_UPGRADE_PRODUCT = PRO_MONTHLY). No Starter → Business (API-tier) upgrade CTA.
- Business isn't self-serve (
selfServe:false, publicVisible:false, currentForCheckout:false); no outbound Dodo subscription-update/proration call exists (only inbound webhook).
Options (cheapest → most complete; decide in planning)
- (a) Portal route: enable plan-switching in the Dodo customer portal + surface a "Change plan" entry point in-app. Verify Dodo supports it for this account.
- (b) In-app upgrade flow: new action calling Dodo's subscription change/proration API + a "Change plan" UI on /pro and in-dashboard.
- (c) Checkout upgrade: make Business self-serve and have checkout perform a plan-change (proration) instead of hard-blocking active subs.
Impact
Blocks the #3199 enforce rollout (over-cap Starter customers have no consent-based path to Business) and is a standing revenue-capture gap (customers who outgrow Starter can't upgrade in-product). Business is unreachable via checkout at all.
Part of the plan-limit upgrade lifecycle epic (#4635). This issue is the self-serve upgrade mechanism — the "way to upgrade" a customer needs once they're notified they're over their plan. (Detection + notification is a sibling issue; see the epic.)
Consent constraint: we must NOT auto-move a customer to a higher-priced plan — that charges them without consent. The customer must choose to upgrade (or reduce their traffic). So this flow is customer-initiated.
Current state (verified in code)
convex/payments/checkout.tsreturnsACTIVE_SUBSCRIPTION_EXISTS→ "use Manage Billing to update it." No upgrade/plan-change branch.convex/payments/billing.ts:114→customerPortal.create(id, { send_email:false })— no plan-change params. Plan-switching is Dodo-dashboard config, not enabled/handled in code.DEFAULT_UPGRADE_PRODUCT = PRO_MONTHLY). No Starter → Business (API-tier) upgrade CTA.selfServe:false, publicVisible:false, currentForCheckout:false); no outbound Dodo subscription-update/proration call exists (only inbound webhook).Options (cheapest → most complete; decide in planning)
Impact
Blocks the #3199 enforce rollout (over-cap Starter customers have no consent-based path to Business) and is a standing revenue-capture gap (customers who outgrow Starter can't upgrade in-product). Business is unreachable via checkout at all.