# Build Plan requirements checklist

Each row is a requirement from "Nova Community Alliance (NCA): Laravel Build Plan". **Status**: Done (built), Partly (built with a stated limit), Later (deliberately deferred by the plan or needs an outside agreement), Differs (built a different way, reason given).

**How to verify:** run `php artisan nca:check` and `php artisan test`, then use the "Try it" column. The automated tests are in `tests/`.

> Honest note: this code was written and statically checked (syntax, route names, controller methods, columns vs models, Blade templates) but the framework could not be installed where it was written, so the first full run is on your computer. `install.bat` runs `php artisan test` at the end: send me any failure and it will be fixed.

## Section 1-2: What it is, stack

| Requirement | Status | Where / how |
|---|---|---|
| Public website, member portal, back office, partner tab | Done | Public pages, `/member`, `/admin`, `/partner` |
| Laravel 12, PHP 8.3, MySQL | Done | Works on PHP 8.2+; SQLite locally, MySQL live |
| Blade + Tailwind + Alpine | Done | `resources/views`, `resources/js/app.js` |
| Livewire wizard | Differs | Plain Blade + Alpine wizard (same 4 steps, phone-first). Fewer moving parts to host |
| Filament back office | Done | `/admin` (Filament 3.3) |
| Breeze/Fortify auth | Differs | Own small auth (`AuthController`): login by email or phone, reset by email link |
| spatie/laravel-permission | Done | 7 roles, `RoleSeeder` |
| spatie/laravel-activitylog | Done | `activity_log`, Back office > Audit log |
| Redis + Horizon | Differs | Database queue + cron by default; switch to Redis by changing `.env` (HOSTING.md section B) |
| medialibrary, resizing | Differs | Plain private-disk storage (`documents/{uuid}/...`), no resizing |
| simple-qrcode, dompdf | Differs | `bacon/bacon-qr-code` (the library simple-qrcode wraps); receipts use browser Print > Save as PDF |
| maatwebsite/excel | Differs | CSV exports that open in Excel (`/staff/export/*`), fewer dependencies |
| Africa's Talking SMS | Done | `AfricasTalkingSender`; `log` driver for development |
| Daraja STK Push, C2B | Done | `DarajaClient`, `MpesaCallbackController` |
| Ratiba | Later | Needs Safaricom approval; fallback built (section 9) |
| Pest tests | Differs | PHPUnit (`php artisan test`) |
| spatie/laravel-backup | Later | Backup procedure in CONNECT-SERVICES.md section 5 |

## Section 3: Architecture principles

| Requirement | Status | Verify |
|---|---|---|
| Slow work in queues (SMS, email, announcements) | Done | `SendSms`, `SendAnnouncement`, queued mail. OTP SMS and the STK request itself are sent immediately so the user sees errors at once |
| Payment callbacks are the source of truth; never "paid" from the browser | Done | Only `PaymentSettler` marks paid. Test: `PaymentSettlementTest`, `MpesaCallbackTest`. The browser only polls |
| Every sensitive action logged (approve, reject, edit, export, view of ID documents) | Done | Test by opening a member and a document, then Back office > Audit log (super admin) |

## Section 4: Roles

| Role | Status | Verify |
|---|---|---|
| Super Admin, Admin, Verifier, Finance | Done | Sign in with each sample login; menus differ. Test: `AccessControlTest` |
| Agent: only own registrations and commissions | Done | `/agent` shows only members carrying the agent's code |
| Partner: aggregated, anonymised only | Done | `/partner`; groups under 5 are merged into "Other" (`SupportClassesTest`) |
| Member: own profile, card, payments, events | Done | Every member URL is scoped to the logged-in user; test: other member's receipt returns 404 |

## Section 5: Member journey

| Step | Status | Where |
|---|---|---|
| 1 Register (+ agent code, terms and consent) | Done | `/register`, `RegisterMember`, `consents` table with version/IP |
| 2 Phone OTP | Done | `OtpService`: hashed, 5-minute expiry, 5 attempts, resend limit, hourly cap |
| 3 Upload documents (ID front/back, selfie, credential) | Done | Wizard step 3; private disk; camera capture on phones |
| 4 Automatic checks: ID format, duplicate ID/phone, flagged vs pass | Done | `RulesVerifier`; results shown to verifier. Duplicate email blocked at sign-up |
| 5 Choose tier and pay (STK Push), then monthly | Done | `/register/pay`; subscription is created at approval |
| 6 Back-office review: approve, reject with reason, request info | Done | Back office > Members |
| 7 Active: SMS + email, Member Number NCA-2026-000123, QR card | Done | `ApproveMember`, `MemberNumberGenerator` (atomic) |
| 8 Ongoing: monthly, events, directory, renewals | Done | Subscription, events, directory, `nca:renewals` |
| Statuses draft, submitted, pending_payment, pending_review, needs_info, approved, active, suspended, rejected, lapsed | Partly | All exist in `MemberStatus`. `draft` is kept in the browser (wizard autosave), not on the server. `approved` is passed through instantly to `active` |
| Human override with logged reason | Done | Approving a flagged application requires a reason, written to the audit log |

## Section 6: Database

| Requirement | Status |
|---|---|
| Tables users, members, member_profiles, tiers, member_documents, verifications, payments, subscriptions, agents, agent_commissions, counties, sectors | Done |
| sub_counties | Partly: table exists, not seeded (no official list shipped; add via a seeder or import) |
| events, event_registrations (tickets) | Done (ticket = registration with QR code) |
| nominations, nominees, categories | Later (Phase 7) |
| partners, partner_user, impact_metrics, announcements, messages, consents, settings, activity_log | Done (`settings` table exists; fees live in `tiers`) |
| Money as decimal(12,2) | Done |
| National ID encrypted + separate hash column | Done (`encrypted` cast + HMAC `id_hash`) |
| Soft deletes on members, documents | Members: Done. Documents are hard-deleted when replaced or anonymised (so ID photos do not linger) |
| Indexes on phone, id_hash, member_no, status, county_id, checkout_request_id | Done |

## Section 7: Structure

Enums, Services (Payments, Verification, Membership, Notifications), Actions, Jobs, Requests, Middleware, Filament, routes (`web`, `api`, `console`), tests: Done. Policies: Differs (access is enforced by role middleware, query scoping and tests rather than Policy classes). `ProcessMpesaCallback` / `InitiateStkPush` jobs: Differs (done inline for instant feedback; callback processing is fast and idempotent).

## Section 8: Front end

| Requirement | Status |
|---|---|
| Public pages (home, about, tiers, how, events, news, sponsors, impact, contact, terms, privacy) | Done, content from the database where it changes |
| 4-step registration, camera capture, mobile-first, draft | Done (draft in browser) |
| Member portal: dashboard, profile, documents, payments + receipts, subscription pause/resume, events + tickets, messages, directory switch, logout | Done. Tier change: contact NCA. Receipt PDF: print to PDF |
| Back office widgets, member approve/reject, document viewer, reconciliation, agents + commissions, events, announcements, exports, audit log | Done. Settings screen: tiers/fees only (Tiers). County reports: Reports page |
| Partner tab with impact report PDF | Done (print to PDF) |
| Kiswahili from the start, PWA | Done (language switch; manifest + icons). Offline sync: Later |

## Section 9: Payments

| Requirement | Status | Verify |
|---|---|---|
| Payment row `pending`, push, store CheckoutRequestID | Done | Back office > Payments |
| Callback: validate, find by CheckoutRequestID, idempotent, store raw JSON, paid/failed, receipts | Done | `mpesa_callbacks`; tests |
| Amount mismatch / duplicate receipt held for finance | Done | Status *Needs finance review* |
| Reconciliation every few minutes + daily statement comparison | Done | `nca:reconcile` (5 min), Back office > Reconciliation (statement import is manual) |
| Monthly: reminder + STK fallback until Ratiba | Done | `nca:renewals` daily 08:00 |
| Paybill/Till C2B with member number as account | Done | Off by default (`MPESA_C2B_ENABLED`) |
| Finance ledger traceable member to receipt | Done | Payments list, filters, member > Payments tab, CSV export |
| Agent commissions | Done | Accrued on paid registration; "Mark commission paid" action for Finance |

## Section 10: Verification

Rules + duplicates + document review + verifier call-back (manual): Done. `VerificationManager` with swappable drivers (rules, fake): Done. Registry / KRA / business checks: Later (needs lawful access; add a `Verifier` class). Human override logged: Done.

## Section 11: Security and Data Protection Act

| Requirement | Status | Verify |
|---|---|---|
| HTTPS, secure cookies, CSRF | Partly | CSRF default. Force HTTPS at the host (HOSTING.md) and set `APP_URL=https://`; `nca:check` warns |
| Rate limiting on login, OTP, registration, payment | Done | `AppServiceProvider` limiters |
| 2FA for all staff | Done | TOTP; test `test_staff_without_two_factor_is_redirected_to_setup` |
| Encrypted IDs; private documents; signed expiring URLs | Done | 5-minute signed URLs behind login, role and 2FA |
| Role-based access; agents only own registrations | Done | Tests |
| Audit log of views and exports | Done | "opened member record", "viewed identity document", "exported ..." |
| Consent capture, versioned | Done | `consents` |
| Export my data / correct my data / request deletion | Done | Profile page; Back office > Deletion requests; erase anonymises |
| ODPC registration, privacy policy | Later | Your task (CONNECT-SERVICES.md section 6); policy text is a placeholder for your lawyer |
| Retention policy, encrypted off-site backups | Partly | Housekeeping job anonymises old rejected applications; backups are a host task |
| Restrict callbacks to Safaricom IPs, verify payloads | Done | URL secret + optional `MPESA_ALLOWED_IPS` + amount check + status query |

## Section 12: Improvements

Member number + QR + public verify page, tiers, agent portal + commissions, directory with opt-in, multi-language, PWA manifest, event ticketing (QR ticket; gate check-in screen not built), fraud signals (duplicate ID/phone/documents, registrations per IP): Done. Referral codes: shown on the card page but not yet tracked. Awards integration, mentorship, scholarships, sponsor portal, WhatsApp, offline sync: Later.

## Sections 13-17

Phases 1-5 are built; Phase 6 (hardening) is your go-live list in HOSTING.md plus `nca:check --live`; Phase 7 is later. Risks: mitigations above.
