# Technology Stack **Analysis Date:** 2026-03-12 --- ## Languages **Primary:** - PHP 8.4 — all backend logic, routing, controllers, repositories, CLI scripts - SQL — raw migration files, all database queries via PDO prepared statements **Secondary:** - SCSS — compiled to CSS for frontend styling (`resources/scss/`) - JavaScript — custom jQuery-based UI alert/confirm module (`resources/modules/jquery-alerts/`) --- ## Runtime & Server **PHP:** - Version: `^8.4` (strict, declared via `declare(strict_types=1)` in every file) - Extensions required: `pdo_mysql`, `openssl`, `curl`, `hash`, `mbstring`, `session` - Dev server: `php -S localhost:8000 -t public public/index.php` (via `composer serve`) - Production: any standard PHP-FPM + Apache/Nginx setup targeting `public/` as document root **Entry point:** - `public/index.php` — bootstraps the application - `index.php` (root) — likely a redirect or secondary entry **CLI scripts (in `bin/`):** - `bin/migrate.php` — runs database migrations (`composer migrate`) - `bin/cron.php` — executes scheduled cron jobs (`composer cron`) --- ## Framework **No third-party framework.** The application uses a fully custom, hand-rolled framework located in `src/Core/`: | Component | Class | File | |---|---|---| | Application container | `App\Core\Application` | `src/Core/Application.php` | | HTTP Router | `App\Core\Routing\Router` | `src/Core/Routing/Router.php` | | HTTP Request | `App\Core\Http\Request` | `src/Core/Http/Request.php` | | HTTP Response | `App\Core\Http\Response` | `src/Core/Http/Response.php` | | Template engine | `App\Core\View\Template` | `src/Core/View/Template.php` | | Database factory | `App\Core\Database\ConnectionFactory` | `src/Core/Database/ConnectionFactory.php` | | Migrator | `App\Core\Database\Migrator` | `src/Core/Database/Migrator.php` | | Session | `App\Core\Support\Session` | `src/Core/Support/Session.php` | | Logger | `App\Core\Support\Logger` | `src/Core/Support/Logger.php` | | CSRF protection | `App\Core\Security\Csrf` | `src/Core/Security/Csrf.php` | | Translator (i18n) | `App\Core\I18n\Translator` | `src/Core/I18n/Translator.php` | | Env helper | `App\Core\Support\Env` | `src/Core/Support/Env.php` | **Routing style:** `GET`/`POST` registration with path-param support (`{id}`) and per-route middleware pipeline. Routes defined in `routes/web.php`. **Template engine:** PHP native `require` with `ob_start()`/`ob_get_clean()`. Views live in `resources/views/`. Layout support via a second render pass. XSS helper `$e()` exposed to every view via `htmlspecialchars`. Translation helper `$t()` also injected. **i18n:** Single-locale PHP array file. Currently Polish only: `resources/lang/pl.php`. --- ## Database **Engine:** MySQL (only supported driver — enforced with `RuntimeException` in `ConnectionFactory`) **Default port:** 3306 **Charset:** `utf8mb4` **Client:** PHP PDO with `ERRMODE_EXCEPTION`, `FETCH_ASSOC`, `EMULATE_PREPARES = false` **ORM:** None — raw PDO prepared statements everywhere **Migrations:** - Plain SQL files in `database/migrations/`, named `YYYYMMDD_NNNNNN_description.sql` - Run via `bin/migrate.php` / `composer migrate` - 46 migrations present (as of analysis date), dating from 2026-02-21 **Config file:** `config/database.php` --- ## Package Manager & Build Tools **PHP dependencies (Composer):** - `composer.json` — PSR-4 autoload: `App\` → `src/`, `Tests\` → `tests/` - No runtime Composer packages — zero third-party PHP libraries - Dev only: `phpunit/phpunit ^11.5` - Lockfile: `composer.lock` (present) **Node / Frontend (npm):** - `package.json` — `devDependencies` only - `sass ^1.97.3` — SCSS compiler (Dart Sass CLI) - Lockfile: `package-lock.json` (present) **Build commands (npm scripts):** ```bash npm run build:css # Compile app.scss + login.scss → public/assets/css/ npm run build:modules # Compile jquery-alerts.scss + copy jquery-alerts.js → public/assets/ npm run build:assets # Full build (build:css + build:modules) npm run watch:css # Watch mode for SCSS development ``` **Output paths:** - `public/assets/css/app.css` — main application styles - `public/assets/css/login.css` — login page styles - `public/assets/css/modules/jquery-alerts.css` — alert/confirm modal styles - `public/assets/js/modules/jquery-alerts.js` — alert/confirm module JS **SCSS sources:** - `resources/scss/app.scss` — main stylesheet - `resources/scss/login.scss` — login page stylesheet - `resources/scss/shared/_ui-components.scss` — shared component partials - `resources/modules/jquery-alerts/jquery-alerts.scss` — module source --- ## Testing **Framework:** PHPUnit `^11.5` **Config:** `phpunit.xml` **Run command:** `composer test` → `vendor/bin/phpunit -c phpunit.xml --testdox` **Test root:** `tests/` (`Tests\` namespace) **Bootstrap:** `tests/bootstrap.php` **Cache dir:** `storage/cache/phpunit` **Coverage source:** `src/` --- ## Security **CSRF:** Session-based token via `App\Core\Security\Csrf` (`src/Core/Security/Csrf.php`) — `hash_equals()` comparison **Credential encryption (integration API keys):** AES-256-CBC + HMAC-SHA256 via `App\Modules\Settings\IntegrationSecretCipher` (`src/Modules/Settings/IntegrationSecretCipher.php`) - Encryption key and HMAC key are both derived from `INTEGRATIONS_SECRET` env var using `hash('sha256', 'enc|...')` / `hash('sha256', 'auth|...')` - Encrypted values stored in DB as `v1:` format **XSS:** `htmlspecialchars()` via `$e()` helper injected into every view by `Template::renderFile()` **SQL injection:** PDO prepared statements enforced throughout all repositories --- ## External Integrations ### Allegro (Polish marketplace) - **OAuth 2.0 client:** `App\Modules\Settings\AllegroOAuthClient` (`src/Modules/Settings/AllegroOAuthClient.php`) - Authorization code + refresh token flow - Environments: `production` (`https://allegro.pl`) and `sandbox` (`https://allegro.pl.allegrosandbox.pl`) - Scopes: `allegro:api:orders:read`, `allegro:api:sale:offers:read`, `allegro:api:shipments:read`, `allegro:api:shipments:write` - **REST API client:** `App\Modules\Settings\AllegroApiClient` (`src/Modules/Settings/AllegroApiClient.php`) - Endpoints used: checkout forms (order list/detail), shipments, delivery services, product offers, shipment labels (binary PDF) - Accept header: `application/vnd.allegro.public.v1+json` - HTTP transport: native PHP `curl` - **Sync services:** - `AllegroOrdersSyncService` — order import from Allegro - `AllegroStatusSyncService` — order status synchronization - `AllegroOrderImportService` — individual order import - **Cron handlers:** `AllegroOrdersImportHandler`, `AllegroStatusSyncHandler`, `AllegroTokenRefreshHandler` - **Credentials stored:** in DB `integrations` table, API key encrypted with `IntegrationSecretCipher` ### shopPRO (Polish e-commerce platform) - **REST API client:** `App\Modules\Settings\ShopproApiClient` (`src/Modules/Settings/ShopproApiClient.php`) - Endpoint pattern: `{baseUrl}/api.php?endpoint=orders&action=list` - Auth: `X-Api-Key` HTTP header - Supports: order list (paginated), order by ID, product by ID - **Sync services:** - `ShopproOrdersSyncService` — order import from shopPRO - `ShopproStatusSyncService` — order status sync - `ShopproPaymentStatusSyncService` — payment status sync - **Cron handlers:** `ShopproOrdersImportHandler`, `ShopproStatusSyncHandler`, `ShopproPaymentStatusSyncHandler` ### Apaczka (Polish shipping aggregator) - **REST API client:** `App\Modules\Settings\ApaczkaApiClient` (`src/Modules/Settings/ApaczkaApiClient.php`) - Base URL: `https://www.apaczka.pl/api/v2` - Auth: HMAC-SHA256 signature (`app_id:route:requestJson:expires` + `appSecret`) - Signature variant fallback: tries multiple formats to handle Apaczka API quirks - Endpoints: `service_structure`, `order_send`, `order/{id}`, `waybill/{id}`, `points/{type}` - **Shipment provider:** `App\Modules\Shipments\ApaczkaShipmentService` (`src/Modules/Shipments/ApaczkaShipmentService.php`) - **Settings repo:** `ApaczkaIntegrationRepository`, `ApaczkaIntegrationController` ### InPost ShipX - **API base URL:** `https://api-shipx-pl.easypack24.net` - **Auth:** Bearer token (stored encrypted in DB) - **Settings repo:** `App\Modules\Settings\InpostIntegrationRepository` (`src/Modules/Settings/InpostIntegrationRepository.php`) - **Shipment provider:** `App\Modules\Shipments\AllegroShipmentService` also covers InPost services through Allegro's shipment management API - **Config stored:** `inpost_integration_settings` DB table - **Features:** sandbox/production toggle, dispatch method, locker size, dimensions, label format (PDF/ZPL/EPL), weekend delivery, multi-parcel ### GS1 (product barcode data) - Referenced in `bin/test_gs1_api.php` and `bin/fix_gs1_brand.php` - Settings stored in `app_settings` table (GS1 API key, GLN, etc.) --- ## Shipment Provider Abstraction Interface: `App\Modules\Shipments\ShipmentProviderInterface` (`src/Modules/Shipments/ShipmentProviderInterface.php`) Methods: - `code(): string` - `getDeliveryServices(): array` - `createShipment(int $orderId, array $formData): array` - `checkCreationStatus(int $packageId): array` - `downloadLabel(int $packageId, string $storagePath): array` Registry: `App\Modules\Shipments\ShipmentProviderRegistry` (`src/Modules/Shipments/ShipmentProviderRegistry.php`) Implementations: - `AllegroShipmentService` — creates shipments via Allegro shipment management API - `ApaczkaShipmentService` — creates shipments via Apaczka API --- ## Cron System **Runner:** `App\Modules\Cron\CronRunner` (`src/Modules/Cron/CronRunner.php`) **Repository:** `App\Modules\Cron\CronRepository` (`src/Modules/Cron/CronRepository.php`) **CLI entry:** `bin/cron.php` **Web trigger (optional):** Cron can run on each web request (throttled, DB-locked) when `CRON_RUN_ON_WEB=true` **Registered jobs:** | Job key | Handler | Purpose | |---|---|---| | `allegro_token_refresh` | `AllegroTokenRefreshHandler` | Refresh Allegro OAuth tokens | | `allegro_orders_import` | `AllegroOrdersImportHandler` | Import orders from Allegro | | `allegro_status_sync` | `AllegroStatusSyncHandler` | Sync order statuses to Allegro | | `shoppro_orders_import` | `ShopproOrdersImportHandler` | Import orders from shopPRO | | `shoppro_order_status_sync` | `ShopproStatusSyncHandler` | Sync order statuses to shopPRO | | `shoppro_payment_status_sync` | `ShopproPaymentStatusSyncHandler` | Sync payment statuses from shopPRO | --- ## Environment Configuration **File:** `.env` (not committed) — see `.env.example` for all required vars | Variable | Purpose | Default | |---|---|---| | `APP_NAME` | Application display name | `orderPRO` | | `APP_ENV` | Environment (`local`/`production`) | `local` | | `APP_DEBUG` | Debug mode (display errors) | `true` | | `APP_URL` | Application base URL | `http://localhost:8000` | | `SESSION_NAME` | PHP session cookie name | `orderpro_session` | | `INTEGRATIONS_SECRET` | Master key for encrypting API credentials | *(required, no default)* | | `CRON_RUN_ON_WEB` | Enable web-triggered cron | `false` | | `CRON_WEB_LIMIT` | Max cron jobs per web request | `5` | | `DB_CONNECTION` | DB driver (only `mysql` supported) | `mysql` | | `DB_HOST` | MySQL host (runtime) | `127.0.0.1` | | `DB_HOST_REMOTE` | MySQL host for agent/migration use only — **not for runtime** | *(empty)* | | `DB_PORT` | MySQL port | `3306` | | `DB_DATABASE` | Database name | `orderpro` | | `DB_USERNAME` | Database user | `root` | | `DB_PASSWORD` | Database password | *(empty)* | | `DB_CHARSET` | Connection charset | `utf8mb4` | **Config files:** `config/app.php`, `config/database.php` --- ## Storage Directories (auto-created at boot) | Path | Purpose | |---|---| | `storage/logs/` | Application log file (`app.log`) | | `storage/sessions/` | PHP session files | | `storage/cache/` | PHPUnit cache | | `storage/tmp/` | Temporary files (e.g. label downloads) | --- ## Platform Requirements (Development) - PHP 8.4+ with extensions: `pdo_mysql`, `openssl`, `curl`, `mbstring` - MySQL 5.7+ or MySQL 8.x - Node.js + npm (for SCSS compilation only) - XAMPP on Windows: `C:\xampp\php\php.exe` (add to `PATH`) --- *Stack analysis: 2026-03-12*