feat(billing): let custom-plan orgs pick a catalog plan
What is this contribution about?
Orgs on the contract (custom) plan saw only "Your plan: Custom. Contact us to change it." in Settings → Plans, with no way to move to a catalog plan themselves.
- Catalog for custom orgs: they now get the same plan cards as everyone else and can subscribe through Stripe checkout. Whether they already have a subscription is read from Stripe, since their gateway plan id says nothing about it. The Custom card is marked as their current plan.
- Invoice option: orgs flagged
invoice_upgrade(and without a live Stripe subscription) also get "Add to my invoice" on Starter/Business.AI_PLAN_INVOICE_UPGRADEno longer requires the AI usage to be exhausted; the flag is the gate. This relaxes the rule added in #7761 and applies to any flagged org. - Staff-managed is now only
ai_service: deco's own plan keeps the contact card. Paywalls now quote the next plan up for custom orgs instead of a bare lock. - The unused
settings.plans.limitReachedstring is removed.
No ai-gateway change needed: Stripe checkout and the webhook move the org through the gateway's admin plan route, which already accepts custom → starter/business and drops the contract's allowance override on the way out.
How did you verify your code works?
bun test apps/api/src/tools/ai-providers/plan-change.test.ts: 7 pass (the test asserting the old usage-exhausted refusal was removed).- Web typecheck,
oxlint, andknippass. - API typecheck fails at
src/mcp-clients/lazy-client.ts:205; the same error reproduces onorigin/mainwithout this change. - Not checked in a browser yet.
Screenshots/Demonstration
Not captured yet.
How to Test
- Put an org on the
customplan through the gateway admin API. - Open Settings → AI providers: the plan catalog shows, with Custom marked as the current plan.
- Choose Starter: it opens Stripe checkout.
- Add the
invoice_upgradefeature override to that org: "Add to my invoice" appears on Starter/Business, and confirming moves the org to that plan.
Review Checklist
- PR title is clear and descriptive
- Changes are tested and working
- Documentation is updated (if needed)
- No breaking changes (invoice upgrades no longer require exhausted usage)
Summary by cubic
Orgs on the contract (custom) plan used to see only "Your plan: Custom. Contact us to change it." in Settings → Plans. They now see the full plan catalog and can move to a catalog plan themselves: subscribing through Stripe checkout, or — when flagged invoice_upgrade — adding the plan to their deco invoice. Only deco's own ai_service plan keeps the contact card.
- The Custom card is marked as the current plan, and subscription state is read from Stripe since the gateway plan id says nothing about it.
- Invoice upgrades are now gated solely by the
invoice_upgradeflag;AI_PLAN_INVOICE_UPGRADEno longer requires exhausted AI usage. - Paywalls now quote the next plan up for custom orgs instead of a bare lock, and the unused
settings.plans.limitReachedstring is removed.