# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Language & Standards

- PHP 8.3 only. Every file must open with `declare(strict_types=1);`.
- PSR-12 code style.
- All DB queries must use prepared statements (PDO or mysqli).
- All state-changing requests must include CSRF validation.
- Output must be escaped per context: HTML → `htmlspecialchars(..., ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8')`, URL → `rawurlencode`, JS → `json_encode`.
- Exception handling: always catch `Throwable $e`, never catch bare `Exception` alone.
- Forbidden calls: `eval`, `exec`, `shell_exec`, `system`, `passthru`, `popen`, `proc_open`, `var_dump`, `print_r`, `die`, `dump`.
- Do not use `fn()` (arrow functions).
- CSS must be minified in any frontend output.

## Architecture

This is a PHP affiliate/performance-marketing platform ("ndulead") with **three independent modules** deployed as separate web roots, sharing one MySQL database.

### Module Map

| Directory | Role | Entry Point |
|-----------|------|-------------|
| `public/` | Admin/tracker portal — manages trackers, campaigns, domains, shortlinks | `public/index.php` |
| `redirect/` | SRP redirect engine — decodes payload, resolves geo/device, fires click, redirects visitor | `redirect/index.php` |
| `statistics/` | Reporting dashboard — click performance, realtime view, postback conversions | `statistics/login.php` |

### Shared Root Utilities

These files live in the repo root and are `require_once`-d by all three modules:

- `env.php` — custom `.env` parser; exposes `load_env_file()`, `app_env()`, `app_required_env()`.
- `Base64URL.php` — URL-safe base64 encode/decode used to pass click payloads between modules.
- `ip_address.php` — client IP detection that handles Cloudflare and reverse proxies.
- `Mobile_Detect.php` — minimal UA-based device detection.

### Database (single shared MySQL DB — `schema.sql` is canonical)

| Table | Written by | Read by |
|-------|-----------|---------|
| `generate` | `public/` portal | `redirect/` (auth check), `statistics/` |
| `addondomain` | `public/api/` | `public/` portal |
| `offering` | `public/api/` | `redirect/_meetups/r.php` (offer selection) |
| `clickrecord` | `redirect/_meetups/index.php` (clicks), `statistics/postback/` (leads/payout) | `statistics/` dashboard |
| `leadreport` | `statistics/postback/index.php` | `statistics/` dashboard |
| `srp_short_links` | `redirect/api/shorten.php` | `redirect/public_link.php` |
| `shortlinks` | `public/shortlinks/response.php` | `redirect/public_link.php` (fallback) |

`sub_id` / `click_id` values are always stored **UPPERCASE**. `clickrecord` is created at runtime via `meetup_ensure_clickrecord_table()` if it doesn't exist.

### Data Flow

1. **Click generation** — `public/index.php` generates a base64url-encoded token embedding `click_id`, `canonical_url`, `user_lp`, `title`, `image_url`, `lg` (landing mode flag). This token becomes the `sub_id` URL path on the redirect domain.
2. **Redirect** — `redirect/index.php` decodes the token, detects country (MaxMind GeoIP2 from `redirect/databases/GeoLite2-Country.mmdb`) and device (Mobile_Detect), applies geo-block and timed filter logic, then issues a 302 to `redirect/_meetups/index.php?rk=<encoded_payload>` (the `SRP_TARGET_BASE` internal hop).
3. **Click recording** — `redirect/_meetups/index.php` upserts a row in `clickrecord` (PDO), then redirects visitor to the offer URL via `redirect/_meetups/r.php`.
4. **Postback** — affiliate network fires `statistics/postback/index.php?click_id=<payload>&payout=<n>&token=<HMAC>`. Handler decodes the base64url click_id, atomically inserts into `leadreport` and updates `clickrecord.leads`/`payout`.
5. **Short links** — two independent systems: legacy (`shortlinks` table, managed via `public/shortlinks/response.php`, served via `public/s.php`); current (`srp_short_links` table, created via `redirect/api/shorten.php` Bearer-auth API, resolved in `redirect/public_link.php`).

### Connection Patterns (differ per module)

- `public/` — uses `dbObj` class (`connection.php`) which wraps `mysqli_connect`; older files use `connection.config.php` which exposes a bare `$link` (mysqli).
- `redirect/` — `connection.config.php` returns a **PDO** instance (not mysqli). Files use `/** @var PDO $pdo */ $pdo = require __DIR__ . '/connection.config.php';`.
- `statistics/` — hardened `connection.config.php` exposes `$link` (mysqli) with retry logic, socket support, and `DB_PERSISTENT` flag.

### Timed Filter (redirect only)

`redirect/index.php` implements a 5-minute cycle: 120 s "filter window" → 180 s "normal". During the filter window visitors are sent to `SRP_FILTER_URL` instead of the offer. The current filter URL can be overridden at runtime via a shared cache file in `sys_get_temp_dir()/srp_bb/`.

### Cloudflare Integration (`public/` only)

`public/api/cf.sync.php` and `public/api/user.cf.config.php` provision DNS records and store per-tracker `cf_token` / `cf_account_id` in the `generate` table. The `addondomain` table stores `cf_zone_id`, `cf_status`, `cf_ns`. Bypass header `X-Bypass-Key` is compared against `IXG_BYPASS_KEY` env var.

### Vendor (redirect only)

`redirect/vendor/` contains MaxMind GeoIP2 installed via Composer. No other module has a `vendor/` or Composer setup.

## Setup

Each module is configured independently via its own `.env` file copied from `.env.example`:

```
cp public/.env.example public/.env
cp redirect/.env.example redirect/.env
cp statistics/.env.example statistics/.env
```

Apply the database schema once against the shared database:

```
mysql -u <user> -p <dbname> < schema.sql
```

`redirect/databases/GeoLite2-Country.mmdb` must be present (MaxMind free download, requires account). The path is the constant `SRP_GEOIP2LITE_DB` in `redirect/index.php`.

## Linting & Static Analysis

No automated test suite is configured. When logic changes are made, run manually if the tools are installed:

```bash
# Code style
vendor/bin/php-cs-fixer fix --dry-run --diff

# Static analysis
vendor/bin/phpstan analyse --level=8

# PSR-12
vendor/bin/phpcs --standard=PSR12

# Auto-fix style
vendor/bin/php-cs-fixer fix
```

## Review Format

When reviewing code, start findings with:
`[Severity][Area][Impact][Fix]`

Review focus: SQL injection, XSS, CSRF, auth/authorization, session/cookie, upload handling, security headers, CSP nonce, ENV/secrets exposure, logging sensitive data, raw queries, dangerous calls, production bugs, edge cases.

## Workflow

Review/Triage → Reproduce + Baseline → Root Cause Analysis → Implement Fix → Targeted Verification → Refactor → Regression Verification → Optimize → Security/Hardening → Cleanup → Production Build → Smoke Test
