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 <noreply@anthropic.com>
This commit is contained in:
2026-04-26 22:15:02 +02:00
parent b6697352e9
commit 5bbec72b59
9 changed files with 989 additions and 0 deletions

View File

@@ -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.

View File

@@ -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*

131
.paul/codebase/concerns.md Normal file
View File

@@ -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/``<? if ...` (deprecated, requires `short_open_tag = On`)
- Fix: Replace with `<?php` in templates over time
**MD5 Session CSRF Token:**
- `index.php``$_SESSION['check'] = md5(...)` used as CSRF-adjacent token
- MD5 for security tokens is considered weak
- Fix: Use `bin2hex(random_bytes(32))` for session tokens
**No README / Setup Guide:**
- No documentation on how to set up dev environment or database
- No `.env.example` or credential template
- Fix: Add `README.md` with setup steps and required config values
---
*Concerns analysis: 2026-04-26*
*Priority order: Critical → High → Medium → Low*
*Address Critical items before any new feature development*

View File

@@ -0,0 +1,144 @@
# Coding Conventions
**Analysis Date:** 2026-04-26
## Naming Patterns
**Files:**
- `class.{ClassName}.php` — All PHP class files (e.g., `class.Tickets.php`, `class.S.php`)
- `kebab-case.php` — Template files (e.g., `main-view.php`, `order-data-table.php`)
- No test file pattern — no test files exist
**Methods/Functions:**
- `snake_case` — Older/dominant style: `main_view()`, `get_session()`, `basket_view()`
- `camelCase` — Newer additions: `buildPurchaseDataLayer()`, `sendPaidOrderSummaryEmail()`, `isValidTestPriceSecret()`
- Action-oriented names mapping to URL: `ticket_add()`, `order_confirm()`, `login_check()`
**Variables:**
- `$snake_case` — All variables
- `$mdb`, `$user`, `$settings` — Global shorthand names
- `$orderArr`, `$ticketsArr` — Array suffix pattern
**Classes:**
- PascalCase: `Tickets`, `Apanel`, `Users`, `Scanner`
- Namespaced: `\controls\Tickets`, `\factory\Tickets`, `\view\Site`
## Code Style
**Formatting:**
- 2-space indentation (spaces, not tabs)
- CRLF line endings (Windows)
- Spaces inside parentheses: `function method( $param )`, `if ( $x === '' )`
- Spaces around `->`: `$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: `<?= $this->name ?>` (no escaping — common pattern)
- Short if: `<? if ($condition): ?>` (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*

144
.paul/codebase/db_schema.md Normal file
View File

@@ -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*

View File

@@ -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*

83
.paul/codebase/stack.md Normal file
View File

@@ -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*

159
.paul/codebase/structure.md Normal file
View File

@@ -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*

55
.paul/codebase/testing.md Normal file
View File

@@ -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*