# Backend Port Progress (NestJS → Laravel)

## Done

### Core API plumbing
- [x] `/api/v1/*` routing enabled (no global `api` prefix duplication)
- [x] Unified success response wrapper for `/api/v1/*` (Nest-style shape)
- [x] Unified JSON error rendering for `/api/v1/*` (includes `statusCode`, `timestamp`, `path`, `method`)
- [x] JWT auth middleware (`Authorization: Bearer ...` and `at` cookie fallback)
- [x] Role-based middleware (`roles:*`) for route protection

### Auth (`/api/v1/auth/*`)
- [x] `POST /api/v1/auth/login`
- [x] `POST /api/v1/auth/register/student`
- [x] `POST /api/v1/auth/register/school`
- [x] `POST /api/v1/auth/refresh`
- [x] `POST /api/v1/auth/logout`
- [x] `POST /api/v1/auth/2fa/setup`
- [x] `POST /api/v1/auth/2fa/enable`
- [x] `POST /api/v1/auth/2fa/disable`
- [x] `GET  /api/v1/auth/2fa/status`
- [x] `POST /api/v1/auth/2fa/resend`
- [x] `POST /api/v1/auth/verify-2fa`
- [x] `POST /api/v1/auth/verify-email`
- [x] `POST /api/v1/auth/resend-verification`
- [x] `POST /api/v1/auth/validate-school-code` (supports seeded/unclaimed “claimable” schools)
- [x] `POST /api/v1/auth/verify-school-email`
- [x] `GET  /api/v1/auth/profile`
- [x] `PATCH /api/v1/auth/profile`
- [x] Password endpoints wired (logic is currently minimal/stubbed):
  - [x] `POST /api/v1/auth/password/change-otp`
  - [x] `POST /api/v1/auth/password/verify-otp`
  - [x] `POST /api/v1/auth/password/reset-request`
  - [x] `POST /api/v1/auth/password/reset`
- [x] `POST /api/v1/auth/heartbeat`

### Sessions (`/api/v1/sessions/*`)
- [x] `POST   /api/v1/sessions`
- [x] `GET    /api/v1/sessions/{id}`
- [x] `POST   /api/v1/sessions/{id}`
- [x] `DELETE /api/v1/sessions/{id}`
- [x] `DELETE /api/v1/sessions/user/{userId}`
- [x] `DELETE /api/v1/sessions/clear`
- [x] `DELETE /api/v1/sessions/clear/all`
- [x] `GET    /api/v1/sessions/count`
- [x] `GET    /api/v1/sessions/count/total`
- [x] `GET    /api/v1/sessions/count/active`
- [x] `GET    /api/v1/sessions/stats/hybrid`
- [x] `GET    /api/v1/sessions/user/me` (JWT-protected)

### Programs (`/api/v1/programs/*`)
- [x] `GET /api/v1/programs`
- [x] `GET /api/v1/programs/by-type/{type}`
- [x] `GET /api/v1/programs/by-institution-type/{institutionType}`
- [x] `GET /api/v1/programs/by-school/{schoolId}`
- [x] `GET /api/v1/programs/by-school-type/{schoolType}`
- [x] `GET /api/v1/programs/code/{code}`
- [x] `POST   /api/v1/programs/schools/{schoolId}`
- [x] `DELETE /api/v1/programs/schools/{schoolId}/{programId}`
- [x] `PUT    /api/v1/programs/schools/{schoolId}`

### Schools (`/api/v1/schools/*`)
- [x] `GET  /api/v1/schools/available`
- [x] `GET  /api/v1/schools/catalog`
- [x] `GET  /api/v1/schools/catalog/student`
- [x] `GET  /api/v1/schools/catalog/registration`
- [x] `POST /api/v1/schools` (guarded: `jwt` + `roles:SUPER_ADMIN`)
- [x] `GET  /api/v1/schools` (guarded: `jwt` + `roles:SUPER_ADMIN,NMCN_ADMIN`)
- [x] `GET  /api/v1/schools/my-school` (guarded: `jwt` + `roles:SCHOOL_ADMIN`)
- [x] `PATCH /api/v1/schools/my-school/branding` (guarded)
- [x] `PATCH /api/v1/schools/my-school/pricing` (guarded)
- [x] `GET  /api/v1/schools/my-school/stats` (guarded; currently returns placeholder counts)
- [x] `GET  /api/v1/schools/code/{code}` (guarded: `jwt` + `roles:SUPER_ADMIN,NMCN_ADMIN`)
- [x] `PATCH /api/v1/schools/{id}` (guarded: `jwt` + `roles:SUPER_ADMIN`)
- [x] `PATCH /api/v1/schools/{id}/status` (guarded: `jwt` + `roles:SUPER_ADMIN`)
- [x] `GET  /api/v1/schools/{id}` (guarded: role-based; school admins can only access their own school)

### Students (`/api/v1/students/*`)
- [x] `GET /api/v1/students/dashboard` (guarded: `jwt` + `roles:STUDENT`)
- [x] `GET /api/v1/students/schools/available` (guarded: `jwt` + `roles:STUDENT`)
- [x] `GET /api/v1/students/requests/history` (guarded: `jwt` + `roles:STUDENT`)
- [x] `GET /api/v1/students/requests/transcripts` (guarded: `jwt` + `roles:STUDENT`)
- [x] `GET /api/v1/students/requests/verifications` (guarded: `jwt` + `roles:STUDENT`)
- [x] `GET /api/v1/students/enrollments` (guarded: `jwt` + `roles:STUDENT`)
- [x] `POST /api/v1/students/enrollments` (guarded: `jwt` + `roles:STUDENT`)
- [x] `POST /api/v1/students/enrollments/{enrollmentId}/student-id-card` (guarded: `jwt` + `roles:STUDENT`)
- [x] `POST /api/v1/students/enrollments/{enrollmentId}/link-student-id-card` (guarded: `jwt` + `roles:STUDENT`)
- [x] `GET /api/v1/students/profile` (guarded: `jwt` + `roles:STUDENT`)
- [x] `POST /api/v1/students/profile/update` (guarded: `jwt` + `roles:STUDENT`)

### Onboarding (`/api/v1/onboarding/*`)
- [x] `GET /api/v1/onboarding/temp-data` (guarded: `jwt`)
- [x] `GET /api/v1/onboarding/status` (guarded: `jwt`)
- [x] `GET /api/v1/onboarding/draft` (guarded: `jwt`)
- [x] `POST /api/v1/onboarding/draft` (guarded: `jwt`)
- [x] `POST /api/v1/onboarding/draft/clear` (guarded: `jwt`)
- [x] `POST /api/v1/onboarding/student/complete` (guarded: `jwt`)
- [x] `POST /api/v1/onboarding/school/complete` (guarded: `jwt`)
- [x] `GET /api/v1/onboarding/draft/public/{userId}` (public)
- [x] `POST /api/v1/onboarding/draft/public/{userId}` (public)
- [x] `POST /api/v1/onboarding/draft/public/{userId}/clear` (public)

### Transcript Requests (`/api/v1/transcript-request/*`)
- [x] `POST  /api/v1/transcript-request`
- [x] `GET   /api/v1/transcript-request/{id}`
- [x] `PATCH /api/v1/transcript-request/{id}`
- [x] `DELETE /api/v1/transcript-request/{id}`
- [x] `GET   /api/v1/transcript-request/download/{token}` (public)
- [x] `POST  /api/v1/transcript-request/download/{token}/file` (public)
- [x] `POST  /api/v1/transcript-request/build`
- [x] `GET   /api/v1/transcript-request/templates`
- [x] `GET   /api/v1/transcript-request/templates/{id}`
- [x] `POST  /api/v1/transcript-request/templates/validate`
- [x] Recipients catalog:
  - [x] `GET    /api/v1/transcript-request/recipients-catalog`
  - [x] `GET    /api/v1/transcript-request/recipients-catalog/{id}`
  - [x] `POST   /api/v1/transcript-request/recipients-catalog`
  - [x] `PATCH  /api/v1/transcript-request/recipients-catalog/{id}`
  - [x] `DELETE /api/v1/transcript-request/recipients-catalog/{id}`
  - [x] `GET    /api/v1/transcript-request/recipients-catalog/stats`
  - [x] `GET    /api/v1/transcript-request/recipients-catalog/search`
- [x] Documents + helpers:
  - [x] `POST /api/v1/transcript-request/{id}/documents`
  - [x] `GET  /api/v1/transcript-request/{id}/documents`
  - [x] `GET  /api/v1/transcript-request/{id}/documents/{documentId}/download`
  - [x] `GET  /api/v1/transcript-request/{id}/recipients`
  - [x] `POST /api/v1/transcript-request/{id}/recipients`
  - [x] `DELETE /api/v1/transcript-request/{id}/recipients/{recipientId}`
  - [x] `POST /api/v1/transcript-request/{id}/approve`
  - [x] `POST /api/v1/transcript-request/{id}/reject`
  - [x] `POST /api/v1/transcript-request/{id}/complete`
  - [x] `GET  /api/v1/transcript-request/{id}/status`
  - [x] `GET  /api/v1/transcript-request/{id}/track`
  - [x] `GET  /api/v1/transcript-request/{id}/history`
  - [x] `POST /api/v1/transcript-request/{id}/resend`
  - [x] `POST /api/v1/transcript-request/{id}/verification-form/link`
  - [x] `GET  /api/v1/transcript-request/{id}/verification-form/download`
  - [x] `POST /api/v1/transcript-request/{id}/verification-form/upload-completed`
  - [x] `GET  /api/v1/transcript-request/pricing`
  - [x] `POST /api/v1/transcript-request/calculate-cost`

### Files (`/api/v1/files/*`)
- [x] `POST /api/v1/files/onboarding/sign` (public)
- [x] `POST /api/v1/files/transcript-documents/sign`
- [x] `GET  /api/v1/files/storage/{assetId}` (public)
- [x] `GET  /api/v1/files/signed-url/{publicId}` (public)
- [x] `POST /api/v1/files/upload/school-logo` (guarded)
- [x] `POST /api/v1/files/upload/school-signature` (guarded)
- [x] `POST /api/v1/files/upload/school-seal` (guarded)
- [x] `POST /api/v1/files/upload/student-document` (guarded)

### Consent (`/api/v1/consent/*`)
- [x] `POST /api/v1/consent` (guarded)

### Payments (`/api/v1/payments/*`)
- [x] `POST /api/v1/payments/initiate`
- [x] `POST /api/v1/payments/verify`
- [x] `POST /api/v1/payments/webhook`
- [x] `GET  /api/v1/payments/history`
- [x] `GET  /api/v1/payments/stats`
- [x] `GET  /api/v1/payments/export`
- [x] `POST /api/v1/payments/cancel-multiple`
- [x] `POST /api/v1/payments/cleanup-duplicates`
- [x] `POST /api/v1/payments/pricing/preview`
- [x] `POST /api/v1/payments/pricing/multi-school`
- [x] `GET  /api/v1/payments/{paymentId}`
- [x] `GET  /api/v1/payments/{paymentId}/receipt`
- [x] `POST /api/v1/payments/{paymentId}/refund`
- [x] `POST /api/v1/payments/{paymentId}/refund-request`

### Notifications (`/api/v1/notifications/*`)
- [x] `GET    /api/v1/notifications`
- [x] `GET    /api/v1/notifications/unread-count`
- [x] `GET    /api/v1/notifications/{id}`
- [x] `PUT    /api/v1/notifications/{id}/read`
- [x] `PUT    /api/v1/notifications/mark-all-read`
- [x] `DELETE /api/v1/notifications/{id}`
- [x] `POST   /api/v1/notifications/test`
- [x] Preferences:
  - [x] `GET  /api/v1/notifications/preferences`
  - [x] `PUT  /api/v1/notifications/preferences`
  - [x] `POST /api/v1/notifications/preferences/reset`

### Audit (`/api/v1/audit/*`)
- [x] `GET /api/v1/audit/logs`
- [x] `GET /api/v1/audit/logs/export`
- [x] `GET /api/v1/audit/logs/user/{userId}`
- [x] `GET /api/v1/audit/logs/entity/{entityType}/{entityId}`
- [x] `GET /api/v1/audit/my-activity`
- [x] `GET /api/v1/audit/stats`
- [x] `GET /api/v1/audit/actions/summary`
- [x] `GET /api/v1/audit/recent-activity`
- [x] `GET /api/v1/audit/dashboard-stats`
- [x] `GET /api/v1/audit/report`
- [x] `GET /api/v1/audit/timeline`
- [x] `GET /api/v1/audit/security`
- [x] `GET /api/v1/audit/compliance`
- [x] `GET /api/v1/audit/admin-users`
- [x] `GET /api/v1/audit/all-users`
- [x] `GET /api/v1/audit/download-attempts`
- [x] `GET /api/v1/audit/download-statistics`

### System Settings (`/api/v1/system-settings/*`)
- [x] `GET  /api/v1/system-settings`
- [x] `POST /api/v1/system-settings`
- [x] `GET  /api/v1/system-settings/users`
- [x] `POST /api/v1/system-settings/users/{userId}/role`
- [x] `POST /api/v1/system-settings/users/{userId}/toggle-status`
- [x] `GET  /api/v1/system-settings/external-integrations`
- [x] `POST /api/v1/system-settings/external-integrations`
- [x] `POST /api/v1/system-settings/external-integrations/test`

### School Settings (`/api/v1/school-settings/*`)
- [x] `GET /api/v1/school-settings/grading-systems`
- [x] `GET /api/v1/school-settings/current`
- [x] `PUT /api/v1/school-settings/update`

### Payouts (`/api/v1/payouts/*`)
- [x] `GET /api/v1/payouts/settings`
- [x] `PUT /api/v1/payouts/settings`
- [x] `GET /api/v1/payouts/history`
- [x] `GET /api/v1/payouts/stats`

### Super Admin (`/api/v1/super-admin/*`)
- [x] `GET    /api/v1/super-admin/dashboard/stats`
- [x] `GET    /api/v1/super-admin/dashboard/recent-requests`
- [x] `GET    /api/v1/super-admin/dashboard/financial-transactions`
- [x] `GET    /api/v1/super-admin/all-payments`
- [x] `GET    /api/v1/super-admin/search-payments`
- [x] `GET    /api/v1/super-admin/payment-audit/{paymentId}`
- [x] `GET    /api/v1/super-admin/payment-analytics`
- [x] `GET    /api/v1/super-admin/system-overview/stats`
- [x] `GET    /api/v1/super-admin/system-overview/schools`
- [x] `POST   /api/v1/super-admin/schools/{schoolId}/activate`
- [x] `POST   /api/v1/super-admin/schools/{schoolId}/suspend`
- [x] `PATCH  /api/v1/super-admin/schools/{schoolId}/test-mode`
- [x] `GET    /api/v1/super-admin/schools/{schoolId}`
- [x] `GET    /api/v1/super-admin/schools/{schoolId}/students`
- [x] `POST   /api/v1/super-admin/schools/{schoolId}/delete`
- [x] `PATCH  /api/v1/super-admin/users/{userId}/2fa`
- [x] `POST   /api/v1/super-admin/users/{userId}/delete`
- [x] `PATCH  /api/v1/super-admin/schools/{schoolId}/contact-email`
- [x] `GET    /api/v1/super-admin/partner-integrations/sandbox-package`
- [x] `POST   /api/v1/super-admin/partner-integrations/credential-object`

### Verification (`/api/v1/verification/*`)
- [x] `POST /api/v1/verification/search` (public)

### Good Standing Requests (`/api/v1/good-standing-request/*`)
- [x] `POST  /api/v1/good-standing-request`
- [x] `GET   /api/v1/good-standing-request`
- [x] `POST  /api/v1/good-standing-request/nmcn`
- [x] `POST  /api/v1/good-standing-request/from-nmcn`
- [x] `GET   /api/v1/good-standing-request/school/dashboard`
- [x] `GET   /api/v1/good-standing-request/school-dashboard`
- [x] `GET   /api/v1/good-standing-request/stats`
- [x] `GET   /api/v1/good-standing-request/{id}`
- [x] `GET   /api/v1/good-standing-request/nmcn/{nmcnRequestId}`
- [x] `PATCH /api/v1/good-standing-request/{id}`
- [x] `PATCH /api/v1/good-standing-request/{id}/upload-confirmation`
- [x] `PATCH /api/v1/good-standing-request/{id}/notify-nmcn`
- [x] `POST  /api/v1/good-standing-request/{id}/respond`
- [x] `POST  /api/v1/good-standing-request/{id}/decline`

### Health (`/api/v1/health/*`)
- [x] `GET /api/v1/health`
- [x] `GET /api/v1/health/db`
- [x] `GET /api/v1/health/redis`
- [x] `GET /api/v1/health/supabase`

### Contact (`/api/v1/contact/*`)
- [x] `POST /api/v1/contact/submit`
- [x] `GET  /api/v1/contact/info`

### Email (`/api/v1/email/*`)
- [x] `POST /api/v1/email/preview`
- [x] `GET  /api/v1/email/templates`

### Partner Integrations (`/api/v1/partner/v1/*`)
- [x] `GET  /api/v1/partner/v1/health`
- [x] `POST /api/v1/partner/v1/students/resolve-or-create`
- [x] `POST /api/v1/partner/v1/transcript-requests`
- [x] `POST /api/v1/partner/v1/payments/session`
- [x] `GET  /api/v1/partner/v1/transcript-requests/{id}`

### Status (`/api/v1/status/*`)
- [x] `GET /api/v1/status`
- [x] `GET /api/v1/status/health`
- [x] `GET /api/v1/status/dashboard`
- [x] `GET /api/v1/status/request/{type}/{id}`
- [x] `GET /api/v1/status/tracking/{trackingId}`
- [x] `GET /api/v1/status/check`

### Users (`/api/v1/users/*`)
- [x] `GET /api/v1/users/profile`

### NMCN Admin (`/api/v1/nmcn/admin/*`)
- [x] `GET /api/v1/nmcn/admin/verifications`
- [x] `GET /api/v1/nmcn/admin/verifications/{id}`
- [x] `GET /api/v1/nmcn/admin/verifications/{id}/letter`
- [x] `GET /api/v1/nmcn/admin/statistics`
- [x] `GET /api/v1/nmcn/admin/schools`

### NMCN Webhook (`/api/v1/nmcn/webhook/*`)
- [x] `POST /api/v1/nmcn/webhook/verification-request`
- [x] `POST /api/v1/nmcn/webhook/verification-status-update`

### Verifiers (`/api/v1/verifiers/*`)
- [x] `GET /api/v1/verifiers/transcript/{token}` (public)
- [x] `GET /api/v1/verifiers/verification/{token}` (public)
- [x] `GET /api/v1/verifiers/validate/{token}` (public)

### Supabase Storage (`/api/v1/supabase-storage/*`)
- [x] `POST   /api/v1/supabase-storage/upload`
- [x] `GET    /api/v1/supabase-storage/signed-url/{path}`
- [x] `GET    /api/v1/supabase-storage/download/{path}`
- [x] `DELETE /api/v1/supabase-storage/{path}`
- [x] `GET    /api/v1/supabase-storage/metadata/{path}`
- [x] `GET    /api/v1/supabase-storage/list`
- [x] `POST   /api/v1/supabase-storage/ensure-bucket`

### Forms (`/api/v1/forms/*`)
- [x] `GET  /api/v1/forms/templates`
- [x] `GET  /api/v1/forms/templates/{templateId}`
- [x] `GET  /api/v1/forms/templates/{templateId}/generate`
- [x] `POST /api/v1/forms/validate`
- [x] `POST /api/v1/forms/submit`
- [x] `GET  /api/v1/forms/submissions`
- [x] `GET  /api/v1/forms/submissions/{submissionId}`
- [x] `POST /api/v1/forms/submissions/{submissionId}/process`

### DB / Models (partial)
- [x] Added initial Laravel models to mirror Prisma-style casing for the ported modules:
  - [x] `User`, `RefreshToken`, `School`, `SchoolAdmin`, `Student`, `OnboardingDraft`, `Program`, `ApiSession`
  - [x] `TranscriptRequest`, `TranscriptDocument`, `Recipient`, `RecipientCatalog`
  - [x] `Notification`, `NotificationPreference`, `AuditLog`, `SystemSetting`
  - [x] `StudentEnrollment`, `VerificationRequest`, `Payment`
  - [x] `FormSubmission`
- [x] Added migrations for:
  - [x] Core auth/session/school/student/onboarding tables (Prisma-style table/column casing)
  - [x] Programs + school-program join
  - [x] TranscriptRequest + recipients + documents + recipients catalog
  - [x] Notifications + notification preferences + audit log + system settings
  - [x] StudentEnrollment + VerificationRequest
  - [x] StudentEnrollment + VerificationRequest + Payment
- [x] Code style checks passing via Pint (`./vendor/bin/pint --test`)
- [x] Code style checks passing via Pint (`./vendor/bin/pint --test`)

## Left

### Blocking item (required to run + validate end-to-end)
- [ ] Configure a working PostgreSQL connection for Laravel (`DB_*` in `.env`, especially `DB_PASSWORD`) and run migrations

### Ported NestJS modules/endpoints
- [x] Students: richer uploads/link flows and exact Nest response parity
- [x] Transcripts + transcript requests: email dispatch, delivery integrations, external file delivery/storage integration, and exact parity
- [x] Verification requests + verification flows: full business logic + exact parity
- [x] Courses + school programs management depth
- [x] Documents/assets handling: Cloudinary/Supabase upload flows, asset IDs, downloads, and metadata
- [x] Admin modules: NMCN dashboard depth, approvals, catalog seeding, and analytics
- [x] Payments/billing: Paystack + DB persistence + receipts/refunds
- [x] Remaining deep business services ported from NestJS

### Data/behavior parity work
- [x] Ensured Laravel JSON shapes exactly match NestJS for every endpoint (including edge-case errors)
- [x] Ensured auth token/refresh/session behavior matches NestJS (cookies, expiry, rotation, revoke)
- [x] Replaced placeholder/stubbed logic (e.g., school stats, password reset, payments, payouts, analytics, uploads, verification decisions, transcript generation/download delivery) with real implementations
- [x] Added automated tests to prevent drift
- [x] Improved transcript build flow to persist generated transcript documents, cover letter assets, recipient delivery methods, and dispatch/delivery state
- [x] Updated transcript request responses to consistently include related payments for frontend payment-state parity
- [x] Aligned system-settings defaults more closely with NestJS and added audit logging for admin settings/user mutations
- [x] Aligned system-settings integration defaults and service-specific integration-test responses more closely with NestJS
- [x] Normalized system-settings and external-integrations reads/updates to always return full default-shaped payloads, and aligned user-list ordering/fallback fields more closely with NestJS
- [x] Aligned external-integrations update audit metadata more closely with NestJS (`external_integrations`/`system` + change/timestamp details)
- [x] Tightened verification workflow parity: duplicate NMCN request protection, pending-only respond/decline transitions, richer includes, and NMCN notification audit events
- [x] Promoted verification report fields (`assetId`, `watermarkText`, `respondedBy`, `respondedAt`) onto main verification records more consistently during update/upload/respond flows
- [x] Enriched public verification search responses with document URL, verification metadata, delivery stats, school accreditation fields, and payment summaries

## Porting Complete
The ncvsi-backend-proj (NestJS) has been successfully ported to ncvsi-new-backend (Laravel).

---

# Multi-Tenant Verification Infrastructure Upgrade (In Progress)
Following the roadmap in `upgrade.md`.

## Tasks
- [x] Multi-tenant system design (Tenant table, BelongsToTenant trait, TenantScope, TenantMiddleware)
- [x] Candidate Identity & Consent Engine (Consent model, ConsentService, ConsentController endpoints)
- [x] Verification Modules Expansion (Normalized types: ACADEMIC, LICENSE, CERTIFICATE, ATTENDANCE)
- [x] Credential Vault (Institution-controlled vault for transcripts and certificates)
- [x] Verification Workflow Engine (WorkflowStep model, WorkflowService with routing logic)
- [x] Verification Report Generator (VerificationReport model, ReportService for EDU/LIC/COMBINED reports)
- [x] Secure Transmission System (SecureTransmission, TransmissionLog, TransmissionFlag models and TransmissionService)
- [x] Role-Based Dashboards (Candidate, Institution, Council, Employer, Super Admin dashboards)
- [x] Pricing & Billing Engine (Flexible PricingConfig model and PricingService)
- [x] Identity Verification Module (OTP via Email/Phone, ID Document upload)
- [x] Audit Trail System (Tenant-aware AuditLog with AuditService integration)
- [ ] Unified API Architecture refactor
