# DroidTester Publisher Panel

Web dashboard for app developers (publishers) to submit Android apps for beta testing on the DroidTester platform.

**Status:** Phase 1 + Phase 2 + Phase 3 + Phase 4 complete (Foundation + Auth + Dashboard + Add Campaign).

---

## What's in this build

### ✅ Phase 1 — Foundation
- PDO singleton (`includes/db.php`)
- DB-backed custom session manager (`includes/session.php`)
- CSRF protection (`includes/csrf.php`)
- Redis-backed rate limiter (`includes/rate-limit.php`)
- Input validator (`includes/validator.php`)
- Mail helper using external SMTP URL (`includes/mail.php`)
- Auth guards (`includes/auth-guard.php`)
- File-based + DB audit logger (`includes/logger.php`)
- Bootstrap loader for env / constants / autoload (`config/bootstrap.php`)
- Master `.htaccess` — clean URLs, HTTPS, security headers, GZIP, cache, file blocking
- DB schema with all `publisher_*` tables + `pub_purchase_history` ALTERs (`database/migrations/001_publisher_schema.sql`)

### ✅ Phase 2 — Authentication
- `/api/auth/register` — signup with optional OTP
- `/api/auth/verify-otp` — OTP verification
- `/api/auth/login` — login with optional 2FA
- `/api/auth/forgot` — send reset link
- `/api/auth/reset` — password reset via token
- `/api/auth/logout` — destroy session
- `/api/auth/resend-otp` — resend OTP with cooldown
- Pages: `/login`, `/register`, `/verify-otp`, `/forgot-password`, `/reset-password`

### ✅ Phase 3 — Dashboard, Profile, Campaigns
- `/api/dashboard/stats` — counts + recent campaigns + chart data
- `/api/campaign/list` — filter (all/active/review/completed/rejected) + search + pagination
- `/api/campaign/details` — single campaign with tester list
- `/api/profile/get` — full profile + quick stats
- `/api/profile/update` — name, phone, country, company
- `/api/profile/delete` — soft delete with password confirmation
- `/api/credit/history` — purchase history paginated
- Pages: `/dashboard` (live stats + chart + recent), `/dashboard/campaigns` (filter + search + paginate), `/dashboard/campaigns/{id}` (full details + tester list), `/dashboard/profile` (collapsible sections + edit form), `/dashboard/credit-history` (expandable rows), `/dashboard/search?q=…` (redirects to campaigns), `/dashboard/campaigns/{id}/approvals` (Phase 6 placeholder), `/dashboard/campaigns/add` (Phase 4 placeholder)
- Shared layout partials: `sidebar.php`, `topbar.php`, `modals.php`, `dashboard-head.php`, `dashboard-foot.php`
- Updated `modals.js`: real logout via API, real delete account flow with password
- Live `csrf-token` meta tag + `window.api()` helper that auto-attaches it to all dashboard AJAX

### ✅ Phase 4 — Add Campaign + Checkout
- `/api/campaign/plans` — live plans from `tester_pkg_plans` (no hardcoded prices in JS)
- `/api/campaign/create` — multipart upload, image resize to 512×512 via GD with graceful fallback, package ID auto-extracted from Play Store URL, inserts `pub_purchase_history` with `status_admin = 0`
- `/api/campaign/checkout-summary` — returns campaign + plan + computed price (subtotal + GST + platform fee + total) + available gateways for currency
- `/dashboard/campaigns/add` — full form: drag-drop icon upload with preview, live package extraction, char counter, dynamic plan cards, validation
- `/dashboard/checkout?cid=X` — read-only summary, billing info, plan card, line-item breakdown, dynamic gateway list, T&C checkboxes, Pay button (Phase 5 wires the actual payment)
- `uploads/.htaccess` — defense-in-depth: blocks all script execution, allows only image MIME serving

### 🚧 Coming next (Phase 5+)
- Cashfree primary integration (createOrder + webhook + verify)
- Add Razorpay / PhonePe / Stripe / PayPal one at a time
- Tester approve / reject + start campaign (Phase 6)
- Polish, SEO, launch (Phase 7)

---

## Prerequisites

- **PHP 8.2+** (8.0 will work, 8.2 recommended)
- **MySQL 5.7+** or MariaDB 10.3+
- **Apache 2.4+** with `mod_rewrite`, `mod_headers`, `mod_deflate`, `mod_expires`
- **Redis** (optional but strongly recommended — for rate limiting & OTP cooldowns)
- **PHP extensions:** `pdo_mysql`, `curl`, `json`, `fileinfo`, `mbstring`, `redis` (optional)
- **External mailer** — a separate URL endpoint that accepts `?email=…&token=…` and sends the SMTP mail (same as DroidTester Android backend)

---

## Setup (cPanel / Hostinger)

### 1. Upload files
Upload everything to the publisher subdomain folder, e.g. `/home/USERNAME/public_html/publisher.droidtester.com/`

Required folder permissions:
```
chmod 755 .
chmod 755 dashboard api includes config payment uploads
chmod 700 logs                   # writeable but not world-readable
chmod 600 .env                   # readable only by web server
chmod 755 uploads/app-icons      # web server must be able to write
```

### 2. Create `.env`
```bash
cp .env.example .env
nano .env
```
Fill in:
- `APP_URL` — your full panel URL (e.g. `https://publisher.droidtester.com`)
- `DB_*` — same DB as Android backend (`panelexploreapps_droidtester`)
- `REDIS_*` — set `REDIS_ENABLED=0` if you don't have Redis (works but no rate limiting)
- `COOKIE_DOMAIN` — set to `.droidtester.com` (note leading dot) so cookies work across subdomains, or leave blank for single-domain
- `SMTP_SIGNUP_URL` and `SMTP_FORGET_URL` — your external mailer endpoints
- `OTP_SIGNUP_ENABLED=0` if you want signups without email verification (good for testing)

### 3. Run database migrations
Open phpMyAdmin → select `panelexploreapps_droidtester` → SQL tab → paste contents of:
```
database/migrations/001_publisher_schema.sql
```
Then run.

⚠️ **Backup first** if production. The ALTER on `pub_purchase_history` and `tester_pkg_plans` only adds columns; it's non-destructive but always backup.

### 4. Verify `.htaccess` is active
Visit `https://publisher.droidtester.com/login` — should load. If you get a 500 error or "Not Found":
- Confirm `mod_rewrite` is enabled (cPanel → MultiPHP INI Editor or contact host)
- Check `AllowOverride All` is set in Apache config

### 5. Test the flow
1. Visit `/register`
2. Fill form → if `OTP_SIGNUP_ENABLED=1`, you go to `/verify-otp` → enter OTP from email
3. Land on `/dashboard` (placeholder page) showing your account info
4. Click logout → back to `/login`
5. Try `/forgot-password` → check your mailer fires

---

## URL structure (clean — no `.php` shown)

| URL | What it does |
|---|---|
| `/` | Landing page (or redirect to dashboard if logged in) |
| `/login` | Login form |
| `/register` | Signup form |
| `/verify-otp?purpose=signup&email=…` | OTP entry screen |
| `/forgot-password` | Request reset link |
| `/reset-password?token=…` | Set new password |
| `/logout` | Destroy session |
| `/dashboard` | Logged-in home (placeholder for now) |
| `/privacy-policy`, `/terms-condition` | Legal pages |
| `/api/auth/{action}` | All auth APIs (POST, JSON) |

Pretty campaign URLs and payment routes activate in Phase 4–5.

---

## File map

```
publisher.droidtester.com/
├── .env                         # YOU CREATE THIS (from .env.example)
├── .env.example                 # template
├── .htaccess                    # clean URLs + security
├── README.md                    # this file
│
├── index.php                    # landing redirect
├── login.php                    # /login
├── register.php                 # /register
├── verify-otp.php               # /verify-otp
├── forgot-password.php          # /forgot-password
├── reset-password.php           # /reset-password
├── logout.php                   # /logout
├── index.html                   # actual landing markup (served by index.php)
├── privacy-policy.html
├── terms-condition.html
│
├── config/
│   └── bootstrap.php            # ALWAYS required at top of every entry point
│
├── includes/                    # core helpers — DENIED to public via .htaccess
│   ├── db.php
│   ├── redis.php
│   ├── session.php
│   ├── csrf.php
│   ├── rate-limit.php
│   ├── validator.php
│   ├── response.php
│   ├── logger.php
│   ├── mail.php
│   └── auth-guard.php
│
├── api/
│   └── auth/
│       ├── register.php         # POST /api/auth/register
│       ├── verify-otp.php
│       ├── login.php
│       ├── forgot.php
│       ├── reset.php
│       ├── resend-otp.php
│       └── logout.php
│
├── dashboard/
│   └── index.php                # placeholder — real one in Phase 3
│
├── database/
│   └── migrations/
│       └── 001_publisher_schema.sql
│
├── assets/                      # CSS, JS, images (from original UI zip)
│   ├── css/
│   ├── js/
│   └── images/
│
├── uploads/                     # user uploads — PHP execution disabled
│   └── app-icons/               # populated in Phase 4
│
├── logs/                        # app + audit logs (chmod 700)
│
├── payment/                     # Phase 5
│   ├── gateways/
│   └── helpers/
│
└── vendor/                      # Phase 5 (Composer)
```

---

## Security notes

- **PDO prepared statements only** — no string concat in SQL anywhere
- **Argon2id** password hashing
- **HttpOnly + Secure + SameSite=Lax** cookies
- **CSRF token** required on all state-changing POSTs (auto on AJAX via `X-CSRF-Token` header, hidden field on forms)
- **Rate limiting** on login (10/IP/15min), signup (5/IP/hr), OTP verify (10/email/30min), forgot (3/email/hr)
- **OTP hashed** in DB (bcrypt), max 5 attempts, 10-min expiry
- **Reset tokens** SHA-256 hashed, single-use, 30-min expiry
- **Sessions DB-backed** with sliding expiry — destroyable on password change
- **`.env`, `includes/`, `database/`, `logs/`** all blocked at .htaccess level
- **PHP execution disabled** in `uploads/`
- **Generic error messages** on login (no email enumeration)
- **All output escaped** with `e()` helper

---

## Configuration toggles (no code change needed)

```env
OTP_SIGNUP_ENABLED=1     # 0 = signup without OTP (immediate login)
OTP_LOGIN_ENABLED=0      # 1 = 2FA on every login
OTP_FORGET_ENABLED=1     # 0 = disables /forgot-password
MAINTENANCE_MODE=0       # 1 = show maintenance page (except payment webhooks)
APP_DEBUG=0              # 1 = show errors (NEVER in production)
```

---

## Common issues

**"Database connection failed"** → Check `.env` DB credentials, confirm DB user has access to `panelexploreapps_droidtester`.

**"Critical: .env file missing"** → Create `.env` from `.env.example`. Make sure permissions allow web server to read it (`chmod 644` if needed; `600` only works if web user IS the file owner).

**Login redirects back to login** → Cookie not being set. Check `COOKIE_DOMAIN` (try blank for single domain), `COOKIE_SECURE=0` for HTTP-only local testing.

**"Too many requests" without trying** → Old rate-limit keys in Redis. Run `redis-cli FLUSHDB` (only if it's not shared with other apps).

**OTP email not arriving** → Check `logs/app-*.log`. The `Mail::sendOtp()` call logs success/failure. Verify `SMTP_SIGNUP_URL` is reachable from this server.

**500 error on every page** → Set `APP_DEBUG=1` temporarily, refresh, copy error from `logs/php-error.log`.

---

## Next: build Phase 3

Phase 3 brings the real dashboard. Tell Claude "start Phase 3" to continue.
