# Kings & Queens Arena — Backend Deployment (tnet.roscaw.com)

This package contains the updated **Kings & Queens Arena** PHP backend for the
live SKYNET host `tnet.roscaw.com`.

- REST API target: `https://tnet.roscaw.com/api`
- WebSocket target: `wss://tnet.roscaw.com/ws`
- Database: `roscaw_tnet` / user `roscaw_sagabe`
- App name/branding: **Kings & Queens Arena** (card-of-jacks logo, TZ +255 mobile
  registration, terms checkbox, password-reset flow, signup bonus removed)

---

## 1. What changed since the last deploy (auth + wallet)

- `includes/AuthApi.php`
  - `POST /auth/register` now **requires** `mobile` (Tanzanian `+255…` or `0…`;
    any 9 digits after the country code) and `terms_accepted=true`.
  - Register **credits the signup bonus** (`security.free_bonus`, 1,000 TZS
    default) to the new user's wallet so they can start playing immediately.
  - Login accepts email / username / mobile (the app sends `+255` + 9-digit mobile).
  - New `POST /auth/forgot-password` (email-only lookup; returns the 6-digit
    `reset_code` in the response for this demo build — no SMS/email gateway yet).
  - New `POST /auth/reset-password` (validates code hash + 15-min expiry, then
    revokes existing sessions).
  - `selfPublic` now returns `mobile`.
- `includes/WalletApi.php`
  - `POST /payments/request` uses the **registered** mobile number (normalized
    via `Helpers::normalizeTzMobile`) and records `phone` in the ledger meta.
  - **Demo: deposits are auto-approved and instantly credited** to the wallet
    (no real M-Pesa/network gateway). This gives players immediate balance to
    create games.
- `includes/Helpers.php`
  - Added `normalizeTzMobile()` — accepts any 9-digit mobile or `255`/`0`/`+255`
    prefixed forms and normalizes to `+255…` 12 digits (dropped the old `^7`
    restriction).
- `config/config.php`
  - `app.env = production`, `app.debug = false`, app name **Kings & Queens Arena**.
- `sql/migration_kqa_auth.sql`
  - Adds `users.mobile` (unique), `users.terms_accepted`,
    `users.reset_code_hash`, `users.reset_code_expires`.

---

## 2. What to upload

Upload the **contents** of this `backend/` folder to your web root so that the
API is reachable at `https://tnet.roscaw.com/api/`:

| Local path              | Remote target (web root)      |
| ----------------------- | ----------------------------- |
| `api/`                  | `<root>/api/`                  |
| `includes/`             | `<root>/includes/`             |
| `config/`               | `<root>/config/`               |
| `sql/`                  | `<root>/sql/` (or your SQL dir)|
| `ws/`                   | `<root>/ws/`                   |
| `router.php`            | `<root>/router.php`            |
| `api/.htaccess`         | ensure it ships (rewrite rules)|

> Replace existing files (do not merge). `uploads/avatars/` is runtime data and
> should **not** be overwritten — leave existing avatar files in place.

### Critical: keep the live database credentials

`config/config.php` in this package already contains the production values
(`roscaw_tnet` / `roscaw_sagabe` / the tnet password). Do **not** upload
`config/config.example.php` over it. If your server enforces per-vhost settings,
verify `config/config.php` after upload matches your live DB.

---

## 3. Run the database migration

**> `#1146 - Table 'roscaw_tnet.users' doesn't exist` means the COPER schema has
> never been imported into the live `roscaw_tnet` database.** The MySQL error
> "#1146" is the standard "table does not exist" error, so `users` (and the whole
> platform schema) is missing. Do the full import below instead of just the
> migration.

### 3a. Full schema import (required when `users` is missing — the round-trip fix)

Against the **live** `roscaw_tnet` database (e.g. via phpMyAdmin on the panel):

1. **Backup** the current `roscaw_tnet` DB first (Export).
2. Import `sql/schema.sql` — this creates every table with
   `CREATE TABLE IF NOT EXISTS` and runs with `SET FOREIGN_KEY_CHECKS = 0`, so it
   is **safe to run over an existing DB** (creates missing tables, never drops or
   wipes data). It already contains the new `users.mobile`,
   `users.terms_accepted`, `users.reset_code_hash`, `users.reset_code_expires`
   columns **and** the `uq_users_mobile` unique key.
3. Import `sql/seed.sql` to load the catalog items + demo users
   (`admin/Admin@1234`, `alice/Alice@1234`, `bob/Bob@1234`).

After running `schema.sql`, the auth columns already exist — **do NOT** also run
`migration_kqa_auth.sql` (it will error on duplicate column/key; that error is
harmless/ignorable if the columns already exist).

### 3b. Upgrade-only path (only when `users` already exists with data)

If `SHOW TABLES` confirms `users` already exists, run ONLY
`sql/migration_kqa_auth.sql` to add the new columns:

```sql
ALTER TABLE `users`
  ADD COLUMN `mobile` VARCHAR(20) NULL COMMENT 'Tanzanian mobile (+255…) used for deposits/withdrawals' AFTER `password_hash`,
  ADD COLUMN `terms_accepted` TINYINT(1) NOT NULL DEFAULT 0 AFTER `mobile`,
  ADD COLUMN `reset_code_hash` CHAR(64) NULL AFTER `terms_accepted`,
  ADD COLUMN `reset_code_expires` DATETIME NULL AFTER `reset_code_hash`,
  ADD UNIQUE KEY `uq_users_mobile` (`mobile`);
```

On a DB that already has some (but not all) of these columns, MySQL will error
on the duplicates — that is safe to ignore if the columns already exist.

### Verify

```sql
SHOW TABLES;                        -- expect users, wallet, wallet_ledger, games, ...
DESCRIBE users;                     -- expect a `mobile` column present
```

---

## 4. WebSocket daemon (real-time matches)

The app connects to `wss://tnet.roscaw.com/ws`. On typical shared hosting you
cannot run a long-lived PHP daemon unless the host allows it (SSH + process
management). Options, in order of preference:

1. **SSH available** — upload `ws/match_server.php` + `ws/algo/*` and run:
   ```bash
   php ws/match_server.php --port=9000
   ```
   (kill previous PHP processes first; the client expects the WS on the `/ws`
   path — terminate at your web server/proxy as needed.)
2. **No SSH** — the REST API (`https://tnet.roscaw.com/api`) works fully for
   auth, wallet, shop, RG, profile; only the realtime match socket (`/ws`) needs
   a persistent process. Arrange a daemon/CRON keep-alive or a host with
   persistent processes.

---

## 5. Smoke test after deploy

```powershell
# health
Invoke-RestMethod https://tnet.roscaw.com/api/health

# register (mobile + terms required; signup bonus granted)
Invoke-RestMethod -Method Post -Uri https://tnet.roscaw.com/api/auth/register `
  -ContentType application/json `
  -Body '{"username":"tester","email":"t@x.com","password":"Password1","country":"TZ","dob":"2000-01-01","mobile":"+255712345678","terms_accepted":true}'

# deposit — auto-approved and credited instantly (demo)
Invoke-RestMethod -Method Post -Uri https://tnet.roscaw.com/api/payments/request `
  -ContentType application/json `
  -Body '{"kind":"deposit","amount":1000}'
```

---

## 6. Known prod TODO

- Password reset currently **returns the `reset_code` in the JSON response**
  (demo only). Wire a real SMS/email gateway so codes are never exposed.
- Deposits are **auto-approved and credited instantly** (demo only). Wire a real
  M-Pesa/network payment callback to approve deposits before releasing funds.
