From 5bbec72b59a171c9dba4cf0e1d50a35223f77ce6 Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Sun, 26 Apr 2026 22:15:02 +0200 Subject: [PATCH] docs: map existing codebase - stack.md - Technologies and dependencies - architecture.md - System design and patterns - structure.md - Directory layout - conventions.md - Code style and patterns - testing.md - Test structure (none) - integrations.md - External services - concerns.md - Technical debt and issues - db_schema.md - Database schema and relationships Co-Authored-By: Claude --- .paul/STATE.md | 4 + .paul/codebase/architecture.md | 157 ++++++++++++++++++++++++++++++++ .paul/codebase/concerns.md | 131 +++++++++++++++++++++++++++ .paul/codebase/conventions.md | 144 +++++++++++++++++++++++++++++ .paul/codebase/db_schema.md | 144 +++++++++++++++++++++++++++++ .paul/codebase/integrations.md | 112 +++++++++++++++++++++++ .paul/codebase/stack.md | 83 +++++++++++++++++ .paul/codebase/structure.md | 159 +++++++++++++++++++++++++++++++++ .paul/codebase/testing.md | 55 ++++++++++++ 9 files changed, 989 insertions(+) create mode 100644 .paul/codebase/architecture.md create mode 100644 .paul/codebase/concerns.md create mode 100644 .paul/codebase/conventions.md create mode 100644 .paul/codebase/db_schema.md create mode 100644 .paul/codebase/integrations.md create mode 100644 .paul/codebase/stack.md create mode 100644 .paul/codebase/structure.md create mode 100644 .paul/codebase/testing.md diff --git a/.paul/STATE.md b/.paul/STATE.md index 3313e38..123b2f1 100644 --- a/.paul/STATE.md +++ b/.paul/STATE.md @@ -29,6 +29,10 @@ PLAN --> APPLY --> UNIFY ## Accumulated Context +### Codebase Mapped +Date: 2026-04-26 +Documents: `.paul/codebase/` (8 files — stack, architecture, structure, conventions, testing, integrations, concerns, db_schema) + ### Decisions - 2026-04-19: Event `purchase` emitowany na `order-confirm`, nie na `przelewy24` (eliminuje falszywe konwersje). - 2026-04-19: Payload ecommerce budowany w backendzie i serializowany bezpiecznie do widoku. diff --git a/.paul/codebase/architecture.md b/.paul/codebase/architecture.md new file mode 100644 index 0000000..81f2739 --- /dev/null +++ b/.paul/codebase/architecture.md @@ -0,0 +1,157 @@ +# Architecture + +**Analysis Date:** 2026-04-26 + +## Pattern Overview + +**Overall:** Custom PHP MVC (no framework) + +**Key Characteristics:** +- URL-based routing via Apache mod_rewrite in `.htaccess` +- Namespace-organized layers (controls, factory, view) +- Custom autoloader mapping namespaces to file paths +- Session-based shopping cart (no cart persistence until order submit) +- Static public methods as controller actions +- Single config file (`config.php`) containing all settings and ticket definitions + +## Layers + +**Controller Layer (`autoload/controls/`):** +- Purpose: Handle HTTP actions, coordinate factory/template calls, return rendered HTML +- Contains: Route action methods (e.g., `main_view()`, `basket_view()`, `przelewy24_response()`) +- Depends on: Factory layer, `\Tpl`, `\S`, `\Html`, `global $settings, $mdb, $user` +- Used by: `\controls\Site::route()` dispatcher + +**Factory/Service Layer (`autoload/factory/`):** +- Purpose: Business logic, database queries, data processing +- Contains: Calendar availability, order creation, user auth, settings management +- Depends on: `global $mdb` (Medoo), `\R::` (RedBeanPHP) +- Used by: Controllers + +**View Layer (`autoload/view/`):** +- Purpose: Layout assembly — wraps controller output in site layout template +- Contains: `show()` dispatcher, layout selection logic +- Depends on: Controllers (via `\controls\Site::route()`), `\Tpl` +- Used by: Entry points (`index.php` calls `\view\Site::show()`) + +**Template Layer (`templates/`):** +- Purpose: HTML rendering with embedded PHP +- Contains: Module-organized `.php` template files +- Variables accessed via `$this->key` (injected by `\Tpl`) +- Used by: Any layer via `\Tpl::view('path/name', ['key' => $value])` + +**Core Utilities (`autoload/`):** +- `\S` — Session management, email, utilities (`autoload/class.S.php`) +- `\Tpl` — Template engine with variable injection (`autoload/class.Tpl.php`) +- `\Html` — Form component builder (`autoload/class.Html.php`) +- `\DbModel` — Simple active-record base class (`autoload/class.DbModel.php`) + +## Data Flow + +**HTTP Request Lifecycle:** + +1. Browser requests `/tickets/basket_view/` +2. `.htaccess` rewrites to `index.php?module=tickets&action=basket_view` +3. `index.php` bootstraps: autoloader, config, Medoo, RedBeanPHP, PHPMailer, session security +4. `\view\Site::show()` called +5. `\controls\Site::route()` resolves module + action to `\controls\Tickets::basket_view()` +6. Controller method executes: reads session basket, calls factory methods, calls `\Tpl::view()` +7. Rendered HTML returned as string to `\view\Site::show()` +8. `show()` wraps content in `templates/site/layout-logged.php` +9. Final HTML output sent to browser + +**AJAX Request Lifecycle:** + +1. JavaScript POSTs to `ajax.php?module=tickets&action=ticket_add` +2. `ajax.php` bootstraps similarly to `index.php` +3. Controller method executes, modifies `$_SESSION['basket']` +4. `echo json_encode([...]); exit;` returns JSON response + +**Ticket Purchase Flow:** + +1. `main_view()` — Shows available tickets + calendar +2. `ticket_add()` — AJAX: validates date, calculates price, updates `$_SESSION['basket']` +3. `basket_view()` — Shows cart + customer form +4. `basketFormHandler()` — Inserts `orders` + `order_tickets` records, generates QR code PNG +5. `przelewy24()` — Renders payment form for P24 gateway +6. `przelewy24_response()` — P24 webhook: marks order paid, generates invoice, sends email +7. `order_confirm()` — Shows final confirmation + Google Analytics data layer + +**State Management:** +- Shopping cart: `$_SESSION['basket']` (array keyed by ticket product_id) +- Admin auth: `$_SESSION['user']` (boolean for admin, array for staff users) +- Flash messages: `$_SESSION['alert']` (cleared after display) +- Session regenerated on first visit; IP-validated on each request + +## Key Abstractions + +**Module/Controller:** +- Purpose: Group of static methods handling a URL namespace (e.g., `/tickets/`) +- Examples: `autoload/controls/class.Tickets.php`, `autoload/controls/class.Apanel.php` +- Pattern: Static public methods, one per URL action + +**Factory:** +- Purpose: Data access and business logic for a domain +- Examples: `autoload/factory/class.Tickets.php`, `autoload/factory/class.Users.php` +- Pattern: Static methods returning data arrays or modifying DB + +**`\S` Utility Class:** +- Purpose: Centralized session, request, and utility operations +- Key methods: `S::get()` (unified POST/GET), `S::get_session()`, `S::set_session()`, `S::send_email()`, `S::hash()`, `S::alert()` +- Accessed statically throughout the codebase + +**`\Tpl` Template Engine:** +- Purpose: Render PHP templates with variable injection +- Usage: `\Tpl::view('tickets/main-view', ['tickets' => $arr])` +- Variables accessible in template as `$this->tickets` +- Template lookup order: `templates_user/` → `templates/` → root + +## Entry Points + +**`index.php`:** +- Triggers: All web page requests +- Responsibilities: Session bootstrap, DB init, autoloader, calls `\view\Site::show()` + +**`ajax.php`:** +- Triggers: jQuery AJAX calls from frontend +- Responsibilities: Same bootstrap as index.php, routes to controller AJAX actions + +**`api.php`:** +- Triggers: External API calls (mobile or third-party) +- Responsibilities: API-specific bootstrap + +**`cron.php`:** +- Triggers: Server cron job (scheduled tasks) +- Responsibilities: Email reminders, CEIDG import, task chaining + +## Error Handling + +**Strategy:** Minimal — most errors are silent (error_reporting suppresses notices/warnings) + +**Patterns:** +- Factories return empty arrays on failure (no exceptions thrown) +- No try/catch in most controller methods +- cURL errors ignored (return value unchecked in payment flow) +- `error_reporting(E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED)` in entry points + +## Cross-Cutting Concerns + +**Session Security:** +- IP address validated on every request; session destroyed if changed +- Session regenerated once on first visit +- Implementation: `index.php` lines 27-31 + +**Globals:** +- `$settings` — all configuration (from `config.php`) +- `$mdb` — Medoo DB connection +- `$user` — current user (from session) +- Accessed via `global $settings, $mdb, $user` at top of controller methods + +**Template Variables:** +- All templates receive data via `\Tpl::view()` second argument +- No direct template output buffering or inheritance + +--- + +*Architecture analysis: 2026-04-26* +*Update when major patterns change* diff --git a/.paul/codebase/concerns.md b/.paul/codebase/concerns.md new file mode 100644 index 0000000..2f4cd7f --- /dev/null +++ b/.paul/codebase/concerns.md @@ -0,0 +1,131 @@ +# Codebase Concerns + +**Analysis Date:** 2026-04-26 + +## Critical Security Issues + +**SQL Injection — Admin Panel:** +- `autoload/controls/class.Apanel.php` (line ~34) +- `$_GET['id']` concatenated directly into raw SQL: `'SELECT * FROM order_tickets WHERE order_id =' . $clientId` +- Any authenticated admin (or CSRF attacker) can inject arbitrary SQL +- Fix: Use Medoo parameterized query: `$mdb->select('order_tickets', '*', ['order_id' => (int)$clientId])` + +**Hardcoded Credentials in Source Control:** +- `config.php` — DB password, SMTP password (`biletyonline`), admin password (`Admin2022!`), Przelewy24 CRC key, fakturowo.pl API key, test mode secret +- Any repo access exposes production credentials +- Fix: Move to environment variables or a non-committed config file + +**Weak Password Hashing (MD5):** +- `autoload/controls/class.Users.php` and `autoload/factory/class.Users.php` +- User passwords stored and compared as plain MD5 — trivially broken by rainbow tables +- Fix: Replace with `password_hash()` / `password_verify()` (bcrypt) + +**Payment Callback Not Fully Verified:** +- `autoload/controls/class.Tickets.php::przelewy24_response()` +- CRC check exists but cURL verification call return value may not be checked before marking order paid +- Fix: Ensure verification response is confirmed successful before updating `payment_status` + +## High Priority + +**Missing Input Validation on Checkout Form:** +- `autoload/controls/class.Tickets.php::basketFormHandler()` +- `$_POST` fields (email, name, NIP, ZIP) trimmed but not format-validated before DB insert +- Email not validated with `filter_var($email, FILTER_VALIDATE_EMAIL)` +- NIP, ZIP not validated for Polish format +- Fix: Add validation layer before processing order + +**888-Line Monolithic Controller:** +- `autoload/controls/class.Tickets.php` — 888 lines, handles 15+ distinct operations +- Mixes: ticket display, cart management, payment processing, email sending, invoice generation, QR generation, GA data layer +- Makes changes risky (large surface area, no tests) +- Fix: Extract into `TicketsCart`, `TicketsCheckout`, `TicketsPayment`, `TicketsNotification` + +**No CSRF Protection on Admin Actions:** +- `autoload/controls/class.Apanel.php` — all POST actions (login, order edits, deletes) +- No CSRF tokens on any form +- Fix: Add token generation + validation middleware + +**cURL Error Handling Missing:** +- `autoload/controls/class.Tickets.php` — Przelewy24 and fakturowo.pl cURL calls +- `curl_exec()` return value not consistently checked; no timeout set +- Silent failures possible: order could be marked paid without invoice, or invoice not generated +- Fix: Check return value, add `CURLOPT_TIMEOUT`, log failures + +**No Session Fixation Protection on Login:** +- `autoload/controls/class.Users.php::login()` — no `session_regenerate_id(true)` on successful auth +- Session regeneration only on first visit (new session), not on privilege escalation +- Fix: Call `session_regenerate_id(true)` after successful login + +## Medium Priority + +**Hardcoded Domain URLs:** +- `autoload/controls/class.Tickets.php` — `https://bilety.brzezovka.pl` hardcoded in ~5 places +- Breaks if domain changes; makes staging difficult +- Fix: Store base URL in `config.php` as `$settings['base_url']` + +**No Application Logging:** +- No error logging system (PSR-3 or equivalent) +- No audit trail for payments, admin actions, or errors +- Only optional `mail_debug.log` for email +- Fix: Add basic file logger for critical operations (payment events, admin changes) + +**Weak Order Hash Generation:** +- `autoload/controls/class.Tickets.php` — hash based on MD5 of email + city + timestamp +- 1-second collision window for same customer; predictable construction +- Fix: Use `bin2hex(random_bytes(16))` for cryptographically random order hashes + +**Global Variables Without DI:** +- All controllers: `global $settings, $mdb, $user` at top of every method +- Untestable, hidden dependencies, risk of undefined global +- Fix: Long-term — dependency injection; short-term — document as known pattern + +**QR Codes in Web-Accessible Directory:** +- `orders/{hash[0]}/{hash[1]}/{hash}.png` — served directly by Apache +- Hash is predictable (see hash concern above) +- Fix: Move QR storage outside web root; serve via PHP controller with auth check + +**Large Template Files:** +- `templates/tickets/main-view.php` — 401 lines +- `templates/admin-panel/order-data.php` — 373 lines +- `templates/tickets/basket-view.php` — 316 lines +- Mixed PHP logic and HTML; difficult to maintain +- Fix: Extract repeated logic to view helper classes + +**Duplicate Bootstrap Code:** +- `index.php`, `ajax.php`, `api.php`, `cron.php` — each contains near-identical DB init, session, autoloader +- Fix: Extract to `bootstrap.php` included by all entry points + +**No Error Handling on DB Writes:** +- `autoload/controls/class.Tickets.php` — `$mdb->insert()` / `$mdb->update()` calls without checking return value +- Silent failure if DB write fails (order not saved but user gets confirmation) +- Fix: Check return value; wrap in try/catch + +## Low Priority / Documentation + +**Magic Numbers in Pricing Logic:** +- `autoload/controls/class.Tickets.php` — Day ranges `0`, `2`, `7` for dynamic pricing; product ID `999999` for delivery +- Fix: Define as named constants (e.g., `const DELIVERY_PRODUCT_ID = 999999`) + +**Commented Debug Code:** +- `autoload/controls/class.Tickets.php` — `// file_put_contents('sandbox_przelewy24_response.txt', ...)` left in code +- Fix: Remove + +**Short PHP Tags in Templates:** +- `templates/` — ``: `$this -> key` (unusual; vs `$this->key` standard) +- Spaces around operators: `$x === ''`, `$x != null` + +**Method Visibility:** +- `static public` order (reversed from PSR standard): `static public function main_view()` +- Mix of `static public` and `public static` — inconsistent +- Private helpers: `private static function _helper()` + +**Visibility Note:** PHP allows both orderings; the existing codebase uses `static public`. + +**Type Usage:** +- Explicit type casting: `(float)$val`, `(int)$id`, `(string)$hash` +- Null coalescing: `$_SESSION[$var] ?? null` +- No strict types declaration (`declare(strict_types=1)` not used) + +**Linting/Formatting:** +- No ESLint, Prettier, PHP-CS-Fixer, or similar configured +- Style enforced only by convention + +## Import Organization + +- No `use` statements or `import` — classes referenced by full namespace inline: `\controls\Tickets::method()` +- Globals declared at method start: `global $settings, $mdb, $user;` +- Libraries loaded in entry points (`index.php`, `ajax.php`) with `require_once` + +## Template Patterns + +**Rendering:** +```php +return \Tpl::view('tickets/main-view', [ + 'tickets' => $ticketsArr, + 'basket' => $basket, +]); +``` + +**Variable access in templates:** +```php +$tickets = $this->tickets; // via __get magic +echo $this->order_price; +``` + +**Output escaping:** +- `htmlspecialchars()` used inconsistently — not universally applied +- Short echo: `name ?>` (no escaping — common pattern) +- Short if: `` (deprecated short tags — present in older templates) + +## AJAX Response Pattern + +All AJAX actions follow this pattern: +```php +echo json_encode([ + 'basket_html' => \Tpl::view('tickets/basket-view', ['basket' => $basket]), + 'count' => count($basket), +]); +exit; +``` +- `exit` always follows `json_encode` output +- Response keys are snake_case +- Often includes rendered HTML fragments for DOM replacement + +## Error Handling Conventions + +- No exceptions thrown in application code +- Factories return `[]` or `false` on failure (no consistent return type) +- No try/catch blocks except in `autoload/factory/class.Tickets.php` (calendar transactions) +- Errors suppressed via `error_reporting(E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED)` in entry points + +## Documentation Style + +- Minimal comments overall +- Inline comments use `//*` prefix (non-standard): `//* Zapisywanie do DB bilety` +- Language: mix of Polish and English in comments and variable names +- No PHPDoc blocks on custom application methods +- Vendored libraries (e.g., Excel.php) have `/** @author */` blocks + +## Common Patterns + +**Global Access (every controller method):** +```php +static public function some_action() { + global $settings, $mdb; + global $user; + // ... +} +``` + +**Session via S class:** +```php +\S::set_session('basket', $basket); +$basket = \S::get_session('basket') ?? []; +\S::del_session('alert'); +\S::alert('Błąd - brak zamówienia'); +``` + +**DB queries via Medoo:** +```php +$order = $mdb->get('orders', '*', ['hash' => $hash]); +$tickets = $mdb->select('order_tickets', '*', ['order_id' => $id]); +$mdb->insert('orders', ['name' => $name, 'email' => $email]); +$mdb->update('orders', ['payment_status' => 1], ['id' => $id]); +``` + +**Config access:** +```php +$settings['ticket'][$product_id]['name'] +$settings['p24_merchant_id'] +$settings['admin-password'] +``` + +--- + +*Conventions analysis: 2026-04-26* +*Update when major style changes are introduced* diff --git a/.paul/codebase/db_schema.md b/.paul/codebase/db_schema.md new file mode 100644 index 0000000..4ff479c --- /dev/null +++ b/.paul/codebase/db_schema.md @@ -0,0 +1,144 @@ +# Database Schema + +**Analysis Date:** 2026-04-26 + +## Overview + +- Database: MySQL (`srv81099_brzez_ticket`) +- ORM: Medoo query builder (`$mdb` global) for most queries; RedBeanPHP for cron tasks +- No migration files — schema managed manually +- No schema files checked into repo — tables inferred from code + +--- + +## Tables + +### `orders` + +Primary order records — one row per customer purchase. + +| Column | Type (inferred) | Notes | +|--------|----------------|-------| +| `id` | INT AUTO_INCREMENT PK | | +| `name` | VARCHAR | Customer first name | +| `surname` | VARCHAR | Customer last name | +| `email` | VARCHAR | Customer email | +| `zip_code` | VARCHAR | | +| `city` | VARCHAR | | +| `street` | VARCHAR | | +| `order_price` | DECIMAL | Total order value | +| `date_added` | DATETIME | Order creation timestamp | +| `hash` | VARCHAR UNIQUE | Order identifier (used in URLs, QR codes) | +| `payment_hash` | VARCHAR | Przelewy24 session/transaction ID | +| `payment_status` | TINYINT | 0 = unpaid, 1 = paid | +| `payment_date` | DATETIME | Timestamp of payment confirmation | +| `invoice_status` | TINYINT | 0 = not generated, 1 = generated | +| `invoice_url` | VARCHAR | URL to fakturowo.pl invoice/receipt | +| `vat` | TINYINT | 0 = paragon, 1 = faktura VAT | +| `company_name` | VARCHAR | For VAT invoice | +| `nip` | VARCHAR | Polish tax ID for VAT invoice | +| `gift_address` | TEXT | Gift ticket delivery address | +| `used_ticket` | TINYINT | Whether QR has been scanned/used | +| `informed_user` | TINYINT | Whether confirmation email was sent | + +**Key queries:** `autoload/controls/class.Tickets.php`, `autoload/controls/class.Apanel.php` + +--- + +### `order_tickets` + +Line items — one row per ticket type per order. + +| Column | Type (inferred) | Notes | +|--------|----------------|-------| +| `id` | INT AUTO_INCREMENT PK | | +| `order_id` | INT FK → orders.id | | +| `product_id` | INT | Ticket product ID from `$settings['ticket']` | +| `name` | VARCHAR | Ticket name (copied from config at time of order) | +| `quantity` | INT | Number of tickets | +| `price` | DECIMAL | Unit price at time of purchase | +| `date_visit` | DATE | Intended visit date | +| `date_added` | DATETIME | Row creation timestamp | + +**Key queries:** `autoload/controls/class.Apanel.php`, `autoload/factory/class.Tickets.php` + +--- + +### `users` + +Staff/admin accounts for non-ticket modules. + +| Column | Type (inferred) | Notes | +|--------|----------------|-------| +| `id` | INT AUTO_INCREMENT PK | | +| `email` | VARCHAR UNIQUE | Login identifier | +| `password` | VARCHAR(32) | MD5 hash (insecure — see concerns.md) | +| `name` | VARCHAR | First name | +| `surname` | VARCHAR | Last name | +| `default_project` | INT | Default project preference | +| `pushover_api` | VARCHAR | Pushover API key for push notifications | +| `pushover_user` | VARCHAR | Pushover user key | + +**Key queries:** `autoload/factory/class.Users.php` + +--- + +### `ticket_calendar_availability` + +Controls which dates tickets can be purchased for. + +| Column | Type (inferred) | Notes | +|--------|----------------|-------| +| `ticket_group` | VARCHAR PK (composite) | e.g., `park-rozrywki`, `park-wodny` | +| `available_date` | DATE PK (composite) | Date that is enabled for purchase | +| `updated_at` | DATETIME | Last modification timestamp | + +**Notes:** +- Created dynamically via `\factory\Tickets::ensureCalendarTable()` if missing +- Admin manages via `/apanel/settings/` → calendar editor +- Ticket groups: `park-rozrywki`, `park-wodny`, `all-open`, `bilety-rodzinne` +- **Key file:** `autoload/factory/class.Tickets.php` + +--- + +### `site_settings` + +Key-value store for runtime configuration (complements `config.php`). + +| Column | Type (inferred) | Notes | +|--------|----------------|-------| +| `setting_key` | VARCHAR PK | Setting name | +| `setting_value` | TEXT | Setting value | + +**Notes:** +- Created dynamically via `\factory\Apanel::getSetting()` if missing +- `REPLACE INTO` for upserts +- Used for admin-editable settings that don't require code deploy +- **Key file:** `autoload/factory/class.Apanel.php` + +--- + +## Relationships + +``` +orders (1) ──────< (N) order_tickets + orders.id = order_tickets.order_id + +users — standalone (no FK to orders) +ticket_calendar_availability — standalone lookup table +site_settings — standalone KV store +``` + +## Migration Approach + +- No migration tool (no Flyway, Phinx, Liquibase) +- Schema changes applied manually via MySQL client +- New tables created programmatically in application code when needed: + - `ticket_calendar_availability` — `\factory\Tickets::ensureCalendarTable()` + - `site_settings` — `\factory\Apanel::getSetting()` + +--- + +*Schema analysis: 2026-04-26* +*Inferred from code — no authoritative schema file in repo* +*Verify against production database for exact column types* diff --git a/.paul/codebase/integrations.md b/.paul/codebase/integrations.md new file mode 100644 index 0000000..876f124 --- /dev/null +++ b/.paul/codebase/integrations.md @@ -0,0 +1,112 @@ +# External Integrations + +**Analysis Date:** 2026-04-26 + +## APIs & External Services + +**Payment Processing:** +- Przelewy24 — Polish payment gateway for ticket purchases + - SDK/Client: Custom cURL integration in `autoload/controls/class.Tickets.php` + - Auth: Merchant ID `227658` + CRC key (MD5-signed) in `config.php` + - Endpoints: `https://secure.przelewy24.pl/trnVerify` (production), sandbox configurable + - Flow: Pre-payment form → P24 hosted page → `przelewy24_response()` webhook callback + - Sandbox mode: toggle in `config.php` + +**Invoice/Receipt Generation:** +- fakturowo.pl — Polish invoicing API (paragon or faktura VAT) + - SDK/Client: Custom cURL POST in `autoload/controls/class.Tickets.php` + - Auth: API ID in `config.php` (`$settings['fakturowo_api_id']`) + - Endpoint: `https://konto.fakturowo.pl/api` + - Triggered after successful Przelewy24 payment + - Returns invoice URL stored in `orders.invoice_url` + +## Data Storage + +**Databases:** +- MySQL — Primary data store + - Connection: Credentials in `config.php`, instantiated in `index.php:44` + - Client: Medoo query builder (`libraries/medoo/medoo.php`) + - Secondary ORM: RedBeanPHP (`libraries/rb.php`) used in cron tasks + +**File Storage:** +- Local filesystem — QR code PNG files stored in `orders/{hash[0]}/{hash[1]}/{hash}.png` + - Web-accessible directory, predictable path structure + - Created with `mkdir($dir, 0755, true)` and `\QRcode::png()` + +## Authentication & Identity + +**Admin Auth:** +- Single shared password stored in `config.php` (`$settings['admin-password']`) +- Session-based: password checked once, `$_SESSION['user'] = true` set +- No individual admin accounts for ticket operations (single login) + +**Staff/User Auth:** +- Separate user table for named staff (`users` table — id, email, MD5 password) +- Used for non-ticket modules (projects, finances, etc.) +- Hard-coded ACL in `autoload/controls/class.Users.php` (`permissions()` method) +- Login via `autoload/factory/class.Users.php::login()` with MD5 comparison + +## Email + +**PHPMailer + SMTP:** +- Library: `libraries/phpmailer/class.phpmailer.php` +- SMTP host: `h53.seohost.pl` port 25 +- From address: `bilety@brzezovka.pl` +- Credentials: in `config.php` +- Fallback: native PHP `mail()` if SMTP unavailable +- Debug logging: optional to `mail_debug.log` +- Used for: order confirmations (pre-payment), payment confirmations (with QR PNG attachment) +- Implementation: `autoload/class.S.php::send_email()` + +## Monitoring & Observability + +**Error Tracking:** Not detected — no Sentry, Rollbar, or similar + +**Analytics:** +- Google Analytics ecommerce data layer — purchase tracking + - `buildPurchaseDataLayer()` in `autoload/controls/class.Tickets.php` + - Generates `$purchase_data_layer` passed to `templates/tickets/order-confirm.php` + - Fires on order confirmation page + +**Logs:** +- Optional mail debug log: `mail_debug.log` (file-based, in project root) +- No centralized application logging + +## CI/CD & Deployment + +**Hosting:** Shared hosting (seohost.pl, Apache) + +**Deployment:** +- FTP sync via VS Code ftp-kr extension +- Config: `.vscode/ftp-kr.json`, cache: `.vscode/ftp-kr.sync.cache.json` +- Manual deploy — upload changed files via FTP + +**CI Pipeline:** None detected + +## Environment Configuration + +**Development:** +- All config in `config.php` (single file, committed to git) +- No separate dev/staging/prod config files +- Test mode: `$settings['test_price_mode_secret']` for pricing tests + +**Production:** +- Przelewy24 sandbox toggle: `$settings['p24_sandbox'] = false` +- All secrets in `config.php` — not managed via environment variables + +## Webhooks & Callbacks + +**Incoming:** +- Przelewy24 — `/tickets/przelewy24_response/` + - Handled in `autoload/controls/class.Tickets.php::przelewy24_response()` + - Verification: MD5 CRC check against P24 parameters + - On success: marks order paid, generates invoice, sends confirmation email + +**Outgoing:** +- fakturowo.pl — POST request on payment confirmation +- Przelewy24 verification — GET/POST to `trnVerify` endpoint to confirm transaction + +--- + +*Integration audit: 2026-04-26* +*Update when adding/removing external services* diff --git a/.paul/codebase/stack.md b/.paul/codebase/stack.md new file mode 100644 index 0000000..edab072 --- /dev/null +++ b/.paul/codebase/stack.md @@ -0,0 +1,83 @@ +# Technology Stack + +**Analysis Date:** 2026-04-26 + +## Languages + +**Primary:** +- PHP 7.4+ - All application code (`index.php`, `autoload/`) +- HTML/PHP - Template files (`templates/`) + +**Secondary:** +- SCSS - Stylesheet authoring (`layout/style-scss/style.scss`) +- JavaScript (ES5+) - Client-side logic embedded in templates and CDN-loaded libraries + +## Runtime + +**Environment:** +- PHP 7.4+ on Apache (shared hosting) +- MySQL database (`srv81099_brzez_ticket`) +- Timezone: Europe/Warsaw + +**Package Manager:** +- None — no Composer, no npm +- All dependencies vendored in `libraries/` + +## Frameworks + +**Core:** +- None — custom MVC built from scratch + +**Frontend:** +- Bootstrap 5.2 — Responsive layout and components (CDN) +- jQuery 3.6 — DOM manipulation and AJAX (CDN) +- Font Awesome 6.1 — Icons (CDN) +- Flatpickr — Date picker for ticket date selection (CDN) +- DataTables — Admin order list pagination/sorting (CDN) +- html5-qrcode — Browser QR code scanner for on-site validation (CDN) + +**Build/Dev:** +- Live Sass Compiler (VS Code extension) — SCSS → CSS compilation +- FTP sync via ftp-kr VS Code extension — Deployment + +## Key Dependencies + +**Critical:** +- Medoo — MySQL query builder (`libraries/medoo/medoo.php` — referenced in code, may be missing from repo) +- RedBeanPHP — ORM used in cron jobs (`libraries/rb.php`, 570KB single file) +- PHPMailer — SMTP email delivery (`libraries/phpmailer/class.phpmailer.php`, `class.smtp.php`) +- phpqrcode — QR code generation for tickets (`libraries/phpqrcode/qrlib.php`) + +**Infrastructure:** +- PDO — Database access layer (used internally by Medoo) +- cURL — HTTP calls to Przelewy24 and fakturowo.pl APIs + +## Configuration + +**Environment:** +- Single file: `config.php` — contains ALL configuration +- Database credentials, SMTP settings, ticket pricing, payment gateway keys, admin password +- No `.env` files — credentials hardcoded (security concern documented in concerns.md) + +**Build:** +- SCSS config in first-line comment of `layout/style-scss/style.scss` +- No build config files (vite, webpack, etc.) + +## Platform Requirements + +**Development:** +- Windows (VS Code with Live Sass Compiler + ftp-kr extensions) +- PHP 7.4+ locally or via FTP-direct editing +- MySQL access for schema changes + +**Production:** +- Apache + mod_rewrite (shared hosting via seohost.pl) +- PHP 7.4+ +- MySQL +- Outbound SMTP (port 25 to h53.seohost.pl) +- Outbound HTTPS for Przelewy24 and fakturowo.pl + +--- + +*Stack analysis: 2026-04-26* +*Update after major dependency changes* diff --git a/.paul/codebase/structure.md b/.paul/codebase/structure.md new file mode 100644 index 0000000..083e36f --- /dev/null +++ b/.paul/codebase/structure.md @@ -0,0 +1,159 @@ +# Codebase Structure + +**Analysis Date:** 2026-04-26 + +## Directory Layout + +``` +bilety.brzezovka.pl/ +├── autoload/ # PHP autoloaded classes (MVC layers + core utilities) +│ ├── controls/ # Controllers (request handlers per module) +│ ├── factory/ # Business logic and DB query services +│ ├── view/ # View composers (layout wrapping) +│ ├── class.S.php # Session, utilities, email +│ ├── class.Tpl.php # Template engine +│ ├── class.Html.php # Form component builder +│ ├── class.DbModel.php # Active-record base class +│ ├── class.Excel.php # Order Excel export +│ └── class.Cron.php # Cron stub +├── templates/ # PHP HTML templates +│ ├── tickets/ # Customer-facing ticket shop +│ ├── admin-panel/ # Admin order management +│ ├── site/ # Layout wrappers +│ ├── html/ # Reusable form components +│ ├── components/ # UI fragments (spinner, etc.) +│ └── cron/ # Cron output +├── libraries/ # Vendored PHP dependencies +│ ├── rb.php # RedBeanPHP ORM +│ ├── phpmailer/ # PHPMailer SMTP +│ └── phpqrcode/ # QR code generator +├── layout/ # Frontend assets +│ ├── style-scss/ # SCSS source files +│ ├── style-css/ # Compiled CSS +│ └── images/ # Logos, icons +├── orders/ # Generated QR code PNGs (web-accessible) +├── .paul/ # PAUL project planning files +├── .vscode/ # VS Code settings + FTP sync config +├── index.php # Main web entry point +├── ajax.php # AJAX endpoint +├── api.php # API endpoint +├── cron.php # Scheduled tasks entry point +└── config.php # All configuration (DB, SMTP, tickets, keys) +``` + +## Directory Purposes + +**`autoload/`:** +- Purpose: All PHP application classes, autoloaded by `spl_autoload_register` +- Naming convention: `class.{ClassName}.php` +- Namespace→path mapping: `\controls\Tickets` → `autoload/controls/class.Tickets.php` + +**`autoload/controls/`:** +- Purpose: Controller classes — one per module +- Key files: `class.Tickets.php` (888 lines, ticket shop), `class.Apanel.php`, `class.Users.php`, `class.Scanner.php`, `class.Site.php` (router) + +**`autoload/factory/`:** +- Purpose: Business logic and DB query classes +- Key files: `class.Tickets.php` (calendar, basket), `class.Apanel.php` (settings), `class.Users.php` (auth) + +**`autoload/view/`:** +- Purpose: Layout composers — wrap controller output in site templates +- Key files: `class.Site.php` (main layout dispatcher) + +**`templates/`:** +- Purpose: PHP HTML templates rendered by `\Tpl::view()` +- Subdirs match module names; files use kebab-case: `main-view.php`, `order-data.php` + +**`libraries/`:** +- Purpose: Vendored third-party PHP libraries (no Composer) +- Do not modify; update by replacing files + +**`layout/`:** +- Purpose: Frontend assets +- SCSS compiled via VS Code Live Sass Compiler — edit `style-scss/`, output goes to `style-css/` + +**`orders/`:** +- Purpose: Runtime-generated QR code PNG storage +- Structure: `orders/{hash[0]}/{hash[1]}/{hash}.png` +- Web-accessible (referenced in confirmation emails and scanner) + +## Key File Locations + +**Entry Points:** +- `index.php` — All web requests +- `ajax.php` — jQuery AJAX calls +- `api.php` — External API access +- `cron.php` — Scheduled background tasks + +**Configuration:** +- `config.php` — Everything: DB, SMTP, ticket types/prices, payment keys, admin password + +**Core Logic:** +- `autoload/controls/class.Tickets.php` — Full ticket purchase flow + P24 payment +- `autoload/controls/class.Apanel.php` — Admin order management +- `autoload/factory/class.Tickets.php` — Calendar availability, basket logic +- `autoload/class.S.php` — Session, email, utility methods used everywhere + +**Templates:** +- `templates/tickets/main-view.php` — Ticket selection + calendar UI +- `templates/tickets/basket-form.php` — Customer details checkout form +- `templates/tickets/przelewy24.php` — Payment gateway form +- `templates/tickets/order-confirm.php` — Post-payment confirmation +- `templates/admin-panel/main-view.php` — Orders list table +- `templates/admin-panel/order-data.php` — Order detail + edit modal +- `templates/site/layout-logged.php` — Main site wrapper + +**Styles:** +- `layout/style-scss/style.scss` — SCSS source (compile with Live Sass Compiler) +- `layout/style-css/style.css` — Compiled output (don't edit directly) + +## Naming Conventions + +**Files:** +- `class.{ClassName}.php` — All PHP class files in `autoload/` +- `kebab-case.php` — Template files in `templates/` +- `style.scss` / `style.css` — Single compiled stylesheet + +**Directories:** +- Singular for layers: `controls/`, `factory/`, `view/` +- Module-named for templates: `tickets/`, `admin-panel/`, `site/` +- Lowercase throughout + +## Where to Add New Code + +**New controller action (e.g., `/tickets/new_action/`):** +- Method: `autoload/controls/class.Tickets.php` — add `static public function new_action()` +- Template: `templates/tickets/new-action.php` +- Business logic: `autoload/factory/class.Tickets.php` + +**New admin panel feature:** +- Controller: `autoload/controls/class.Apanel.php` +- Template: `templates/admin-panel/` +- Logic: `autoload/factory/class.Apanel.php` + +**New module (e.g., `/reports/`):** +- Controller: `autoload/controls/class.Reports.php` +- Factory: `autoload/factory/class.Reports.php` +- View: `autoload/view/class.Reports.php` (if custom layout needed) +- Templates: `templates/reports/` + +**New utility method:** +- Add to `autoload/class.S.php` as static method + +**SCSS changes:** +- Edit `layout/style-scss/style.scss` — save triggers Live Sass Compiler + +## Special Directories + +**`orders/`:** +- Generated at runtime; not in git (should be gitignored) +- Contains QR code PNGs served directly by web server + +**`.paul/`:** +- PAUL project planning files +- Committed to git as project documentation + +--- + +*Structure analysis: 2026-04-26* +*Update when directory structure changes* diff --git a/.paul/codebase/testing.md b/.paul/codebase/testing.md new file mode 100644 index 0000000..024da3b --- /dev/null +++ b/.paul/codebase/testing.md @@ -0,0 +1,55 @@ +# Testing + +**Analysis Date:** 2026-04-26 + +## Framework + +**No automated test framework detected.** + +- No PHPUnit, PHPSpec, Codeception, or Behat +- No Composer (so no dev dependencies at all) +- No `package.json` or Node.js test tooling + +## Structure + +**No test directories or test files found:** +- No `tests/`, `test/`, `__tests__/` directories +- No files matching `*.test.php`, `*Test.php`, `*Spec.php` +- No `phpunit.xml`, `phpunit.xml.dist`, or similar config + +## Coverage + +**Zero automated test coverage.** + +All validation is runtime-only: +- IP-based session security in `index.php` and `ajax.php` (security guard, not tests) +- Manual payment flow testing via Przelewy24 sandbox mode (`$settings['p24_sandbox']`) +- Test price mode: `test_price_mode_on()` sets all ticket prices to 1 PLN via secret key (`autoload/controls/class.Tickets.php`) + +## Tools + +**None configured:** +- No Xdebug or pcov for coverage +- No CI/CD (no `.github/workflows/`, no `.travis.yml`, no `bitbucket-pipelines.yml`) +- No static analysis (PHPStan, Psalm) +- No linting tools + +## Testing Approach in Practice + +This is a legacy production system with manual testing only: +1. Developer makes changes and uploads via FTP +2. Tests manually in browser against live or sandbox payment mode +3. Przelewy24 sandbox available for payment flow testing +4. No regression protection + +## Recommendations + +If adding tests in future: +- PHPUnit for factory/service layer unit tests (no framework dependency to work around) +- Focus first on `autoload/factory/class.Tickets.php` — calendar logic, basket calculations +- Integration tests for payment webhook handler (`przelewy24_response()`) +- Bootstrap challenge: globals (`$mdb`, `$settings`) need to be injectable for testability + +--- + +*Testing analysis: 2026-04-26*