# io-subscription — V3 migration plan

**Branch:** `v3` (forked from `master` @ v1.0.39, 2026-05-13)
**Target first V3 release:** `v3.0.1`
**Triggered by:** [Project Hermes](https://gitlab.apexl/apexl-internal/hermes) needs a Cygnus V3-compatible subscription module for its end-user subscription lifecycle.

## Why migrate

- V1 codebase targets `apexl/io ^1.0` + PHP `^7.1`. Cygnus V3 era requires `apexl/io ^3.6` + PHP 8.4.
- V1 has solid baseline: `Subscription` + `SubscriptionChange` + `SubscriptionCredit` + `SubscriptionPayment` entities. Schema covers user_id, company_id, product_id, billing dates, grace period.
- V3 needs to add: `Plan` entity (billable definition with pricing + limits), `Usage` tracking, tier-enforcement signals. Hermes's specific patterns map onto this.
- Multi-gateway awareness — io-subscription should be gateway-agnostic. Each Subscription holds a gateway reference + provider-side subscription ID; io-payment handles the gateway dance.

## Migration scope

### Composer constraints
- `php` `^7.1` → `^8.4`
- `apexl/io` `^1.0.0` → `^3.6`
- `apexl/io-product` `^1.0.0` → `^3.0`
- `apexl/io-user` `^1.0.0` → `^3.0`
- Add `apexl/io-payment` `^3.0` (was implicit before)
- Drop `minimum-stability: dev`

### Namespace
- `apexl\Io\modules\subscription\*` — unchanged from V1.

### PHP 8.4 compatibility sweep
- Same pattern as io-payment + io-product.

### Entity modernisation
- `Subscription` adopts V3 patterns + becomes `Ownable` + `Tagged` + `Indexable`.
- `subscription_status` taxonomy (system-seeded: `trialing` / `active` / `past_due` / `cancelled` / `expired` / `grace_period`).
- `Plan` entity added (billable definition: linked to a Product, with pricing + limits as columns or via custom-fields).
- `Usage` entity added (append-only event log: tenant_id + metric + value + timestamp). Background `UsageRollupJob` aggregates to daily rollups.
- Add Feature tests for the subscription lifecycle state machine.

### Subscription state machine
V3 hardens the lifecycle with a proper state machine:
- States: `trialing` → `active` → (`past_due` → `active` | `cancelled`) → `expired`
- Transitions emit hooks (`subscriptions.{transition}`) so consumers can react (send email, update entitlements, notify webhook subscribers).
- Hermes consumes these hooks to fan out its outgoing webhook to consuming products.

### Tier enforcement primitives

V3 adds reusable primitives for tier-by-capacity enforcement:
- `Plan.limits` — JSON column carrying limits per metric (`{servers: 25, api_calls: 100000}`).
- `TierEnforcer` service — given a Subscription + current usage, returns `ok` / `warning` / `overage` / `suspend`.
- `TierEnforcementPolicy` enum — `hard_cap` / `overage_charge` / `notify_only` per metric.

### Display module (subscriptionDisplay → @apexl/icarus-subscription)

Extract Vue from `subscriptionDisplay/` into `@apexl/icarus-subscription` npm package. Components: subscription list, subscription detail, plan picker, upgrade-modal, invoice-history table.

## Phasing

1. **Scaffold v3 branch + composer constraints + plan doc** ✅ (this commit)
2. **PHP 8.4 sweep** — types, strict_types, casts merge.
3. **Bump apexl/io + apexl/io-product + apexl/io-user to ^3.x**.
4. **Add Plan + Usage entities** + migrations.
5. **State-machine refactor** — subscription transitions + hooks.
6. **Tier-enforcement primitives** — TierEnforcer service + policy enum.
7. **Vue split** — extract `subscriptionDisplay/` → `@apexl/icarus-subscription`.
8. **Tag v3.0.1**.

## Open questions

- **Repo group:** keep on Bitbucket / codestore or migrate to GitLab / packages.apexl? Recommend GitLab for V3.
- **Plan entity placement:** io-product (entity-extension) vs io-subscription (where it's billed) vs new io-billing module? **Recommend io-subscription** — Plan is the billable shape; io-product owns the saleable shape. Cleaner separation.
- **Multi-currency subscriptions:** V3 V1.1 feature. Plan-per-currency or currency-as-Subscription-column? Probably plan-per-currency (Stripe's model).
- **Daedalus-style "active server count" enforcement:** Hermes's pattern is consuming product posts usage → Hermes returns signal in JWT licence claim. io-subscription's TierEnforcer becomes the engine; Hermes wraps it with the JWT-issuance layer.

## Status

- 2026-05-13: v3 branch created. Migration plan committed. composer.json bumped. No code changes yet.
