# deployment.md

## Environment

- **Production:** retireuk.costusworx.co.za, hosted on cPanel shared hosting (Linux + Apache + PHP-FPM + MySQL 8).
- **Local dev:** Windows machine, PHP not installed locally — all PHP runs on the host. Local editing only, with cPanel File Manager for uploads.
- **PHP version:** 8.5+ required (uses `readonly`, `enum`, `?->`, native enums, named arguments).
- **MySQL version:** 8.0+ (uses `JSON` columns, `utf8mb4`).

---

## Deployment Steps (current — manual cPanel)

1. **Make changes locally** under `C:\Users\User\OneDrive\Developments 2026\Claude\retireuk.costusworx.co.za`.
2. **Bump version** in `config/version.php` AND `public/assets/js/app.js` (the `APP_JS_VERSION` constant).
3. **Update `DEPLOY_NOW.md`** in the repo root with:
   - Version number
   - Files to upload (with paths)
   - SQL migrations (if any)
   - Smoke tests
   - Rollback plan
4. **Open cPanel File Manager.** Navigate to the project root.
5. **Upload each modified PHP file** to its matching path. Overwrite existing.
6. **Upload `public/assets/js/app.js`** if changed.
7. **Run any SQL migrations** via phpMyAdmin (paste content from `database/*.sql`).
8. **Hard-refresh** the browser (Ctrl+F5). The footer version banner should match the new `APP_JS_VERSION`.
9. **Run the smoke tests** in `DEPLOY_NOW.md`. Stop if anything fails — roll back.

### Cron jobs
- **Monthly fund refresh:** `php bin/funds-sync.php --source lse-etf` on the 1st of each month at 02:00 UTC. Runs via cPanel Cron.
- **Daily DB backup:** scheduled via cPanel's automated backup (24h retention).

---

## Configuration

| File | Purpose | Notes |
|---|---|---|
| `config/database.php` | DB credentials, options | `.gitignore`'d. PDO options include `ATTR_EMULATE_PREPARES => false`. |
| `config/app.php` | mc_runs, mc_max_runs, debug toggle, app name | Tracked. |
| `config/version.php` | Single source of truth for app version | Bumped on every deploy. |
| `config/funds.php` | Source registry for fund ingest (LSE ETF config, etc.) | Tracked. |
| `config/billing.php` | Stripe secret key + webhook secret | `.gitignore`'d. |
| `public/.htaccess` | Apache routing + security headers + **env vars** | Contains `SetEnv OPENFIGI_API_KEY ...` and `SetEnv STRIPE_SECRET_KEY ...`. Upload with care — do not expose keys in version control. |

### Environment variables (set in `public/.htaccess` via `SetEnv`)

| Variable | Purpose |
|---|---|
| `OPENFIGI_API_KEY` | OpenFIGI API key for fund metadata lookups. Used by `AdminFigiController` → `OpenFigiClient`. |
| `STRIPE_SECRET_KEY` | Stripe secret key for payment processing (replaces `PAYFAST_PASSPHRASE`). |
| `STRIPE_WEBHOOK_SECRET` | Stripe webhook signing secret for verifying `Stripe-Signature` header on `/billing/notify`. |
| `ENCRYPT_KEY` | AES-256-GCM key for PII encryption (base64-encoded, 32 bytes). UK GDPR Art.32. |
| `APP_ENV` | `production` \| `development` |
| `APP_DEBUG` | `true` to enable error traces (never on production). |

> **Note:** `.htaccess` is blocked from public serving by Apache by default. `SetEnv` values are only visible server-side via `getenv()`. Do not place `.htaccess` in git if it contains real credentials. Keep a `.htaccess.example` in git without real values and maintain the live copy manually on the server.

---

## Backup Strategy

- **Database:** daily snapshot via cPanel's automated backup → retained 7 days on host, weekly copied to OneDrive.
- **Files:** weekly full-site tarball via cPanel → retained 4 weeks on host.
- **Critical configs:** `config/database.php` and `config/billing.php` kept off-server in a password manager.
- **Customer data exports:** monthly CSV export of `clients`, `scenarios`, `simulation_runs` to encrypted local archive.

---

## Rollback Plan

For any failed deploy:

1. Identify what changed (the `DEPLOY_NOW.md` file lists exact paths).
2. If files-only: re-upload the previous version's files from local git. We tag each release as `2026.MM.DD.x` in git.
3. If DB schema changed: re-run the rollback SQL block from `DEPLOY_NOW.md`. Every migration includes a `-- ROLLBACK` block.
4. If state was corrupted: restore yesterday's DB snapshot via phpMyAdmin import.
5. Run the smoke tests for the previous version to confirm restore.

---

## Future deployment improvements (in `backlog.md`)

- **Git-deploy hook** via cPanel "Git Version Control" → auto-pull on push to `main`.
- **Migration runner** — single CLI that applies pending `database/*.sql` files in order.
- **Health endpoint** — `/health` returns `200 OK` with version + DB ping + last cron timestamp.
- **Smoke-test automation** — Playwright run against staging URL after each deploy.
- **CI** — GitHub Actions runs PHP linter (`php -l`) and PHPStan on PR.

---

## mPDF install (optional, manual)

Documented in `INSTALL_MPDF.md` in the repo root. Summary:

1. On local dev machine with Composer: `composer require mpdf/mpdf` in a throwaway folder.
2. Zip the resulting `vendor/` folder.
3. Upload zip to cPanel, extract alongside `app/`.
4. Add `require __DIR__ . '/../vendor/autoload.php';` to `public/index.php`.
5. Create `/storage/mpdf-tmp/` writable directory.
6. `PdfReport::stream()` auto-detects mPDF and uses it; falls back to client-side jsPDF otherwise.

---

## UK-specific configuration notes

- **HMRC tax year** runs 6 April – 5 April. Update `tax_brackets` within 7 days of the Spring Budget (typically March). Process: run the INSERT SQL from `database/uk_tax_YYYY.sql` in phpMyAdmin.
- **UK State Pension rate** uprated each April. Update `state_pension_rates` table with the new weekly rate.
- **ONS CPI / FTSE data** updated annually via Admin → Market Data → Import CSV.
- **Stripe webhooks** — configure the webhook endpoint `https://retireuk.costusworx.co.za/billing/notify` in the Stripe Dashboard. Copy the webhook signing secret to `STRIPE_WEBHOOK_SECRET` in `.htaccess`.
