# cPanel / shared hosting deployment

Layout (recommended – application **outside** the web root):
```
/home/CPUSER/voiceorder-app/     ← everything from the ZIP except the contents of public/
/home/CPUSER/public_html/        ← contents of voiceorder-app/public/ (index.php, .htaccess, assets/)
```
Subdomain variant: make `app.example.com`'s document root `/home/CPUSER/voiceorder-app/public` (then no copying/editing is needed).

## 1. Database
cPanel → **MySQL® Database Wizard** → create DB + user, grant ALL PRIVILEGES. Note the prefixed names.
## 2. Upload & extract
cPanel → **File Manager** → upload `voiceorder-saas.zip` to `/home/CPUSER/`, **Extract** into `voiceorder-app/`. (`vendor/` is included – Composer is not needed on the server.)
## 3. Public directory
*Option A (preferred):* point the (sub)domain document root to `voiceorder-app/public`.
*Option B:* copy `voiceorder-app/public/*` (including the hidden `.htaccess`) into `public_html/`, then replace `public_html/index.php` with `voiceorder-app/deploy/public_html/index.php` (it loads `../voiceorder-app`). Keep `deploy/.htaccess-root` out of the web root.
## 4. Configure
Copy `.env.example` → `.env` in `voiceorder-app/` (File Manager → edit): `APP_ENV=production`, `APP_DEBUG=false`, `APP_URL=https://…`, `DB_*`, `MAIL_*`.
`.env` is outside the web root in both options; the shipped `.htaccess` also denies dotfiles.
## 5. Key, migrations, seed (needs a shell)
cPanel → **Terminal** (or SSH):
```bash
cd ~/voiceorder-app
php artisan key:generate --force
php artisan migrate --force
php artisan db:seed --force
php artisan config:cache && php artisan route:cache && php artisan view:cache
```
**No Terminal?** cPanel → **Cron Jobs** → add a *temporary* job (e.g. every minute) with the command below, wait for it to run, then **delete the job**:
```bash
cd /home/CPUSER/voiceorder-app && /usr/local/bin/php artisan migrate --force && /usr/local/bin/php artisan db:seed --force && /usr/local/bin/php artisan key:generate --force >> /home/CPUSER/vo-install.log 2>&1
```
(`/usr/local/bin/php` may be `ea-php82` – pick PHP 8.2+ in **MultiPHP Manager** and use the path shown in *Cron Jobs*.) Run `key:generate` **once only**; regenerating the key makes encrypted provider secrets unreadable.
## 6. Permissions
`storage/` and `bootstrap/cache/` writable by the account (755 dirs / 644 files normally suffices; 775 if needed). Never 777.
## 7. Cron
See [CRON.md](CRON.md). ## 8. Mail
Use a cPanel mailbox (SMTP `mail.yourdomain`, port 587/465) in `.env`; verification emails and the free-credit award depend on it. Add SPF/DKIM in cPanel → **Email Deliverability**.
## 9. HTTPS
cPanel → **SSL/TLS Status** → run AutoSSL. The app forces https URLs in production; set `SESSION_SECURE_COOKIE=true`.
## 10. Production cache
After any `.env` change: `php artisan config:clear && php artisan config:cache`.
## 11. Backups
cPanel → **Backup** (full + MySQL). Also schedule a nightly `mysqldump` via cron to a path outside `public_html`. **Back up `.env`'s `APP_KEY` separately**: without it encrypted provider credentials and stored call phone numbers cannot be decrypted. Test a restore.
## 12. Hardening checklist
* `https://yourdomain/.env`, `/storage/logs/laravel.log`, `/vendor/` must all return 403/404 – verify in a browser.
* Remove `VOICEORDER_ADMIN_PASSWORD` from `.env` after seeding. Use a long unique super-admin password.
* PHP version 8.2+, `display_errors=Off`.
See also [Troubleshooting](TROUBLESHOOTING.md).
