# Developer A — Identity, Billing & Platform Foundation

**Scope:** the `identity_billing` database (plus `catalog`-hosted plans/pricing/discounts per the
domain map) and the cross-cutting platform foundations every other track builds on.
**Owns:** auth for all 11 user types, roles/permissions, account links, subscriptions, payments,
invoices, refunds, revenue, tax, currency, pricing, plans, affiliate program *configuration*,
tenant accounts (corporate/CSR/ORG/sponsor) with verification, seats & bulk licenses, system settings.

Legend: `DEPS:` = hard dependencies (must be DONE before starting). `DEPS⚡:` = contract-only
dependency (contract defined in Phase 0; may start immediately against it).

---

## PHASE 0 — Foundations (unblocks all other tracks)

### T-A-01 · Base domain model + migration scaffolding
- `BaseDomainModel` abstract class: enforces `$connection` (throws if unset), default
  timestamps, soft-delete where appropriate, `audit_log()` helper that appends to `audit_events`.
- Migration layout: `database/migrations/{identity_billing,catalog,learning,engagement,analytics}/`.
- `.env.example` updated with all 5 `DB_*` vars; `config/database.php` already defines connections (verify + lock).
- **DEPS:** none
- **AC:** (1) test asserts instantiating a model without `$connection` throws;
  (2) `php artisan migrate` runs clean against 5 in-memory SQLite files simulating the 5 DBs;
  (3) static-analysis lint rule (or test) fails any model in `app/Models` missing `$connection`.

### T-A-02 · Audit events (cross-cutting)
- `audit_events` table (identity_billing): actor_id, actor_type, action, target_type, target_id,
  before (json), after (json), ip, created_at. Immutable — no update/delete paths.
- `App\Services\Audit\AuditService::log(...)` + `AuditController` (GET filters: actor, action,
  target, date range; super-admin permission only).
- **DEPS:** T-A-01
- **AC:** every `BaseDomainModel::save/delete` hook writes an audit row; test creates a user,
  verifies audit row with correct before/after; audit row cannot be deleted via API (403) or model.

### T-A-03 · Verification tokens (email, phone OTP, reset links)
- `verification_tokens` table + `TokenService`: create/verify/consume; single-use, TTL (email 24h,
  OTP 5 min), resend cooldown (60s), max attempts (OTP: 5 invalid → invalidate), rate limit per
  account+IP. OTP 6-digit.
- Endpoints: `POST /auth/verify/email`, `POST /auth/verify/phone/otp`, `POST /auth/otp/resend`,
  `POST /auth/password/reset` (request+confirm; 30-min single-use link).
- **DEPS:** T-A-01
- **AC:** replaying a consumed token → 410; expired → 410; resend before cooldown → 429;
  5th bad OTP invalidates (6th attempt → 422 even with right code); password reset invalidates
  all user sessions (test: old token 401 after reset).

### T-A-04 · RBAC engine (roles, permissions, middleware)
- Tables: `roles` (name unique, status active/archived), `permissions` (catalog: name, type
  feature/action/data-scope), `role_permission`, `user_role` (user_id = identity_billing.users.id).
- Seed: Super Admin (immutable — create/edit/delete endpoints refuse), Billing Admin, Teacher,
  Content Reviewer, Student Support, Institute Admin, CSR/ORG/SPON tenant roles.
- `Can` middleware + `Permission` policy base: **deny-by-default**; multi-role = union of
  permissions; data scopes resolved per user (institute_id scope = hard boundary).
- Super-admin CRUD: `CRUD /roles`, `PUT /roles/{id}/permissions` (with affected-user-count
  preview response), `PUT /users/{id}/roles` (atomic).
- **DEPS:** T-A-01, T-A-02
- **AC:** user with no roles gets 403 on every protected endpoint (test matrix); role deletion
  blocked while held (409); archived role: holders keep access, new assignment blocked;
  super-admin role cannot be modified (403); permission edit propagates on next request (test:
  revoke permission → user's next call 403).

### T-A-05 · Response envelope + error conventions
- `ApiResponse` trait: `{data, message}` success envelope (matches existing AuthController);
  exception handler → `{message, errors{field:[...]}}` with 4xx; 422 for validation;
  403/404/409/423 (locked)/429 (rate) semantics documented in the README
  "API response & error conventions" section.
- **DEPS:** T-A-01
- **AC:** existing AuthController tests pass with envelope; validation failure returns field map;
  404 for wrong IDs never leaks resource existence (identical body for missing vs forbidden
  resource on user endpoints).

### T-A-06 · Domain events bus + queue wiring
- `App\Events\{Identity,Billing,Catalog,Learning,Engagement}` namespaces with all event classes
  named in README §"Event Contracts" (contract stubs — A/B/C emit, D consumes).
- Queue: database driver by default; `queue:work` instructions in docs; events dispatched via
  `dispatch()`/`dispatch_sync()` policy documented.
- **DEPS:** T-A-01
- **AC:** test listener records each of the 15 contract events when fired; consumer failure
  retries 3× then moves to failed_jobs (test with failing listener).

---

## PHASE 1 — Identity & Authentication

### T-A-07 · User model v2 + all 11 user types
- `users` (identity_billing): type enum (sub_admin, student, parent, affiliate, institute,
  corporate, csr, sponsor, organization, teacher, tertiary), status (active/suspended/
  deactivated), verification flags, provenance (self/admin-created), org_affiliation (institute
  id / tenant id), DOB, phone.
- Migrate existing `create_users_table` (currently on default conn) → identity_billing;
  keep `personal_access_tokens` on identity_billing; move cache/jobs to default.
- `AuthService`: register per type (type-specific onboarding fields), login (password /
  password+OTP per role policy / social Google+Facebook / SSO stub hook for T-D-13),
  sessions (list, revoke, force logout), remember-me.
- **DEPS:** T-A-02, T-A-03, T-A-05
- **AC:** register+login for each of 11 types (test per type); login errors never distinguish
  bad email vs bad password (identical message, no timing oracle); suspended user → 403 with
  "suspended" message; deactivated → different message; one phone → one active account (second
  bind 409); email/phone change requires re-verification and old stays active until new verified.

### T-A-08 · Security policy (2FA, lockout, IP rules, geo)
- 2FA: TOTP (authenticator) + SMS fallback, 10 single-use backup codes, enable/disable,
  per-role mandatory-2FA flag.
- Progressive lockout: per-account AND per-IP; duration doubles per recurrence up to cap;
  OTP failures counted separately from password failures.
- `ip_rules` (allow/deny, optional expiry; **deny beats allow; checked before credentials**)
  + `geo_restrictions` (platform-level).
- **DEPS:** T-A-07
- **AC:** 5th failed login → 423 with remaining-seconds; lockout duration doubles; IP deny
  blocks even correct credentials (test); 2FA enable requires valid TOTP; mandatory-2FA role
  without enrollment gets enrollment-forced flow on login; backup code single-use.

### T-A-09 · User account management (directory + lifecycle)
- `GET /users` (filter type/status/org/plan, pagination, hierarchy view), detail, create
  (admin), update, `PUT /users/{id}/status` (suspend/deactivate), bulk register (CSV:
  row-level validation, duplicates mapped not created, explicit preview-then-commit, error
  report preserving original rows), bulk status/enroll (confirmed preview + recorded reason,
  per-item failures reported, one audit event per op).
- Lifecycle interlocks: **suspend** = immediate (sessions die next request) + pauses recurring
  billing (emits `UserSuspended` → Billing listener T-A-19); **deactivate** = cancels
  subscription, releases seat, triggers privacy flow (T-A-25), irreversible.
- **DEPS:** T-A-07, T-A-08
- **AC:** suspend → user's existing token 403 on next request AND next recurring charge skipped
  (test); deactivate → subscription cancelled + seat freed (test); CSV import with 3 bad rows →
  7 imported, 3 failed with per-row reasons, commit only after preview confirmation;
  re-activation of deactivated requires retention-restore path (test).

### T-A-10 · Account links (parent↔child, student↔institute)
- `account_links` table: type (parent_child, student_institute), source_id, target_id,
  verification state, consent ref.
- Parent↔child: multi-child per parent (max configurable). A parent request stays pending
  and hidden from the parent until the student confirms the relationship; the guardian must
  then verify their email or phone before the link becomes active. Learning projections are
  filtered by the latest monitoring consent, including withdrawals and policy re-consent.
  Unlink requires a reason and a replacement guardian with their own verified active link.
- Student↔institute: institute admins can manage students affiliated with their institute;
  cross-institute transfers require a platform super-admin. Transfers lock the student and
  destination institute, check seat capacity, and preserve old rows as history.
- Student↔institute: exactly one primary; re-link releases old seat atomically before new
  allocation; blocked if target institute seat-full.
- **DEPS:** T-A-07
- **AC:** parent A cannot read child B (not linked) data (404, test); re-link to full institute
  → 409 and old seat untouched; seat transfer atomic (test: no window where student has 0 or 2
  seats); unlink without reason → 422.

### T-A-11 · Consents (minor, guardian, data)
- `consent_records`: user, scope (terms, privacy, minor guardian, monitoring scope), version,
  status granted/pending/denied/withdrawn, timestamp. Versioned docs; withdrawal **appends**
  record (history immutable); material policy change forces re-consent.
- Minors: limited access until verified guardian consent; withdrawal pauses account.
- Endpoints: `GET/PUT /consent`, `GET /consent/history`.
- **DEPS:** T-A-07
- **AC:** minor without guardian consent gets content-restricted responses (test); withdrawal
  does not delete grant row (history intact); policy v2 change → user must re-consent before
  next access (test).

### T-A-12 · Tenant accounts + verification (corporate, CSR, ORG, sponsor)
- `tenants` table: type (corporate/csr/organization/sponsor), status (pending/verified/
  suspended), profile fields per type (domain, bank details, regions, brand assets…),
  change history.
- Corporate: **domain verification** — issue TXT value; verify (exact match; 24h cache;
  change → re-verify); one account per verified domain (409 on dup).
- CSR/ORG/SPON: document/brand verification with **platform approval workflow**
  (pending → approved/rejected by super admin).
- `tenant_members`: invite-by-email (acceptance link), roles per type (CORP 4 / CSR 3 / ORG 7 /
  SPON 3), deactivate; CORP: at least one Corporate Admin must remain (409 on violation).
- **Gate:** only verified/active tenants can transact — `TenantGate` middleware on all
  tenant-portal routes (used by T-D-13..18).
- **DEPS:** T-A-07, T-A-08
- **AC:** corp account stays pending until TXT verified; employee enrollment before verification
  → 403; duplicate domain 409; CSR funding endpoint (stub from D) 403 until approved;
  demoting last CORP admin → 409; role change effective next session (test).

---

## PHASE 2 — Commercial Catalog (plans, pricing, discounts)

### T-A-13 · Membership plans
- `plans` (catalog per domain map): type (academic single/bundle/full-grade, professional
  per-course/lifetime/installment, skill-path, family, bulk), billing period, features/limits
  (AI question limit, download cap, concurrency), status (active/deactivated), visibility ×
  availability.
- Access scope explicit per plan (single-subject limited to that subject's content/assessments).
- Family: child-count limit, consolidated invoice, per-child profiles.
- `SubscriptionService::isEntitled(userId, scope)` — THE entitlement gate used by B (samples)
  and C (playback/downloads/join).
- Trial: 7-day, no card, once per user (counter in identity_billing). Start selects an active,
  public plan available for self-service; its scope is granted for the trial window. Freemium:
  first 2 videos per subject, sample assessments, limited AI.
- **DEPS:** T-A-04, T-A-07
- **AC:** deactivated plan → not sellable (409 on new subscription) but existing subscribers
  keep access + grace; trial twice → 409; entitlement check: single-subject plan user denied
  other-subject content (test via service); family limit exceeded → 409 with upgrade message.

### T-A-14 · Pricing engine
- `prices`: per grade/subject/bundle/full-grade/course/lifetime/skill-path; `price_versions`
  (effective-dated, immutable history); bundle price < sum of individuals (validation).
- Currencies: base + supported, exchange rates (refresh frequency, auditable updates).
- Tax rules: GST/VAT per region, rate, tax base.
- Upgrade proration: daily-rate on remaining period, difference pricing, explicit rounding.
- Bulk tier pricing (10–50 / 51–200 / 201–500 / 500+, decreasing per-license price; crossing
  tier re-applies pricing, recorded); custom enterprise pricing (count+price+terms).
- **DEPS:** T-A-13
- **AC:** proration test: 30-day cycle, day 20 upgrade → charge = diff × 10/30 with documented
  rounding; price update effective-dated (past invoices keep old price); bulk 150 units →
  51–200 tier price; tax computed per region on invoices (test).
- **Pricing implementation policy:** catalog price versions are append-only integer minor-unit
  snapshots; a later effective date adds a row and never overwrites the version an invoice
  captured. Catalog prices use the configured base currency; supported local currencies are
  rendered from immutable base→quote exchange-rate snapshots refreshed no more than once per
  24 hours. Tax rules are region-specific effective-dated snapshots, applied to the converted
  subtotal. Proration, FX, tax, and bulk-discount division round to the nearest minor unit,
  with exact halves rounded upward. Bulk discounts are 5% for 10–50, 10% for 51–200, 15% for
  201–499, and 20% from 500 units; the 500+ tier starts at 500 to make the listed bands
  non-overlapping. Every tier quote and custom enterprise quote records the unit count,
  applied price, currency, and (for enterprise) terms.

### T-A-15 · Discounts & promotions
- `discount_codes`: string unique platform-wide; type (pct/fixed); scope; per-user + total usage
  limits; validity; status (active/deactivated/expired); owner (platform/affiliate).
- Affiliate code grants: permission flag + limits (count, max discount, scope) — used by T-A-16.
- Campaigns: period, target segment (new/returning/lapsed/high-value), channels, A/B variants;
  launch/stop; delivery itself is Track D (T-D-09) — A owns definitions + redemption.
- `GET /discount-codes/{id}/usage` (redemptions + revenue per code/affiliate/period).
- **DEPS:** T-A-13
- **AC:** code expired/deactivated/depleted → 409 at checkout with reason; per-user limit
  enforced (2nd redemption 409); A/B variant assignment stable per user (test); affiliate
  code created without grant → 403.

### T-A-16 · Affiliate program configuration (admin side)
- Commission structure: base rate, two-tier (T1 direct, T2 sub-affiliates — **no deeper**),
  recruitment rewards, leadership bonuses (dual-gated: downline size AND active sub-affiliates).
- Levels Bronze→Platinum: increasing rates, milestones, seasonal bonuses; rate schedule with
  change history (non-retroactive per accrual event).
- Applications: approve/reject (unlock referral tools on approval — gate checked by T-D-07).
- Payout config: minimum threshold, schedule; payout requests → approval workflow
  (pending/processed/paid).
- **DEPS:** T-A-13, T-A-15
- **AC:** structure changes audit-logged + non-retroactive (existing pending commissions keep
  old rate — test); payout below threshold rejected (409); approval of application flips
  affiliate `referral_enabled` (test).

---

## PHASE 3 — Billing Core

### T-A-17 · Subscriptions + recurring billing
- `subscriptions`: user, plan, scope, status (trial/active/expiring/lapsed/cancelled),
  start/end/renewal dates, auto_renew, grace state.
- Lifecycle: activate **only on successful payment**; cancel → access to period end;
  failed renewal → grace period (configurable) → lapsed; reactivation immediate;
  upgrade/downgrade prorated (T-A-14), downgrade blocked if usage exceeds new scope (409).
- Billing runner: `Billing:process` scheduled command — charges on configured date,
  notification via `NotificationService` (DEPS⚡ T-D-01), retry config (count/interval),
  failure handling; emits `SubscriptionActivated/Expired/Cancelled`, `PaymentSucceeded/Failed`.
- **DEPS:** T-A-13, T-A-14
- **AC:** subscription without payment stays `pending_payment`, entitlement = none (test);
  cancel on day 20 → access until day 30, renewal skipped; failed charge → grace → lapse →
  entitlement revoked; downgrade over-usage blocked (409).

### T-A-18 · Payments, gateways, transactions
- `payment_gateways`: method→gateway mapping (card/UPI/wallet/bank), credentials, status
  (active/inactive/error); method enable/disable at checkout.
- `transactions`: status (pending/successful/failed), retry (count, interval, notification);
  **webhooks from gateway: payment failure does NOT activate subscription; chargeback
  suspends; refund reverses access; idempotent processing** (duplicate webhook = no-op, test).
- Gateway adapters: `GatewayInterface` + Stripe adapter as reference implementation (sandbox
  in tests); others stubbed with test doubles.
- **DEPS:** T-A-17
- **AC:** checkout with disabled method → 422; failed payment retry per config; chargeback
  webhook → subscription suspended (test); duplicate webhook idempotent.

### T-A-19 · Invoices, refunds, adjustments
- `invoices`: sequential numbering, line items (incl. proration lines), tax, total, status
  (draft/issued/paid/overdue), **immutable once issued** (corrections via credit notes
  ≤ referenced total); branded variant per institute; share links (access-controlled,
  expirable).
- `refunds`: request → approval → processing; policy (7-day window, partial limits);
  re-approval after rejection recorded; emits `RefundProcessed` (reverses access via listener).
- `billing_adjustments`: request → approval, reason recorded.
- Interlock listener: `UserSuspended` → pause recurring billing (from T-A-09).
- **DEPS:** T-A-18
- **AC:** issued invoice not modifiable via API (409); credit note exceeding invoice total → 422;
  refund outside window → 403 per policy; suspension pause + reactivation resume (test);
  sequential numbering with no gaps on concurrent issue (concurrency test).

### T-A-20 · Revenue, tax, currency, reconciliation
- `revenue_records` (source, amount, currency, region, period, recognized date) with
  breakdowns by course/period/region (report axes).
- Tax reports by period+region; FX conversion/settlement logged with rate used.
- `reconciliation_runs`: match platform transactions vs gateway records (matched/unmatched/
  mismatched); discrepancies identified → pending → resolved.
- Commission ledger (shared with T-A-16): `commission_entries` pending → accrued → earned →
  settled → paid (renewals generate continuing commissions); `payouts` ledger.
- **DEPS:** T-A-18, T-A-19
- **AC:** reconciliation run flags a planted mismatch (test); commission state machine:
  settled→paid only via payout; rate change non-retroactive (test); revenue report axes
  return correct splits (test).

### T-A-21 · Seats & bulk licenses (institute + corporate + ORG)
- `license_tiers`, `bulk_purchases`, `license_pools` (total/assigned/unassigned by tier/term,
  real-time), `seat_assignments` (student↔license), `license_events` (**append-only**:
  purchase/assignment/reassignment/release/renewal/expiry with actor+timestamp).
- Rules: pool entries on successful payment; min/max purchase qty; **one active license per
  student**; reassignment revokes previous student (immediate); release returns to pool;
  lapse revokes (grace shown); renewal extends from current expiry; entitlement = pool cap,
  warning threshold configurable + **hard block at 100%**; alerts at 80%/95% to Billing
  Manager + Admin (via NotificationService, DEPS⚡ T-D-01).
- Corporate variant: individual seats, revocation deactivates access **within 1 minute**,
  completed records retained; un-renewed seats deactivate at cycle end; ORG variant:
  department/group-granularity allocation (`granularity` column — resolution of open question).
- **DEPS:** T-A-17, T-A-21's purchase path uses T-A-18
- **AC:** double-assign student → 409; reassign → previous student entitlement gone on next
  request (test); pool counter real-time (concurrent purchase test); 100% cap blocks new
  assignment (409); corporate revocation → access 403 within 60s (test with short TTL cache).

---

## PHASE 4 — Cross-Cutting Platform

### T-A-22 · Data protection & compliance
- DSAR: access (export w/ third-party redaction, identity verification mandatory), deletion
  (eligibility checks; **legal holds block**; legally required records retained but
  anonymized; irreversible), portability. Status lifecycle requested → granted/denied/completed.
- DRM flags on catalog content (per-content, per-user license; download-block recorded per
  attempt; watermark visible/invisible flags; secure-streaming flag) — enforcement in B/C
  via flags (DEPS⚡ T-B-08).
- DMCA notices → takedown (content status → catalog via event); content licenses
  (active/expired/revoked gate availability).
- Policies (ToS/privacy/refund/AUP/cookie): versioned, click-through acceptance, change
  notification (material change → re-consent, T-A-11).
- Age verification + child-safety: monitoring channel config, creator screening flag,
  inappropriate-content report intake (→ T-D-05 moderation).
- **DEPS:** T-A-09, T-A-11
- **AC:** deletion with legal hold → blocked + reason (test); export redacts other users'
  names (test); policy v2 → consent re-request; DMCA takedown event received by catalog
  listener flips content status (test).

### T-A-23 · System settings
- Branding (name, logos, email templates — merge-field validation, per-language, protected
  compliance blocks; previous state retained for rollback), localization (languages, base+
  supported currencies, time zones).
- `feature_toggles`: enforce like permissions (off → 404/403 for all users).
- Trial/freemium policy (T-A-13 values), content settings (quality ladder+cap, subtitle
  languages, download policy: entitlement/per-user cap/retention), notification defaults
  (channels per alert type, thresholds, severity routing), maintenance mode, banners/popups,
  scheduled notifications (delivery in D).
- **DEPS:** T-A-04, T-A-13
- **AC:** toggle off → feature endpoints 404 (test); download policy change: entitled user can
  download, lapsed user cannot (test); branding save → rollback to previous (test);
  email template with unknown merge field → 422.

### T-A-24 · API keys, rate limits, webhooks (admin side)
- `api_keys`: scopes (≥1 required), full key shown **once**, masked after, revoke immediate,
  rotation with grace period, per-key usage tracking.
- `rate_limit_policies`: per-key override > per-endpoint > global; endpoint never looser than
  global; 429 + `Retry-After`; burst allowed, sustained throttled.
- `webhook_endpoints` (per tenant): events, retry policy (non-2xx retried exponential backoff
  ≤24h, exhausted → dead-letter), signing secret (shown once, rotatable), IP allowlist.
  **Dispatch engine itself is T-D-12 — A owns admin CRUD + validation.**
- **DEPS:** T-A-04
- **AC:** key usable only within scope (over-scope → 403 + logged); rotation: old key works
  until grace ends (test); rate limit: sustained burst → 429 with Retry-After (test);
  webhook secret rotation invalidates old signature (test via D dispatcher contract).

### T-A-25 · Backup, export, DR
- `backup_jobs` (scheduled/manual, scope, retention prune), `restore_jobs` (RPO/RTO tracked),
  DR capability state per scope (ready/not-ready).
- `export_jobs`: scope (user data/content/financial/institute records — **entitled data only**)
  × format; status in-progress→completed/failed; secure delivery (expiring links, 24h).
- Portability requests (user-initiated, ties T-A-22).
- Financial exports source-of-truth = identity_billing. Exports ≤100k rows (split).
- **DEPS:** T-A-09
- **AC:** export for institute scoped to its students only (test); retention prunes oldest
  beyond count (test); DSAR portability request → export job linked (test); failed export
  reported with reason.

---

## Phase Gates (Track A)
- **Gate 1 (end Phase 0):** all tracks unblocked; CI green; 5-DB migrations clean; audit on all writes.
- **Gate 2 (end Phase 1):** every user type can register/login/2FA/sessions; tenant verification works.
- **Gate 3 (end Phase 3):** full money path — plan → checkout → payment → subscription →
  renewal → refund — E2E test green; reconciliation clean.
- **Gate 4 (end Phase 4):** compliance + settings + keys live; handoff to D for dispatcher.

## Contract Stub Obligations (Track A must ship these interfaces in Phase 0/1)
- `UserService`, `SubscriptionService::isEntitled`, `TokenService`, `AuditService`,
  `NotificationService` (interface only — implementation lands in T-D-01), `TenantGate`.
