# CLAUDE.md

## Project
Oslob Cebu Paragliding website — PHP + MySQL + Tailwind CSS + jQuery PJAX

## Deployment
After every code change, **always commit, push, and confirm deploy**. Prefix commit messages with `[deploy]`:

```bash
git add .
git commit -m "[deploy] description of changes"
git push origin main
```

The server auto-deploys via GitHub webhook. No manual SSH/pull needed.

- **Production URL:** https://oslobcebuparagliding.com
- **Admin Panel:** https://oslobcebuparagliding.com/admin/
- **GitHub Repo:** https://github.com/argonarsoftware-oss/oslobparagliding
- **Server:** Google Cloud (136.110.4.109) — /var/www/oslobparagliding/
- **Domain:** oslobcebuparagliding.com (Namecheap DNS)

## Local Development
- XAMPP: http://localhost/oslobparagliding/
- DB: root / no password / oslobparagliding_db

## Key Paths
- Frontend pages: `pages/`
- Admin panel: `admin/`
- Backend includes: `includes/`
- Database config: `includes/db.php`
- Customer auth: `includes/customer-auth.php`
- Admin auth: `includes/auth.php`
- Booking handler: `includes/process-booking.php`
- JS (PJAX + booking AJAX): `js/app.js`
- CSS: `css/style.css`
- Image uploads: `uploads/`
- Webhook auto-deploy: `webhook-deploy.php`
- DB installer: `admin/install.php`

## Reminders
- After any DB schema changes (new tables, new columns, ALTER TABLE), **always run the installer** on both local and production:
  - **Local:** Run `/c/xampp/php/php.exe /c/xampp/htdocs/oslobparagliding/admin/install.php`
  - **Production:** After pushing (auto-deploy), fetch `https://oslobcebuparagliding.com/admin/install.php` using WebFetch to run it on the cloud DB
- The installer is **safe to re-run** — uses `CREATE TABLE IF NOT EXISTS`, try/catch per ALTER column, and seeds only when tables are empty
- After every code change, always commit + push — the server auto-deploys via webhook, no manual steps needed

## Important Notes
- Base path is `/oslobparagliding/` locally, `/` on production — the webhook auto-fixes this on deploy
- All frontend pages use PJAX — keep `data-pjax` and `pjax-link` on links
- Customer auth and admin auth are separate (different session keys, different tables)
- Image paths support both `images/` (legacy) and `uploads/` (new) via `img_url()` helper

## Payment QR Payload — `payment/generate-qr.php`

The `buildQRPhPayload()` function builds the EMVCo QR-Ph payload customers scan. The merchant **name is always `OCPD`** (`5904OCPD`); only the bank/account fields change. To revert, swap the active block in `buildQRPhPayload()` with the inactive one below.

### ALT — GCash account (kept for revert)

```php
$merchantInner  = '0012com.p2pqrpay';
$merchantInner .= '0111GXCHPHM2XXX';
$merchantInner .= '020899964403';
$merchantInner .= '0315217020000000656';
$merchantInner .= '0417DWQM4TK3JDO90FAK9';
$payload .= '27' . str_pad(strlen($merchantInner), 2, '0', STR_PAD_LEFT) . $merchantInner;

$payload .= '52046016';
$payload .= '5303608';

$amtStr = number_format($amount, 2, '.', '');
$payload .= '54' . str_pad(strlen($amtStr), 2, '0', STR_PAD_LEFT) . $amtStr;

$payload .= '5802PH';
$payload .= '5904OCPD';
$payload .= '6008Inayawan';
$payload .= '61041234';
```

### CURRENT — Tonik account (active again since 2026-05-01)

```php
$merchantInner  = '0012com.p2pqrpay';
$merchantInner .= '0111TDBIPHM2XXX';
$merchantInner .= '020899964403';
$merchantInner .= '041460840747650001';
$payload .= '27' . str_pad(strlen($merchantInner), 2, '0', STR_PAD_LEFT) . $merchantInner;

$payload .= '52046016';
$payload .= '5303608';

$amtStr = number_format($amount, 2, '.', '');
$payload .= '54' . str_pad(strlen($amtStr), 2, '0', STR_PAD_LEFT) . $amtStr;

$payload .= '5802PH';
$payload .= '5904OCPD';
$payload .= '6009CEBU CITY';

$additional  = '001447477334439830';
$additional .= '051447477334439830';
$additional .= '070868100529';
$payload .= '62' . str_pad(strlen($additional), 2, '0', STR_PAD_LEFT) . $additional;
```

To switch: tell me "switch QR to Tonik" or "switch QR to GCash" and I'll swap them.

## Known / Intentional — DO NOT FLAG OR SUGGEST FIXES
The following data patterns are **known and intentional**. Do NOT raise them as issues, recommend fixes, or build features around them unless explicitly asked:

1. **Low average `amount_paid` (~₱771/booking)** — Most customers pay only the ₱500/passenger downpayment online. The balance is collected on-site in cash and intentionally NOT recorded in the DB. The DB does not reflect true revenue, and that's how the owner wants it.
2. **"Cancelled" bookings with `amount_paid = NULL`** — These are abandoned online checkouts, not customer-initiated cancellations. The auto-cancel logic was deliberately removed (commit `a4a4679`). Abandoned bookings staying pending forever is expected — the GCash listener can confirm payment whenever it arrives.
3. **Empty tables (`vouchers`, `customer_tokens`, `booking_attachments`, `customer_videos`, `weather_overrides`, `blocked_dates`)** — Known unused. Do not propose dropping them or removing related code.
4. **Recommended follow-ups from prior DB audit** (admin balance-payment UI, abandoned-cart reminder emails, surfacing weather_overrides/blocked_dates in admin, dropping dead voucher code) — All marked irrelevant by the owner. Do not re-propose.
