One plan per organization, a 25-seat Enterprise ceiling, and a way to stop paying

FeatureBillingService
Ya
12 Agosti 2026, 02:11 UTC
Mwandishi
Kamo
Ahadi ya
41a1a14

The plan is now an organization-level decision and nothing else can override it. Letting a self-payer or a group manager pick their own reads like a kindness and is a correctness bug: **************** answers "does this org have this app" by OR-ing across every live subscription targeting the org, so one person on one seat choosing a higher plan switched that plan's apps on for the entire organization. Pinning every payer to the organization's plan makes that OR harmless — the subscriptions all name the same plan, so whichever wins answers identically. OrgPlanService reads the plan from the same subscription the Plans & billing console calls "your plan", so the rule and the screen cannot disagree. Enterprise is required above 25 seats, counted across the organization and every billing group. Counting per-payer would have made splitting into groups a way to buy fifty seats twenty-five at a time. An edit excludes its own subscription so shrinking is measured against the new size, not the old. GET /self/alerts drives a home-page banner. Restricted access with no explanation is the worst version of this product — apps stop opening and nobody says why, or that the person reading is the one who can fix it. Only raised for bills this member is responsible for: their own seat and groups they manage. Telling someone their organization's card failed when they cannot pay it is noise, and noise is how banners stop being read. POST /self/seat/cancel stops the automatic payment on a seat someone bought themselves. Always at period end: they have paid for the current period, and cancelling immediately would take that access away while refunding nothing, since that path asks Stripe for no proration. Two bugs in cancel() found while wiring that up: - It was a silent no-op for COMP and TRIAL. The whole body sits behind a stripeSubscriptionId null check, so a comped or trialing subscription returned 200 with the row still ACTIVE — the caller told it worked when nothing changed. It now refuses and says why. - immediate=true remains dangerous and is deliberately not exposed: it sets CANCELED then calls fallbackToFree, which sets ACTIVE/FREE with seatCount 1, so a 25-seat org would keep one working person and tell 24 their seat was cut for a seat-count reason that never happened. entitlement.fail-open is set false here to match SecurityService, which serves /api/security/features and is what the Apps & features screen reads. Two services answering the same entitlement question differently is worse than either answer. Verified safe: all six active plans carry an explicit row for all 21 service types, so nothing previously granted is being denied.

Mabadiliko yote

Je, unaona nini kuhusu usafiri?

Kila moja ya hizi updates ardhi katika nafasi yako ya kazi moja kwa moja. Kuanza bure na kuangalia kukua wiki baada ya wiki.

Kuwa Huru MileleMtazamo wa bei