Skip to content

billing: requirePlan gates on subscription.plan but not subscription.status (canceled plan still passes) #3679

@PierreBrisorgueil

Description

@PierreBrisorgueil

Context

modules/billing/middlewares/billing.requirePlan.js gates by subscription?.plan only. A subscription with plan: 'growth' but status: 'canceled' (or past_due/unpaid) still passes requirePlan('growth','pro').

Pre-existing (middleware had zero route usages until trawl_node #1183 wired it on apiKeys/webhooks/org-invites for plan-entitlement enforcement). Surfaced during Trawl Phase 2 (infra plan 2026-05-19-pricing-perfect-pre-stripe-live.md). Left out of scope deliberately (locked design = plan-membership 403; status filtering would expand the contract).

Ask

Decide intended semantics: should an active-plan check require status ∈ {active, trialing}? If yes, add status filtering in requirePlan (generic, propagates to all downstream via /update-stack).

Related divergence to reconcile

trawl_node #1183 already changed requirePlan.js response shape to surface {errorCode:'PLAN_REQUIRED', requiredPlans, currentPlan} at top level (the documented contract wasn't achievable with the old responses.error nesting). That is currently a downstream patch in trawl_node — should be upstreamed here (or tracked in trawl_node DOWNSTREAM_PATCHES.md) so /update-stack --theirs doesn't wipe it. Backward-compat verified (6 existing requirePlan unit tests pass).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions