# Sclone backend — installation

Two commands after prerequisites. ~5 minutes.

---

## 1. Prerequisites

| Tool | Version | Check |
|------|---------|-------|
| PHP (XAMPP) | **8.2+** | `php -v` |
| Composer | 2.x | `composer -V` |
| MySQL (XAMPP) | 8.x | running in the XAMPP control panel |

Enable these in `C:\xampp\php\php.ini` (uncomment by removing the leading `;`):

```
extension=pdo_mysql
extension=mbstring
extension=openssl
extension=fileinfo
extension=gd
extension=zip
extension=intl
extension=curl
extension=bcmath
extension=exif
```

Restart Apache after editing.

---

## 2. Create the database

Open <http://localhost/phpmyadmin> and run:

```sql
CREATE DATABASE sclone_local CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```

---

## 3. Apache virtual host

Add to `C:\xampp\apache\conf\extra\httpd-vhosts.conf`:

```apache
<VirtualHost *:80>
    ServerName sclone.test
    ServerAlias *.sclone.test
    DocumentRoot "C:/xampp/htdocs/Sclone/backend/public"
    <Directory "C:/xampp/htdocs/Sclone/backend/public">
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
```

Make sure `httpd.conf` has this line uncommented:

```
Include conf/extra/httpd-vhosts.conf
```

Add to `C:\Windows\System32\drivers\etc\hosts` (open Notepad as administrator):

```
127.0.0.1  sclone.test
127.0.0.1  freshcart.sclone.test
127.0.0.1  zaika.sclone.test
```

Restart Apache.

---

## 4. Install

```bash
cd C:\xampp\htdocs\Sclone\backend
composer install
composer run setup
```

`composer run setup` copies `.env`, generates the app key, runs every migration, seeds the demo data, and links storage.

If `composer install` complains about a missing extension, go back to step 1.

---

## 5. Verify

### Panels

| Panel | URL | Email | Password |
|-------|-----|-------|----------|
| Super admin | <http://sclone.test/super-admin> | `super@sclone.test` | `password` |
| Admin — FreshCart | <http://freshcart.sclone.test/admin> | `admin@freshcart.test` | `password` |
| Vendor — FreshCart | <http://freshcart.sclone.test/vendor> | `vendor@freshcart.test` | `password` |
| Admin — Zaika | <http://zaika.sclone.test/admin> | `admin@zaika.test` | `password` |

The Admin and Vendor panels take their primary colour from the tenant's branding row — FreshCart renders green, Zaika red. That's the white-label engine working end to end.

### API

Grab a tenant key:

```bash
php artisan tinker --execute="echo App\Domain\Tenancy\Models\Tenant::where('slug','freshcart')->value('tenant_key');"
```

Then:

```bash
curl http://sclone.test/api/v1/ping -H "X-Tenant-Key: PASTE_KEY_HERE"
curl http://sclone.test/api/v1/verticals -H "X-Tenant-Key: PASTE_KEY_HERE"
curl http://sclone.test/api/v1/config -H "X-Tenant-Key: PASTE_KEY_HERE"

curl -X POST http://sclone.test/api/v1/stores/nearest \
  -H "X-Tenant-Key: PASTE_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d "{\"latitude\":19.1197,\"longitude\":72.8464,\"vertical\":\"instamart\"}"
```

FreshCart returns four verticals; Zaika returns two (`food`, `dining`) — the vertical gating is real, not cosmetic.

### Tests

```bash
php artisan test
```

Expect 243 passing — Phase 1 (21), Phase 2 auth (65 + 12 CVE-regression), Phase 3 catalog (48), Phase 4 checkout (37), Phase 5 lifecycle (22), Phase 5b wine compliance (17), Phase 5c dining (21). `TenantIsolationTest` is the important one — it proves tenant A cannot see tenant B's data.

The suite runs on in-memory SQLite and needs no database of its own. `composer analyse` (Larastan level 6) and `composer lint` (Pint) should both come back clean.

### Signing in through the API (Phase 2)

```bash
# 1. request a code — it is printed to storage/logs/laravel.log by the log SMS driver
curl -X POST http://sclone.test/api/v1/auth/otp/request \
  -H "X-Tenant-Key: <freshcart tenant_key>" -H "X-App: customer" \
  -H "Content-Type: application/json" -d '{"phone":"9999900001"}'

# 2. exchange it for a bearer token
curl -X POST http://sclone.test/api/v1/auth/otp/verify \
  -H "X-Tenant-Key: <freshcart tenant_key>" -H "X-App: customer" -H "X-Device-Id: dev-1" \
  -H "Content-Type: application/json" -d '{"phone":"9999900001","code":"<code>"}'
```

Vendor staff use `POST /api/v1/auth/login` with `X-App: vendor` — `vendor@freshcart.test` / `password`.

---

## 6. Background processes (optional in Phase 1)

Separate terminals:

```bash
php artisan queue:work        # jobs: builds, imports, notifications
php artisan reverb:start      # websockets on :8080
php artisan schedule:work     # scheduled tasks
```

---

## Troubleshooting

| Symptom | Fix |
|---------|-----|
| `Class 'PDO' not found` | Enable `pdo_mysql` in `php.ini`, restart Apache |
| Subdomain shows the XAMPP dashboard | `ServerAlias *.sclone.test` missing, or the hosts entry isn't added |
| `SQLSTATE[HY000] [1049] Unknown database` | Create `sclone_local` (step 2) |
| `Specified key was too long` | MySQL < 5.7 — upgrade XAMPP |
| Filament assets 404 | `php artisan filament:assets` |
| Blank page, no error | Check `storage/logs/laravel.log`; ensure `storage/` is writable |
| `Target class [platform] does not exist` | `php artisan config:clear && php artisan optimize:clear` |

---

## What exists after this

The whole backend through Phase 5c, plus the Phase 11 white-label control plane: multi-tenancy with row-level isolation, four verticals, OTP auth, catalog and search, cart, checkout and payments, the order and reservation lifecycles, wine compliance — and tenant provisioning, branding, DNS-verified custom domains, plan limits and audited impersonation.

Not built yet: the Flutter apps (Phases 6–9), the tenant admin panel's later screens (Phase 10), and the build pipeline (Phase 12).

See `../docs/PROGRESS.md` for the live state and the next action.

---

## Trying the white-label engine (Phase 11)

```bash
php artisan migrate:fresh --seed
```

Open <http://sclone.test/super-admin> (`super@sclone.test` / `password`):

1. **Tenants → Provision workspace** — four steps, then a live workspace with verticals, an owner, its subdomain and a `config.json`.
2. **Tenants → ⋮ → Branding** — upload a square logo, hit *Generate assets*, and every Android density appears under `storage/app/tenants/{slug}/assets/`. The collapsible panel at the bottom shows the exact config CI will fetch.
3. **Tenants → edit → Domains** — add a custom host. It stays `pending` and routes nothing until the TXT record it shows you is published; that is deliberate (ADR-039). `POST …/verify` from the panel or the API re-checks, and the scheduler retries hourly.

`WHITELABEL_DISK` decides where all of that lands — `local` (i.e. `storage/app/`) in development, object storage the CI runner can read in production.

Asset generation needs an image extension. GD ships with XAMPP and is enough; set `WHITELABEL_IMAGE_DRIVER=imagick` if the server has Imagick, which resamples the large downscales better.
