# Capita Consultants — Deployment, Operations & Testing Guide

Platform: **startwithcapita.com** · Next.js 16 (App Router, server-rendered) · PostgreSQL · Drizzle ORM

> **Stack note.** The brief asked for PHP/MySQL. This build runs on the platform's required runtime instead: **Node.js + Next.js + PostgreSQL**. All pages are rendered on the server, and every module in the brief is backed by the database. Standard cPanel accounts can run it **only if** the host offers *Setup Node.js App* (CloudLinux Passenger) and PostgreSQL. If not, use a small VPS or a managed Node host (instructions below). A native PHP/MySQL version would be a separate port.

---

## 1. Requirements
| Component | Version |
|---|---|
| Node.js | 20.x or 22.x LTS |
| PostgreSQL | 14+ (tested on 16) |
| Disk | ~600 MB including `node_modules` + document storage |

## 2. Environment variables (`.env`, outside the public web root)
See `.env.example`. **Never commit real secrets.**

| Variable | Purpose |
|---|---|
| `DATABASE_URL` | PostgreSQL connection string |
| `APP_SECRET` | 32+ random characters. Used to encrypt stored secrets such as the SMTP password (AES-256-GCM) |
| `SITE_URL` | `https://startwithcapita.com` |
| `ADMIN_EMAIL` / `ADMIN_PASSWORD` | First super-admin, created on first boot only |
| `PRIVATE_STORAGE_DIR` | Absolute path **outside** the web root for client documents (default `./private-storage/documents`) |
| `SMTP_HOST`, `SMTP_PASSWORD` | Optional env fallbacks. You can also set these in Admin → Settings |
| `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET` | Only after merchant approval |
| `CRON_SECRET` | Protects `/api/cron` |
| `MALWARE_SCAN_URL` | Optional scanner endpoint. Uploads are rejected unless it returns HTTP 200 |

## 3. Installation
1. Create the PostgreSQL database and user.
2. Upload the project and run `npm ci`.
3. Create the schema in one of three ways:
   - run `npx drizzle-kit push`, **or**
   - import `database/schema.sql` with `psql "$DATABASE_URL" -f database/schema.sql`, **or**
   - let the app create it on first boot. The installer runs `database/schema.sql` when tables are missing.
4. Run `npm run build`, then `npm run start` (or point Passenger/PM2 at `next start`).
5. Visit `/api/health`. The first request seeds:
   - all 18 packages
   - the inclusion matrices
   - 18 add-ons
   - tax codes, numbering sequences, menu, FAQs, blog posts, booking availability and email templates
   - the super-admin
6. Sign in at **/admin/login**. The default is `admin@startwithcapita.com` / `CapitaAdmin#2026` unless `ADMIN_PASSWORD` was set. **Change it right away**, then turn on 2FA under *My 2FA*.

### cPanel (Node.js App) layout
```
/home/account/
  capita-app/            ← application (NOT inside public_html)
    .env                 ← chmod 600
    private-storage/     ← chmod 750, client documents
    database/schema.sql
  backups/               ← pg_dump output, chmod 700
  public_html/           ← Passenger proxies the domain to the Node app
```
Set the Application root to `capita-app`, the startup file to `node_modules/next/dist/bin/next` with argument `start`, and the mode to Production. Enable AutoSSL and force HTTPS. Do **not** use 777 permissions anywhere.

### Cron (cPanel → Cron Jobs, daily)
```
15 6 * * * curl -s "https://startwithcapita.com/api/cron?key=YOUR_CRON_SECRET" >/dev/null
```
This expires out-of-date quotations, purges expired sessions and sends 30-day licence renewal reminders.

## 4. SMTP
Go to Admin → Settings → SMTP. Enter the host, port, encryption, user, password, sender and reply-to, then use **Send test email**.
- Every send is logged under Audit & Security → Email delivery log, with status `sent`, `failed` or `not_configured`.
- The UI never reports success unless the SMTP server accepted the message.

## 5. Payments
- **Manual bank transfer:** enable it in Settings and enter the bank details. Orders become *Paid* only when finance confirms receipt (Order → Confirm received / Record verified payment).
- **Stripe hosted checkout:** set `STRIPE_SECRET_KEY` and `STRIPE_WEBHOOK_SECRET`, enable it in Settings, and point the webhook to `https://startwithcapita.com/api/webhooks/stripe`.
  - Events: `checkout.session.completed`, `checkout.session.async_payment_succeeded`, `checkout.session.async_payment_failed`, `checkout.session.expired`.
  - Signatures are checked with a 5-minute tolerance.
  - Duplicate events are ignored (unique `payment_events`).
  - The amount and currency must match the server record.
  - Returning from the payment page does **not** mark an order paid.
- **Telr / PayTabs / Network International:** add each as a provider module in `src/lib/payments.ts` using the same pattern (hosted page + signed callback) once merchant credentials are approved.
- Card data never touches this server.

## 6. Packages & verification workflow
- Both categories are independent: *Virtual Office (0–10 visa quota)* and *Free Zone Formation*.
- Every inclusion row starts as **Pending Verification**, which the public site shows as "Subject to Confirmation". Free zone rows carry the business-supplied *proposed* inclusions as notes.
- **Add to Cart / online payment** can be turned on for a package only after both the *price verification date* and the *authority verification date* are recorded. Until then, the cart sends a **Request for Final Quotation**.

## 7. VAT & billing
- Tax codes: SR 5% · ZR · EX · OS · DISB · PENDING. Manage them under Admin → Tax Codes.
- VAT is worked out **per line**, and only standard-rated lines carry VAT, only when *VAT registered* is on.
- **Tax invoices are blocked** until VAT registration, a 15-digit TRN and the legal name are configured, and no line is `PENDING`.
- Issued documents are immutable snapshots with atomic sequential numbers. Changes go through credit notes.
- PDFs: open any document and click **Print / Save as PDF** (A4 print stylesheet).

## 8. Roles
Super Administrator, Business Manager, Sales Manager, Sales Executive, Finance / Accountant, Application Processing Officer, Content Editor and Customer. The full matrix is under Admin → Staff & Roles. Permissions are enforced on the server for every page, action and API.

## 9. Security checklist (production)
- [ ] Set `FRAME_ANCESTORS="'self'"` in production to block clickjacking (framing is allowed by default for preview tools).
- [ ] HTTPS forced. HSTS, nosniff, Referrer-Policy and Permissions-Policy headers are sent (`next.config.ts`).
- [ ] `.env` chmod 600, outside the web root. `APP_SECRET` set.
- [ ] Default admin password changed and 2FA enabled for all staff.
- [ ] `PRIVATE_STORAGE_DIR` outside the web root, chmod 750.
- [ ] Database user limited to the app database. Daily `pg_dump` to `backups/` plus an off-site copy.
- [ ] `CRON_SECRET` set.
- [ ] Stripe in test mode until live testing passes.

Built-in protections:
- bcrypt password hashing (cost 12)
- httpOnly + SameSite session cookies, with a new session on login
- 8-hour staff sessions
- login throttling: 5 failures per 15 minutes per email/IP
- TOTP 2FA
- Server Actions origin checks (CSRF)
- parameterised queries (Drizzle)
- React output escaping
- magic-byte + extension upload validation, 10 MB limit, random file names, authenticated and audit-logged downloads
- CSV formula-injection guard
- audit log of data changes

## 10. Backup & restore
```
pg_dump -Fc "$DATABASE_URL" > ~/backups/capita-$(date +%F).dump
tar czf ~/backups/docs-$(date +%F).tgz -C ~/capita-app private-storage
pg_restore --clean -d "$DATABASE_URL" ~/backups/capita-YYYY-MM-DD.dump
```

## 11. Feature testing checklist
**Frontend:** all pages load · mega menu works with hover/click/keyboard · mobile drawer and accordion · package filters · compare up to 4 (table on desktop, swipe cards on mobile) · add to cart / quote request · one-package rule · coupons · 7-step checkout · booking slots (double-booking blocked by a unique index) · WhatsApp links carry the encoded package message.

**Backend:** admin login + 2FA · role restrictions (log in as Sales Executive → Tax Settings is denied) · package CRUD, duplicate, archive · inclusion matrix · add-on CRUD · quotation draft → lines with tax codes → send → customer views (status Viewed) → accept → convert to order · pro forma / invoice / tax invoice guard · credit note · receipt · payments, deposits, refunds and balance reconciliation · document request → upload → approve/reject/replace · application status with required evidence · tickets · blog scheduling · settings changes appear on the site.

**Payments (Stripe test mode):** successful card (4242…) · declined (4000 0000 0000 0002) · cancelled checkout · resent event (duplicate ignored) · bad signature (400) · refund recorded.

**Security:** wrong password ×6 (throttled) · `/api/admin/export` without a session (401) · customer opening another customer's document ID (403/404) · non-PDF renamed `.pdf` (rejected) · expired reset token (rejected).

## 12. External dependencies not yet configured
SMTP credentials · VAT registration/TRN/legal name · bank transfer details · Stripe or other gateway credentials · authority-verified package fees and inclusions · social profile URLs · genuine testimonials · malware scanner endpoint.
