A member cannot cancel a subscription while an operator's grant holds its ladder, and the Products page shows no price for it while it bills #269

Open
opened 2026-10-26 20:58:21 +00:00 by cgalo5758 · 2 comments
Owner

What happens

Some terms first. A plan ladder is a ranked sequence of tiers, such as Plus, Pro and Max; an organization holds one tier per ladder at a time. A grant is an operator's record that gives an organization a product without payment. An operator can issue a grant over a tier the organization pays for through a subscription. The grant supersedes (ends and replaces) the subscription's tier and holds the ladder until it ends or is revoked, while the subscription keeps billing in Stripe and delivers nothing on that ladder (docs/stripe.md:214).

The member then has no way to stop that subscription in the console:

  • Every plan move, Cancel plan included, finds the subscription through the tier the organization holds on the ladder. That tier is the grant's, which no subscription backs, so the move answers that there is no active paid subscription on the ladder (resolveLadderSubscription, internal/fulfillment/plan_change.go:770-850, the refusal at :801-804; CancelSubscription resolves through it at :613-619).
  • The Products page reads the ladder as one no subscription pays for (internal/server/plan_offer.go:142-145). The grant's card shows no price line, because the price is read from the subscription behind the held tier and there is none (resolveCurrentPlanBilling, internal/server/member_plans.go:759-762), and Cancel plan is drawn only on a card with a price line (:451-466). No move is offered below a tier no subscription pays for, since a member cannot end a grant (rule 12 of plans.Offer, internal/plans/offer.go:179-180, code at :270).
  • Stripe keeps invoicing, and the Billing page lists each invoice.

The cancellation requirement promises a member-initiated cancellation of a live paid subscription, active or suspended (openspec/specs/stripe-subscription-management/spec.md:169-171). The comment on resolveCurrentPlanBilling says a tier an operator issued as a grant genuinely has no billing (internal/server/member_plans.go:750-753), which is not true while such a subscription bills. Only the operator's organization page says the subscription delivers nothing on that ladder.

What should happen

The member can see that the organization pays for a subscription that delivers nothing while the grant holds the ladder, and can cancel it from the console. Where that control sits and what the page says about the subscription is the product decision this issue waits on.

Where

internal/fulfillment/plan_change.go:770-850 (resolveLadderSubscription); internal/server/plan_offer.go:142-145; internal/server/member_plans.go:451-466, :750-762.

Steps

  1. An organization pays for Plus through a subscription.
  2. An operator issues a Pro grant on the same ladder. Pro replaces Plus.
  3. The member opens Products: Pro is the current plan, with no price and no Cancel plan, and nothing on the page mentions the Plus subscription.
  4. At renewal Stripe invoices for Plus, and the Billing page lists the invoice.

Why it matters

A member pays every period for a plan that delivers nothing, and cannot stop paying without asking an operator to cancel the subscription in Stripe.

### What happens Some terms first. A plan ladder is a ranked sequence of tiers, such as Plus, Pro and Max; an organization holds one tier per ladder at a time. A grant is an operator's record that gives an organization a product without payment. An operator can issue a grant over a tier the organization pays for through a subscription. The grant supersedes (ends and replaces) the subscription's tier and holds the ladder until it ends or is revoked, while the subscription keeps billing in Stripe and delivers nothing on that ladder (`docs/stripe.md:214`). The member then has no way to stop that subscription in the console: - Every plan move, Cancel plan included, finds the subscription through the tier the organization holds on the ladder. That tier is the grant's, which no subscription backs, so the move answers that there is no active paid subscription on the ladder (`resolveLadderSubscription`, `internal/fulfillment/plan_change.go:770-850`, the refusal at `:801-804`; `CancelSubscription` resolves through it at `:613-619`). - The Products page reads the ladder as one no subscription pays for (`internal/server/plan_offer.go:142-145`). The grant's card shows no price line, because the price is read from the subscription behind the held tier and there is none (`resolveCurrentPlanBilling`, `internal/server/member_plans.go:759-762`), and Cancel plan is drawn only on a card with a price line (`:451-466`). No move is offered below a tier no subscription pays for, since a member cannot end a grant (rule 12 of `plans.Offer`, `internal/plans/offer.go:179-180`, code at `:270`). - Stripe keeps invoicing, and the Billing page lists each invoice. The cancellation requirement promises a member-initiated cancellation of a live paid subscription, active or suspended (`openspec/specs/stripe-subscription-management/spec.md:169-171`). The comment on `resolveCurrentPlanBilling` says a tier an operator issued as a grant genuinely has no billing (`internal/server/member_plans.go:750-753`), which is not true while such a subscription bills. Only the operator's organization page says the subscription delivers nothing on that ladder. ### What should happen The member can see that the organization pays for a subscription that delivers nothing while the grant holds the ladder, and can cancel it from the console. Where that control sits and what the page says about the subscription is the product decision this issue waits on. ### Where `internal/fulfillment/plan_change.go:770-850` (`resolveLadderSubscription`); `internal/server/plan_offer.go:142-145`; `internal/server/member_plans.go:451-466`, `:750-762`. ### Steps 1. An organization pays for Plus through a subscription. 2. An operator issues a Pro grant on the same ladder. Pro replaces Plus. 3. The member opens Products: Pro is the current plan, with no price and no Cancel plan, and nothing on the page mentions the Plus subscription. 4. At renewal Stripe invoices for Plus, and the Billing page lists the invoice. ### Why it matters A member pays every period for a plan that delivers nothing, and cannot stop paying without asking an operator to cancel the subscription in Stripe.
cgalo5758 added this to the Public launch milestone 2026-10-26 20:58:21 +00:00
Author
Owner

Decided: while a grant holds a ladder against a subscription, the subscription's payment collection is paused at Stripe, so the member is not charged for a plan the grant has replaced. Stripe's pause_collection exists for this case, giving service away for a while: the subscription stays active, and the invoices Stripe creates while it is paused are voided. When the grant ends, collection resumes and the subscription's tier is delivered again, which is #270's half of the same change. The member sees the paused plan on the Products page and can still cancel it.

The wording, as decided:

  • Operator, the organization page's badge title: "Billing paused while a grant holds Demo Ladder."
  • Member, on the paid plan's card: "Paused while Pro is granted", with Cancel plan.
  • The grant form's warning: "Products marked ⚠ would supersede a tier a subscription pays for; its billing pauses until the grant ends."

Where the pause is set and cleared (in each act that starts or ends a grant's hold, or in the subscription reconcile as the one place that converges on the rule) is being worked out now, with what Stripe does to a paused subscription's schedule, a past-due invoice and a trial.

Decided: while a grant holds a ladder against a subscription, the subscription's payment collection is paused at Stripe, so the member is not charged for a plan the grant has replaced. Stripe's `pause_collection` exists for this case, giving service away for a while: the subscription stays `active`, and the invoices Stripe creates while it is paused are voided. When the grant ends, collection resumes and the subscription's tier is delivered again, which is #270's half of the same change. The member sees the paused plan on the Products page and can still cancel it. The wording, as decided: - Operator, the organization page's badge title: "Billing paused while a grant holds Demo Ladder." - Member, on the paid plan's card: "Paused while Pro is granted", with Cancel plan. - The grant form's warning: "Products marked ⚠ would supersede a tier a subscription pays for; its billing pauses until the grant ends." Where the pause is set and cleared (in each act that starts or ends a grant's hold, or in the subscription reconcile as the one place that converges on the rule) is being worked out now, with what Stripe does to a paused subscription's schedule, a past-due invoice and a trial.
Author
Owner

Two more parts decided.

Moving to a higher tier while a grant holds the ladder: the paid plan's card is not the only control. On the granted tier and on each tier above it, the Products page offers a switch of the paused subscription, which takes the ladder from the grant at once and resumes billing (#278, which this change closes). No tier on that ladder is sold through Checkout meanwhile, so the organization never holds two subscriptions on one ladder (#277). Cancel plan on the paused subscription ends it at its period end, as everywhere else.

Money at the grant's two edges: this change moves none. The renewal paid before the grant began is not credited, and the rest of the period in which the grant ends is not charged. That is exact for a grant of whole periods and inexact otherwise, by up to one period either way depending on where the renewal date falls; docs/stripe.md will say so. The exact rule, a grant never using up time the member paid for, is #280, its own change after this one.

Two more parts decided. Moving to a higher tier while a grant holds the ladder: the paid plan's card is not the only control. On the granted tier and on each tier above it, the Products page offers a switch of the paused subscription, which takes the ladder from the grant at once and resumes billing (#278, which this change closes). No tier on that ladder is sold through Checkout meanwhile, so the organization never holds two subscriptions on one ladder (#277). Cancel plan on the paused subscription ends it at its period end, as everywhere else. Money at the grant's two edges: this change moves none. The renewal paid before the grant began is not credited, and the rest of the period in which the grant ends is not charged. That is exact for a grant of whole periods and inexact otherwise, by up to one period either way depending on where the renewal date falls; `docs/stripe.md` will say so. The exact rule, a grant never using up time the member paid for, is #280, its own change after this one.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: wiki-cafe/member-console#269