# Technology Stack & Integrations

## Languages

| Language | Version | Notes |
|----------|---------|-------|
| PHP | 7.4 – <8.0 | Production constraint — no PHP 8.0+ syntax |
| JavaScript | ES5 + jQuery 2.1.3 | No modern framework |
| CSS | Bootstrap 4.x (pre-compiled SCSS) | No build pipeline |

**PHP 8.0+ features explicitly forbidden:**
- `match` expressions → use ternary / if-else
- Named arguments
- Union types (`int|string`) → use single type + docblock
- `str_contains()`, `str_starts_with()`, `str_ends_with()` → use `strpos()`

## Core Libraries

| Library | Version | Location | Purpose |
|---------|---------|----------|---------|
| Medoo | 1.7.10 | `libraries/medoo/medoo.php` | Database ORM |
| PHPMailer | classic | `libraries/phpmailer/` | Email sending |
| RedBeanPHP | — | `libraries/rb.php` | Legacy ORM — **unused, candidate for removal** |

## Frontend Libraries

| Library | Location | Purpose |
|---------|----------|---------|
| jQuery | 2.1.3 | DOM / AJAX |
| jQuery Migrate | 1.0.0 | Backward compat |
| Bootstrap | 4.1.3 / 4.5.2 | `libraries/bootstrap*/` |
| CKEditor | 4.x | `libraries/ckeditor/` | Rich text editor |
| ApexCharts | — | `libraries/apexcharts/` | Admin charts |
| FancyBox | 2 + 3 | `libraries/fancyBox/`, `fancybox3/` | Lightbox |
| Plupload | — | `libraries/plupload/` | File uploads |
| Selectize.js | — | — | Select dropdowns |
| Lozad.js | — | — | Lazy loading |
| Swiper | — | — | Carousel/slider |
| CodeMirror | — | `libraries/codemirror/` | Code editor |
| Font Awesome | 5.7.0 | `libraries/fontawesome-5.7.0/` | Icons |
| File Manager | 9.14.1 & 9.14.2 | `libraries/filemanager-9.14.*/` | File browsing |

## Database

- **ORM**: Medoo 1.7.10 (custom-extended with Redis support)
- **Engine**: MySQL
- **Table prefix**: `pp_`
- **Connection**: `new medoo([...])` in each entry point via credentials from `config.php`
- **Key tables**: `pp_shop_products`, `pp_shop_orders`, `pp_shop_categories`, `pp_shop_clients`

## Caching

- **Technology**: Redis
- **PHP extension**: Native `Redis` class
- **Wrapper**: `\Shared\Cache\CacheHandler` (singleton via `RedisConnection`)
- **Config**: `config.php` → `$config['redis']['host/port/password']`
- **Serialization**: PHP `serialize()` / `unserialize()`
- **Default TTL**: 86400 seconds (24h)
- **Key patterns**:
  - `shop\product:{id}:{lang_id}:{hash}` — product details
  - `ProductRepository::getProductPermutationQuantityOptions:v2:{id}:*`
  - `pp_routes:all` — URL routing patterns
  - `pp_settings_cache` — shop settings

## Email

- **Library**: PHPMailer (classic, not v6)
- **Config**: `config.php` (host, port, login, password)
- **Helpers**:
  - `\Shared\Helpers\Helpers::send_email($to, $subject, $text, $reply, $file)`
  - `\Shared\Email\Email::send(...)` — newsletter / template-based
- **Issue**: Duplicate PHPMailer logic in both classes — should be unified

## HTTP Client

- **Technology**: Native PHP cURL (`curl_init`, `curl_setopt`, `curl_exec`)
- **No abstraction library** (no Guzzle, Symfony HTTP Client)
- **Used in**: `IntegrationsRepository.php` (Apilo calls), `cron.php` (image downloads)

## Dev & Build Tools

| Tool | Purpose |
|------|---------|
| Composer | PHP dependency management |
| PHPUnit 9.6 | Testing (`phpunit.phar`) |
| PowerShell `test.ps1` | Recommended test runner |
| No webpack/Vite/Gulp | SCSS pre-compiled, assets served as-is |

## External Integrations

### Apilo (ERP/WMS)
- **Auth**: OAuth 2.0 Bearer token (client_id + client_secret from `pp_shop_apilo_settings`)
- **Base URL**: `https://projectpro.apilo.com/rest/api/`
- **Sync operations**: order sending, payment sync, status polling, product qty/price sync, pricelist sync
- **Code**: `autoload/Domain/Integrations/IntegrationsRepository.php`
- **Cron jobs**: `APILO_SEND_ORDER`, `APILO_SYNC_PAYMENT`, `APILO_STATUS_POLL`, `APILO_PRODUCT_SYNC`, `APILO_PRICELIST_SYNC`
- **Logging**: `\Domain\Integrations\ApiloLogger` → `pp_log` table

### Ekomi (Reviews)
- **Type**: CSV export
- **Code**: `api.php` → generates `/ekomi/ekomi-{date}.csv`

### TrustMate (Review Invitations)
- **Type**: Browser-based (requires JS execution)
- **Code**: `cron.php` (line ~741), `cron-trustmate.php`
- **Config**: `$config['trustmate']['enabled']`

### Google Shopping Feed
- **Type**: XML feed generation
- **Cron job**: `GOOGLE_XML_FEED`
- **Code**: `cron.php` → `ProductRepository::generateGoogleFeedXml()`

### shopPRO Product Import
- **Type**: Direct MySQL connection to remote shopPRO instance
- **Config**: `pp_shop_shoppro_settings` (domain, db credentials)
- **Code**: `IntegrationsRepository.php` (lines 668–850)
- **Logs**: `/logs/shoppro-import-debug.log`

### REST API (ordersPRO — outbound)
- **Auth**: `X-Api-Key` header
- **Endpoints**: orders (list/get/status/paid), products (list/get), dictionaries, categories
- **Code**: `api.php` → `autoload/api/ApiRouter.php` → `autoload/api/Controllers/`

## Cron Job System

| Job Type | Purpose |
|----------|---------|
| `APILO_TOKEN_KEEPALIVE` | OAuth token refresh |
| `APILO_SEND_ORDER` | Sync orders to Apilo (priority 40) |
| `APILO_SYNC_PAYMENT` | Sync payment status |
| `APILO_STATUS_POLL` | Poll order status changes |
| `APILO_PRODUCT_SYNC` | Update product qty & prices |
| `APILO_PRICELIST_SYNC` | Update pricelist |
| `PRICE_HISTORY` | Record price history |
| `ORDER_ANALYSIS` | Order/product correlation |
| `TRUSTMATE_INVITATION` | Review invitations |
| `GOOGLE_XML_FEED` | Google Shopping XML |

- **Priority levels**: CRITICAL(10), HIGH(50), NORMAL(100), LOW(200)
- **Backoff**: Exponential on failure (60s → 3600s max)
- **Storage**: `pp_cron_jobs` table
