Skip to content

v2.0.0 - Subject model: workspace subscriptions + user memberships

Choose a tag to compare

@MichaelSowah MichaelSowah released this 03 Aug 03:41
· 19 commits to main since this release
5637fdb

Subscriptions 2.0 generalizes the workspace-only 1.x lifecycle engine into two coexisting, never-crossing products on one payment/lifecycle engine: workspace subscriptions (tenant billing, unchanged for existing callers) and user memberships (a user's paid relationship to a single workspace — paywalls, member benefits, recurring content access).

⚠️ Upgrading from 1.x — the bridge is mandatory

A populated install cannot migrate directly from 1.3.1. Migration 006 refuses to run without the 1.4.0 preparation marker:

  1. Put subscription writes into a maintenance window.
  2. Install 1.4.0 and run php glueful subscriptions:prepare-v2 (idempotent).
  3. Install 2.0.0 and run php glueful migrate:run.

Fresh installs: subscriptions:plans:import-config is now a required install step — with an empty subscription_plans table every entitlement resolves empty (a loud default_plan_unresolvable diagnostic tells you exactly this).

Breaking changes

  • Subject-model schema (migration 006). Every domain row carries the (tenant_uuid, subject_type, subject_uuid) triple; plan_uuid becomes a NOT NULL reference; uniqueness is now one subscription per subject. A new subscription_provider_event_receipts table is the first claim authority for inbound provider events.
  • Direct subscription_overrides inserts must supply subject_type/subject_uuid — a 1.x-shaped insert silently matches nothing (a hand-written deny override would stop denying). Use the new supported writer: OverrideRepository::upsertForSubject() / deleteForSubject().
  • DB-authoritative catalog. Config plans are seeds only — no runtime overlay; new config plans require subscriptions:plans:import-config. Plan keys are immutable and scoped by (audience, owner_tenant_uuid); subscriptions reference immutable plan_uuids.
  • Projector rejection semantics. Deterministic rejections (missing_subject, invalid_subject, plan_scope_mismatch, subject_mismatch) commit an auditable receipt; UnmappedProviderSubscriptionException is retryable — webhook endpoints MUST let it surface as a retry-inducing response. Provider payloads are sanitized (closed allowlist) in both receipts and events, including historical rows during 006.
  • Stricter read-path validation. current('') and the …For() methods throw invalid subject on empty/incoherent identities where 1.x returned null.

New

  • Subject-aware core (currentFor/startFor/changePlanFor/cancelFor/reconcileFor(Subject …)) with the full 1.x tenant API preserved as a supported facade — existing integrations keep working.
  • Memberships are enabled by binding a SubjectResolverInterface — no new config keys. Includes MemberEntitlementResolver + require_member_entitlement middleware (fail-closed; rate tiers remain tenant-only).
  • Owner-fenced, receipts-first provider projection with deterministic race handling (same-plan idempotent, different-plan conflict) proven against real PostgreSQL savepoints (env-gated PostgresSavepointTest, SUBSCRIPTIONS_TEST_PG_*).
  • Strict payvia delivery lane (requires glueful/payvia >= 2.4): subscription events ride payvia's strict tagged lane with at-least-once delivery and genuine retry on the checkout race. With payvia ≤ 2.3 delivery degrades to the fault-isolated bus — unmapped events are then permanently lost, not retried (upgrade payvia to get the guarantee).
  • Tenant lifecycle mechanics: tenant-table registration, system-context discipline, and a host-neutral SubscriptionSubjectDataPurger for user- and workspace-scoped purges.
  • Console subject options (--subject-type/--subject-uuid) across subscriptions:show/set-plan/reconcile; scoped plans:* commands (--audience/--owner).

Compatibility

  • PHP ^8.3 · glueful/framework ^1.57 · payvia and tenancy remain optional (suggest); payvia ≥ 2.4 required only for the strict delivery guarantee.
  • The 1.x tenant service facade is not deprecated — it is the supported API for workspace billing.

Two notes before you publish: the body assumes payvia 2.4.0 is released first (the strict-lane bullet references it, and this repo's require-dev resolves ^2.4 from Packagist) — publish that release before or together with this one. And make sure the pushed v2.0.0 tag points at 26f6c52 (the local tag moved when we dropped the CI workflow; if you pushed an earlier tag, force-push the tag or re-tag remotely first).