# VIXHUB — OMNICHANNEL / E-COMMERCE RE-AUDIT REPORT (DODATEK) ### Głęboki prześwietl modułu online/gateway/cart/orders w repozytoriach SliceHub **Audytor:** Principal System Architect & Lead Auditor **Data:** 2026-08-25 **Zakres:** `slicehub_pro` + `slicehub` — katalogi `api/online/`, `api/cart/`, `api/gateway/`, `api/orders/`, `api/payments/` (tylko slicehub), `modules/online/`, `modules/online_studio/`, `core/GatewayAuth.php`, `core/OrderEventPublisher.php`, `core/OrderStateMachine.php`, `core/SettlementEngine.php`, migracje 001/008/009/017/026/027/047/054/068/069 **Protokół:** Repo-First Policy (§II Vixplan) — uzupełnienie pominiętego silosu Omnichannel --- ## 0. STRESZCZENIE WYKONAWCZE (TL;DR) **Poprzedni raport miał krytyczną lukę.** SliceHub Pro posiada **kompletny, produkcyjny silnik e-commerce / omnichannel** — nie jest to "tylko POS restauracyjny". Znaleziono: - **Publiczny storefront API** (`api/online/engine.php`, 122 KB, 12 akcji) — get_menu, get_dish, cart_calculate, delivery_zones, init_checkout, **guest_checkout** (atomowy INSERT zamówienia), track_order (z GPS kierowcy na mapie Leaflet) - **Unified Gateway v2** (`api/gateway/intake.php`, 28 KB) — jedna bramka dla wszystkich źródeł: `web`, `mobile_app`, `kiosk`, `pos_3rd`, `public_api`, `aggregator_uber`, `aggregator_glovo`, `aggregator_pyszne`, `aggregator_wolt` — z autoryzacją X-API-Key, rate-limiting (sliding window), idempotency przez `external_id`, walidacją schema per source - **Server-Authoritative Cart Engine** (`api/cart/CartEngine.php`, 47 KB) — pure calculation class, grosze (int minor), auto-promocje (`sh_promotions`), promo codes (`sh_promo_codes`), channel-aware pricing (`sh_price_tiers`), half-half surcharge - **Payment Settlement** (`api/payments/settle.php` w slicehub, `core/SettlementEngine.php`) — split-tender (wiele płatności sumujących się do totalu), tip_amount, print_receipt - **Full PWA Storefront** (`modules/online/`, 20 plików) — manifest.webmanifest, service worker (offline.html), Surface dark canvas, akordeon kategorii, cart drawer, 3-step checkout, **Order Tracker z mapą LIVE** (Leaflet + GPS kierowcy z `sh_driver_locations`) - **Transactional Outbox** (`sh_event_outbox`, m026) + **Webhook Endpoints** (`sh_webhook_endpoints`, HMAC-SHA256 signing) + **Integration Deliveries** (m028, retry/backoff/DLQ) - **Idempotency Locks** (`sh_checkout_locks`, m017) — TTL 5min, cart_hash SHA-256, zapobieganie double-tap checkout - **External Order Refs** (`sh_external_order_refs`, m027) — mapowanie external_id → order_id, idempotency dla aggregatorów - **Guest Tracking** (`tracking_token` na `sh_orders`, m017) — 16-hex token + phone match, bez rejestracji - **Delivery Zones** (`sh_delivery_zones`, m001) — POLYGON + SPATIAL INDEX, geo-fence z `ST_GeomFromText` - **3-Pillar State Machine** (m009) — `status` (kitchen) × `payment_status` (payment) × `delivery_status` (delivery), niezależne osie **Kluczowe dla Vixhub:** Ten kod to **gotowy szkielet sklepu D2C** — nie trzeba pisać od zera koszyka, checkoutu, trackera, gateway, idempotency, rate-limitingu, webhooków. Trzeba: (1) spłaszczyć Multi-Tenant→Single-Tenant, (2) zaadaptować domeny restauracyjnej→meblowej, (3) dopisać integracje PayPo/Ratami/WooCommerce. **ZERO WooCommerce/WordPress w kodzie** — potwierdzone (grep: 1 wzmianka w zarchiwizowanym doc wizji `_docs/ARCHIWUM/06_WIZJA_MODULU_ONLINE.md` linia 36, jako konkurent do pokonania, nie integracja). --- ## 1. ŚCIEŻKI I NAZWY PLIKÓW — POMINIĘTY KOD ONLINE/OMNICHANNEL ### 1.1 `slicehub_pro` — silos Omnichannel (KOMPLETNY) #### API Backend (PHP, strict_types) | Ścieżka | Rozmiar | Funkcja (z odczytu kodu) | |---------|---------|------------------------| | `api/online/engine.php` | 122 KB | **Publiczny storefront REST API** (POST, action-based, PUBLIC — no JWT). 12 akcji: `get_storefront_settings`, `get_doorway`, `get_menu`, `get_dish`, `cart_calculate`, `get_popular`, `delivery_zones`, `estimate_time`, `get_scene_menu`, `get_scene_dish`, `get_scene_category`, **`guest_checkout`** (atomowy INSERT sh_orders + sh_order_lines + sh_order_audit + OrderEventPublisher + sh_checkout_locks consume), **`init_checkout`** (lock_token TTL 5min, cart_hash SHA-256), **`track_order`** (token + phone match, ETA, driver GPS). Tenant scope z POST body (`tenantId`). Channel-aware pricing przez `sh_price_tiers`. Schema detection (graceful skip jeśli optional table missing). | | `api/online/sse.php` | 5.7 KB | Server-Sent Events dla realtime updates storefront | | `api/cart/CartEngine.php` | 47 KB | **Server-Authoritative Cart Engine** — pure calculation class (zero HTTP). Grosze (int minor). Auto-promocje (`sh_promotions`: discount_percent, discount_amount, combo_half_price, free_item_if_threshold, bundle), promo codes (`sh_promo_codes`), channel-aware pricing (`sh_price_tiers`), half-half surcharge, time-window gating (days + start/end), stock validation. | | `api/cart/calculate.php` | 2.5 KB | Thin HTTP wrapper nad CartEngine (preview, no state mutation) | | `api/gateway/intake.php` | 28 KB | **Unified Gateway v2** — jedna bramka dla wszystkich źródeł zamówień. Flow: (1) Auth X-API-Key (GatewayAuth), (2) Rate limit per key (minute+day sliding window), (3) Idempotency external_id (sh_external_order_refs), (4) Schema validation per source, (5) CartEngine recalculate, (6) Atomic INSERT sh_orders + sh_order_lines + sh_order_audit + publish event, (7) Response order_id/order_number/was_duplicate. Source whitelist: web, mobile_app, public_api, kiosk, pos_3rd, internal, aggregator, aggregator_uber, aggregator_glovo, aggregator_pyszne, aggregator_wolt. Order number prefix per source (WWW/MOB/KIO/EXT/API/UBR/GLV/PYS/WLT). | | `api/orders/audit.php` | 21 KB | Audit log zamówienia | | `api/orders/edit.php` | 49 KB | Edycja zamówienia (RFC-001 Faza 2/3 — edycja metadanych, korekta pozycji, revert, reopen zamkniętych) | | `api/orders/estimate.php` | 5.4 KB | Estymacja czasu (PromisedTimeEngine) | | `api/orders/get.php` | 6.8 KB | Pobranie zamówienia | | `api/orders/get_for_edit.php` | 4.9 KB | Pobranie do edycji | | `api/orders/history.php` | 14 KB | Historia zamówień | | `api/orders/reopen.php` | 4.2 KB | Reopen zamkniętego zamówienia | | `api/orders/revert.php` | 14 KB | Revert (korekta) | | `api/orders/sla_monitor.php` | 7.3 KB | SLA monitor (green/yellow/red) | | `api/orders/DeltaEngine.php` | 6.5 KB | Silnik delty (kitchen_delta JSON — diff pozycji po edycji) | #### Core (PHP, strict_types) | Ścieżka | Funkcja | |---------|---------| | `core/GatewayAuth.php` | Autoryzacja kluczy API gateway: `authenticateKey()` (SHA-256 hash, hash_equals timing-safe), `checkAndIncrementRateLimit()` (sliding window in-DB), `lookupExternalRef()` / `storeExternalRef()` (idempotency). Legacy fallback do env GATEWAY_API_KEY. Scopes: order:create, order:read, menu:read, *. | | `core/OrderEventPublisher.php` | Transactional outbox publisher — publishOrderLifecycle() w tej samej transakcji co INSERT sh_orders. 19 kanonicznych eventów (order.created/accepted/preparing/ready/dispatched/in_delivery/delivered/completed/cancelled/edited/recalled/delayed/fiscalized + payment.settled/refunded + metadata_edited/payment_changed/force_edited/reverted/reopened). Idempotency key, snapshot payload. | | `core/OrderStateMachine.php` | Status transitions z whitelistą (3-pillar: status × payment_status × delivery_status) | | `core/SettlementEngine.php` | Settlement brain — split-tender, tip, print_receipt | | `core/PromisedTimeEngine.php` | Dynamiczny kalkulator promised_time (ASAP z load-factor + scheduled z lead-time gate + business-hours gate) | | `core/SlaThresholds.php` | Progi SLA | | `core/SequenceEngine.php` | Atomic order numbering (ON DUPLICATE KEY — race-safe) | | `core/WebhookDispatcher.php` | Generic HTTP push do sh_webhook_endpoints (HMAC-SHA256 signing, retry, backoff) | | `core/Integrations/BaseAdapter.php` | Abstract kontrakt dla adapterów 3rd-party (buildRequest, parseResponse, parseInboundCallback) | | `core/Integrations/AdapterRegistry.php` | Mapa provider_key → class, resolveForTenant() | | `core/Integrations/IntegrationDispatcher.php` | Konsument outbox dla adapterów (exponential backoff 30s→86400s, DLQ, audit) | #### Frontend PWA (`modules/online/`) | Ścieżka | Rozmiar | Funkcja | |---------|---------|---------| | `modules/online/index.html` | 12 KB | Storefront entry point (Surface dark canvas) | | `modules/online/manifest.webmanifest` | 2 KB | PWA manifest (installable, standalone) | | `modules/online/sw.js` | 2.9 KB | Service Worker (offline.html fallback, cache) | | `modules/online/offline.html` | 1.8 KB | Offline fallback page | | `modules/online/track.html` | 8.5 KB | Order Tracker page (Leaflet map) | | `modules/online/css/style.css` | 94 KB | Storefront styles (mobile-first, glass-morphism) | | `modules/online/css/doorway.css` | 14 KB | Doorway (hero surface) styles | | `modules/online/css/living-scene.css` | 8 KB | Living Scene (animacje) | | `modules/online/css/track.css` | 23 KB | Tracker styles | | `modules/online/js/online_api.js` | 4.7 KB | API wrapper (fetch, JSON, tenantId) | | `modules/online/js/online_app.js` | 22 KB | Główna logika storefrontu (stan, routing) | | `modules/online/js/online_checkout.js` | 41 KB | **Checkout overlay** — 3 kroki (dostawa→płatność→potwierdzenie), init_checkout→lock_token, guest_checkout→orderNumber+trackingToken, localStorage persistence, success screen z CTA "Śledź zamówienie". Walidacja: imię, phone (min 9 cyfr), email, adres dostawy. | | `modules/online/js/online_track.js` | 25 KB | **Order Tracker** — polling co 10s (TRACK_POLL_MS), timeline 6 stages (new→accepted→preparing→ready→in_delivery→completed), driver card z GPS, Leaflet map z pozycją kierowcy + adresem dostawy, status pills, stop polling gdy completed/cancelled. | | `modules/online/js/online_doorway.js` | 18 KB | Doorway (hero surface, channel selector dostawa/odbiór) | | `modules/online/js/online_renderer.js` | 4.7 KB | Renderer (Surface Card, warstwy) | | `modules/online/js/online_table.js` | 10 KB | Table (akordeon kategorii) | | `modules/online/js/online_ui.js` | 41 KB | UI helpers (cart drawer, FAB, toasts) | | `modules/online/js/surface/ModifierOrchestrator.js` | 2.8 KB | Orkestrator modyfikatorów na surface | #### Manager Editor (`modules/online_studio/`) | Ścieżka | Rozmiar | Funkcja | |---------|---------|---------| | `api/online_studio/engine.php` | 124 KB | Manager API (auth_guard, JWT): library_list/upload/update/delete, composer_load_dish/save_layers/calibrate, surface_upload, companions_save, preview_render | | `api/online_studio/library_upload.php` | 9.4 KB | Multipart upload assetów (dual-photo: layer + hero) | | `modules/online_studio/index.html` | 5.7 KB | Studio entry | | `modules/online_studio/js/studio_app.js` + `studio_api.js` | 26 KB | Główna logika + API wrapper | | `modules/online_studio/js/director/DirectorApp.js` | 50 KB | Director (kompozytor scen) | | `modules/online_studio/js/director/panels/*` | 7 plików | HierarchyPanel, InspectorPanel, PromotionsPanel, ScenographyPanel, TimelinePanel, ToolbarPanel, ViewportPanel | | `modules/online_studio/js/director/magic/*` | 8 plików | MagicBake, MagicColorGrade, MagicCompanions, MagicConform, MagicDust, MagicEnhance, MagicHarmonize, MagicRelight | | `modules/online_studio/js/tabs/storefront.js` | 21 KB | Storefront tab (podgląd klienta) | #### Migracje (SQL) | Migracja | Tabele/Kolumny | Funkcja | |----------|----------------|---------| | `001_init_slicehub_pro_v2.sql` | `sh_orders` (id CHAR(36), tenant_id, order_number, channel, order_type, source, subtotal/discount/delivery_fee/grand_total INT grosze, status, payment_status, payment_method, customer_name/phone, delivery_address, lat/lng, promised_time, driver_id, course_id, stop_number, tip_amount, kitchen_delta JSON), `sh_order_lines` (id CHAR(36), order_id, item_sku, snapshot_name, unit_price INT, quantity, line_total INT, vat_rate, vat_amount, modifiers_json, removed_ingredients_json, comment), `sh_order_audit`, `sh_delivery_zones` (POLYGON + SPATIAL INDEX), `sh_price_tiers` (target_type, target_sku, channel, price — macierz cenowa omnichannel), `sh_promo_codes`, `sh_product_mapping` (external_name→internal_sku), `sh_order_item_modifiers`, `sh_sla_breaches`, `sh_dispatch_log` | **Schema bazowa orders/cart/pricing/delivery** | | `008_delivery_ecosystem.sql` | `sh_driver_locations` (driver_id, tenant_id, lat, lng, heading, speed_kmh, accuracy_m) | Real-time GPS tracking kierowcy | | `009_delivery_state_machine.sql` | `sh_orders.delivery_status` (NULL/unassigned/in_delivery/delivered), `sh_orders.cancellation_reason` | 3-Pillar State Machine | | `017_online_module_extensions.sql` | `sh_checkout_locks` (lock_token CHAR(36), tenant_id, customer_phone, cart_hash SHA-64, grand_total_grosze, channel, expires_at, consumed_at, consumed_order_id), `sh_orders.tracking_token` (CHAR(16) hex, idx_orders_tracking), `sh_visual_layers.version` (optimistic locking), `sh_visual_layers.library_category/sub_type`, `sh_tenant_settings` defaults (online_min_order_value, online_default_eta_min, online_guest_checkout, online_apple_pay_enabled, online_promotion_banner) | **Idempotency checkout + guest tracking + online settings** | | `026_event_system.sql` | `sh_event_outbox` (id, tenant_id, event_type, aggregate_type, aggregate_id, idempotency_key, payload JSON, source, actor_type/id, status ENUM pending/dispatching/delivered/failed/dead, attempts, next_attempt_at, last_error), `sh_webhook_endpoints` (id, tenant_id, name, url, secret HMAC-SHA256, events_subscribed JSON, is_active, max_retries, timeout_seconds, consecutive_failures) | **Transactional Outbox + Webhook Endpoints** | | `027_gateway_v2.sql` | `sh_gateway_api_keys` (id, tenant_id, key_prefix, key_secret_hash SHA-256, name, source, scopes JSON, rate_limit_per_min/day, is_active, last_used_at/ip, expires_at), `sh_rate_limits` (api_key_id, window_kind minute/day/hour, window_bucket, request_count — sliding window in-DB), `sh_external_order_refs` (tenant_id, source, external_id, order_id, api_key_id, request_hash SHA-256 — idempotency), `sh_orders.gateway_source` + `gateway_external_id` | **Gateway v2 — multi-source intake, API keys, rate limiting, idempotency** | | `047_order_geocoding.sql` | `sh_orders` geocoding columns | Geokodowanie adresu dostawy | | `054_order_line_combo_meta.sql` | `sh_order_lines` combo meta | Combo/bundle metadata | | `068_order_metadata_notes.sql` | `sh_orders` metadata/notes | Dodatkowe metadane + notatki | | `069_order_is_corrected.sql` | `sh_orders.is_corrected` | Flaga korekty | ### 1.2 `slicehub` (starszy) — różnice vs `slicehub_pro` | Ścieżka | Rozmiar | Różnica vs slicehub_pro | |---------|---------|------------------------| | `api/online/engine.php` | 110 KB | Starsza wersja (mniej akcji, brak get_scene_*, brak guest_checkout z lock_token) | | `api/gateway/intake.php` | 28 KB | Niemal identyczny | | `api/payments/settle.php` | 6.9 KB | **OBECNY w slicehub, BRAK w slicehub_pro** — thin wrapper nad SettlementEngine (split-tender, tip, print_receipt). W slicehub_pro settle jest inline w POS, nie osobny endpoint. | | `api/cart/CartEngine.php` | — | Starsza wersja (brak auto-promocji sh_promotions) | **Wniosek:** `slicehub_pro` jest nadzbiorem z wyjątkiem `api/payments/settle.php` jako osobnego endpointu (warto zaadaptować ten wzorzec dla Vixhub — osobny endpoint płatności, nie inline). --- ## 2. MATRYCA REUŻYCIA (REPO-FIRST) — SKLEP D2C DLA VIXHUB ### 2.1 Komponenty do wyciągnięcia 1:1 (po spłaszczeniu Multi-Tenant→Single-Tenant) | # | Komponent | Ścieżka źródłowa | Funkcja w Vixhub | Refaktoring Single-Tenant | |---|-----------|------------------|------------------|---------------------------| | **1** | **Public Storefront API** | `api/online/engine.php` (122 KB) | Backend sklepu D2C Vilmax — get_menu (katalog mebli), get_dish (karta produktu z wariantami), cart_calculate, delivery_zones (strefy dostawy mebli), init_checkout, guest_checkout, track_order (śledzenie wysyłki mebli) | Usunąć `tenantId` z POST body (hardcoded `tenant_id=1` lub usunąć kolumnę). Zmienić domeny: `sh_menu_items`→`vx_products`, `sh_modifiers`→`vx_fabric_options`, `sh_categories`→`vx_furniture_categories`. Akcje get_scene_* → zaadaptować pod Visual Studio mebli. | | **2** | **Server-Authoritative Cart Engine** | `api/cart/CartEngine.php` (47 KB) | Koszyk sklepu D2C — zero-trust (serwer przelicza, nie ufa klientowi), grosze (int minor), auto-promocje, promo codes, channel-aware pricing (cena mebla różna dla D2C vs Allegro vs hurt) | Usunąć `$tenantId` parametr. Domena: half-half surcharge→ "wariant tkaniny surcharge", combo_half_price→ "zestaw sofowy rabat". | | **3** | **Unified Gateway v2** | `api/gateway/intake.php` (28 KB) | Bramka dla wszystkich źródeł zamówień meblowych: `web` (sklep D2C), `mobile_app`, `public_api` (integracja BaseLinker), `aggregator_allegro`, `aggregator_erli`. Idempotency external_id (Allegro order_ref, BL order_id). | Usunąć tenant barrier. Dodać source `aggregator_allegro`, `aggregator_erli` do whitelist. Order number prefix: WWW/MOB/API/ALG/ERL. | | **4** | **GatewayAuth** | `core/GatewayAuth.php` | Autoryzacja kluczy API (BaseLinker API key, Allegro API key) — SHA-256 hash, timing-safe, rate-limit sliding window, scopes (order:create, order:read, menu:read) | Usunąć tenant_id z kluczy. | | **5** | **OrderEventPublisher** | `core/OrderEventPublisher.php` | Transactional outbox — eventy lifecycle zamówienia meblowego (order.created→BaseLinker sync, order.accepted→produkcja, order.dispatched→kurier, order.delivered→klient, order.completed→faktura) | Usunąć tenant_id. Dodać eventy meblowe: `order.production_started`, `order.production_completed`, `order.shipped`. | | **6** | **OrderStateMachine** | `core/OrderStateMachine.php` | 3-Pillar State Machine — status (produkcja) × payment_status (PayPo/Ratami/przelew) × delivery_status (kurier DPD/InPost) | Dodać statusy: `in_production`, `ready_to_ship`. Payment: `paypo_pending`, `ratami_pending`, `prepaid`. | | **7** | **SettlementEngine** | `core/SettlementEngine.php` + `api/payments/settle.php` (slicehub) | Settlement — split-tender (PayPo + Ratami + przelew w jednej zamówieniu), tip→ "koszt wniesienia", print_receipt→faktura | Zaadaptować pod PayPo/Ratami. | | **8** | **PromisedTimeEngine** | `core/PromisedTimeEngine.php` | Dynamiczny czas realizacji Make-to-Order — base_prep (produkcja mebla 5/7/10/14 dni) × load_factor (obciążenie produkcji) + channel_buffer (D2C vs Allegro vs hurt) | Przenieść stałe do config (DEFAULT_BASE_PREP=7 dni, CHANNEL_BUFFERS per kanał). Zgodne z §IV Vixplan (czas z BaseLinkera jako zmienna dynamiczna). | | **9** | **SequenceEngine** | `core/SequenceEngine.php` | Atomic order numbering — race-safe (ON DUPLICATE KEY) | Bezpośrednio 1:1 | | **10** | **WebhookDispatcher** | `core/WebhookDispatcher.php` | Webhooki do BaseLinkera/Allegro (status updates, order sync) — HMAC-SHA256 signing, retry, backoff | Usunąć tenant_id. | | **11** | **Integrations (BaseAdapter + AdapterRegistry + IntegrationDispatcher)** | `core/Integrations/*` | Wzorzec adaptera dla `BaseLinkerAdapter`, `AllegroAdapter`, `ErliAdapter` — retry/backoff/DLQ/CredentialVault | Usunąć tenant_id. Dodać providery: `baselinker`, `allegro`, `erli`, `paypo`, `ratami`. | | **12** | **PWA Storefront** | `modules/online/` (20 plików) | Frontend sklepu D2C — manifest, service worker, Surface dark canvas, akordeon kategorii mebli, cart drawer, 3-step checkout, Order Tracker z mapą (kurier DPD/InPost zamiast kierowcy restauracji) | Zmienić domeny UI: pizza→meble, modyfikatory→tkaniny/rozmiary, companions→dodatki (poduszki, pościele). Tracker: driver→kurier (InPost paczkomat map, DPD tracking). | | **13** | **Checkout Overlay** | `modules/online/js/online_checkout.js` (41 KB) | 3-step checkout (dostawa→płatność→potwierdzenie), init_checkout→lock_token, guest_checkout, localStorage, success screen z CTA tracker | Dodać metody płatności: PayPo, Ratami 0%, przelew, BLIK. Adres dostawy: paczkomat InPost (geokodowanie paczkomatów). | | **14** | **Order Tracker** | `modules/online/js/online_track.js` (25 KB) | Tracker zamówienia meblowego — timeline (złożone→w produkcji→gotowe→wysłane→dostarczone), mapa z paczkomatem/oddziałem kuriera, polling 10s | Zmienić stages: new→accepted→in_production→ready_to_ship→shipped→delivered. Mapa: InPost paczkomat network zamiast driver GPS. | | **15** | **Migracja 017 (checkout locks + tracking)** | `database/migrations/017_online_module_extensions.sql` | `sh_checkout_locks` (idempotency), `sh_orders.tracking_token` (guest tracking), `sh_tenant_settings` online defaults | Usunąć tenant_id z sh_checkout_locks. Zmienić prefix vx_. | | **16** | **Migracja 026 (event outbox + webhooks)** | `database/migrations/026_event_system.sql` | `sh_event_outbox`, `sh_webhook_endpoints` (HMAC-SHA256) | Usunąć tenant_id. | | **17** | **Migracja 027 (gateway v2)** | `database/migrations/027_gateway_v2.sql` | `sh_gateway_api_keys`, `sh_rate_limits`, `sh_external_order_refs`, `sh_orders.gateway_source/external_id` | Usunąć tenant_id. | | **18** | **Migracja 001 (orders/order_lines/delivery_zones/price_tiers/promo_codes)** | `database/migrations/001_init_slicehub_pro_v2.sql` | `sh_orders`, `sh_order_lines`, `sh_delivery_zones` (POLYGON geo-fence), `sh_price_tiers` (macierz cenowa omnichannel), `sh_promo_codes`, `sh_product_mapping` (external_name→internal_sku) | Usunąć tenant_id. Zmienić domeny: sh_menu_items→vx_products, sh_price_tiers→vx_price_tiers. | | **19** | **Migracja 008 (driver locations)** | `database/migrations/008_delivery_ecosystem.sql` | `sh_driver_locations` → `vx_courier_locations` (GPS kuriera InPost/DPD) | Usunąć tenant_id. | | **20** | **Migracja 009 (3-pillar state machine)** | `database/migrations/009_delivery_state_machine.sql` | `sh_orders.delivery_status`, `cancellation_reason` | Bezpośrednio 1:1. | ### 2.2 Wzorce architektoniczne do zaadaptowania (nie 1:1, ale jako blueprint) | Wzorzec | Gdzie w kodzie | Zastosowanie w Vixhub | |---------|----------------|----------------------| | **Transactional Outbox** | `OrderEventPublisher::publishOrderLifecycle()` + `sh_event_outbox` + `scripts/worker_webhooks.php` | Każde zamówienie D2C publikuje event → worker synchronizuje z BaseLinkerem (status, tracking number, faktura). Zero eventual inconsistency. | | **Idempotency Locks** | `sh_checkout_locks` (lock_token, cart_hash SHA-256, TTL 5min) | Zapobieganie double-tap checkout w sklepie D2C (klient klika 2× "Zamawiam"). | | **External Idempotency** | `sh_external_order_refs` (tenant_id, source, external_id, order_id, request_hash) | Allegro/Erli wysyła to samo zamówienie 2× (network retry) → drugi POST zwraca identyczny order_id + was_duplicate=true. | | **Rate Limiting (sliding window in-DB)** | `sh_rate_limits` (api_key_id, window_bucket, request_count) | Ochrona przed nadużyciami API BaseLinkera/Allegro. Docelowo Redis (komentarz w m027: "Dla dużego ruchu docelowo Redis"). | | **Channel-Aware Pricing** | `sh_price_tiers` (target_type, target_sku, channel, price) | Cena mebla różna dla: D2C (sklep własny), Allegro, Erli, hurt. Macierz cenowa omnichannel. | | **3-Pillar State Machine** | `status` × `payment_status` × `delivery_status` (m009) | Niezależne osie: produkcja (status) × płatność (PayPo/Ratami/przelew) × wysyłka (InPost/DPD/paczkomat). | | **Guest Checkout (phone-keyed)** | `sh_orders.customer_phone` + `tracking_token` (m017) | Sklep D2C bez rejestracji — phone jako klucz klienta, tracking_token do śledzenia. Zgodne z §Vixplan (guest checkout must-have). | | **Geo-fence Delivery Zones** | `sh_delivery_zones` (POLYGON + SPATIAL INDEX) | Strefy dostawy mebli (InPost paczkomaty, strefy kuriera DPD). | | **Schema Detection (graceful skip)** | `engine.php`: `$pdo->query("SELECT 1 FROM sh_checkout_locks LIMIT 0")` w try/catch | Defensive — endpoint działa nawet jeśli optional tabela nie istnieje (świeża instalacja). | --- ## 3. GAP ANALYSIS DLA SKLEPU — BRaki do spięcia z WooCommerce/PayPo/BaseLinker ### 3.1 Braki KRYTYCZNE (do napisania od zera) | Brak | Priorytet | Stack | Uwagi | |------|-----------|-------|-------| | **Integracja WooCommerce (WordPress)** | WYSOKI | WordPress + WooCommerce + PHP | **ZERO w kodzie** (grep potwierdza — 1 wzmianka w zarchiwizowanym doc wizji). SliceHub ma własny storefront (modules/online), NIE WooCommerce. Dla Vixhub §VIII wymaga WooCommerce — trzeba wdrożyć WordPress + WooCommerce + theme meblowy + spiąć z backendem Vixhub przez Gateway v2 (source=`web` lub nowy `woocommerce`). | | **Integracja PayPo** | WYSOKI | PHP REST client | **ZERO w kodzie**. PayPo = "kup teraz, zapłać za 30 dni". Trzeba: (1) `PayPoAdapter` (extends BaseAdapter), (2) redirect do PayPo w checkout, (3) callback webhook z PayPo (status: approved/rejected), (4) `payment_status='paypo_pending'/'paypo_approved'/'paypo_rejected'`. | | **Integracja Ratami 0%** | WYSOKI | PHP REST client | **ZERO w kodzie**. Ratami = raty 0%. Trzeba: (1) `RatamiAdapter`, (2) kalkulator rat w checkout (slider: 3/6/9/12 miesięcy), (3) redirect do Ratami, (4) callback webhook. | | **Moduł próbek darmowych** | WYSOKI | PHP + JS | **ZERO w kodzie**. Vixplan §VIII wymaga: klient zamawia darmowe próbki tkanin (limit N na adres). Trzeba: (1) endpoint `request_samples`, (2) rate-limit per phone/adres, (3) integracja z BaseLinker (order z flagą `is_sample`). | | **InPost Paczkomat geokodowanie** | ŚREDNI | JS + InPost API | `sh_delivery_zones` (POLYGON) jest, ale InPost paczkomaty to punktowa dostawa (nie polygon). Trzeba: (1) InPost API client (lista paczkomatów), (2) mapa Leaflet z paczkomatami, (3) wybór paczkomatu w checkout. | | **DPD/Kurier tracking integration** | ŚREDNI | PHP REST | `online_track.js` ma mapę Leaflet + driver GPS, ale dla mebli trzeba: (1) integracja DPD tracking API, (2) InPost tracking API, (3) osadzenie na tracker map. | ### 3.2 Braki ŚREDNIE (wymagają przebudowy istniejącego kodu) | Brak | Co jest | Co brakuje | |------|---------|------------| | **Warianty produktów meblowych** | `sh_menu_items` z `parent_sku` (warianty: rozmiar, kolor, tkanina) — engine.php get_menu obsługuje family/variants | Domena: warianty meblowe (rozmiar 140×200/160×200/180×200, tkanina A/B/C, mechanizm DL/delfin/puma) zamiast pizzy 30cm/40cm. Schema OK, dane do seedowania. | | **Stock reservation (Make-to-Order)** | `sh_orders` ma `source`, ale brak rezerwacji stoku na produkcję | Make-to-Order = nie ma stocku, jest czas produkcji. Trzeba: (1) `PromisedTimeEngine` z base_prep=5/7/10/14 dni (produkcyjny), (2) brak `sh_inventory` dla MTO, (3) flaga `is_made_to_order` na produkcie. | | **Faktura automatyczna (KSeF)** | `core/Ksef/Client.php` (API v2, RSA-OAEP, JWT) — gotowy klient KSeF | Zaadaptować: po order.completed → automatyczna faktura KSeF → publish event → BaseLinker sync. KSeF Client jest gotowy, trzeba business logic meblowej. | | **Apple Pay / Google Pay** | `sh_tenant_settings.online_apple_pay_enabled` (flaga w m017), ale **brak implementacji** | Stripe/PayPal integration — endpoint `payments/init_session` (wzmianka w doc wizji 06, P1). | | **Address autocomplete** | Brak | Google Places API lub Nominatim/OSM (P2 w doc wizji). | | **Loyalty/Rewards** | Brak | Phone-keyed (P2 w doc wizji). | ### 3.3 Braki NISKIE (kosmetyczne / P2-P3) | Brak | Uwagi | |------|-------| | SMS notifications | Twilio/Plivo + `core/Integrations/SmsClient.php` (wzmianka w doc wizji) | | Bundles/Combos | `sh_menu_items` typ `bundle` (P3 w doc wizji) — zestawy sofowe (sofa + fotel + pufa) | | Easy Orders / 1-click reorder | `localStorage.lastOrders` + endpoint `repeat_order` (P1 w doc wizji) | | WebSocket (zamiast polling) | `online_track.js` używa polling 10s — WebSocket fallback (P3 w doc wizji) | --- ## 4. BLOKADY I RYZYKA — OMNICHANNEL SPECIFIC ### 4.1 Multi-Tenant → Single-Tenant (KRYTYCZNE dla Vixhub) **Wszystkie pliki omnichannel mają `tenant_id` barrier:** - `api/online/engine.php`: `$tenantId = max(0, inputInt($input, 'tenantId'))` — tenant z POST body (public endpoint) - `api/gateway/intake.php`: `$tenantId = (int)($auth['tenantId'] ?? 0)` — tenant z API key - `api/cart/CartEngine.php`: `CartEngine::calculate($pdo, $tenant_id, $input)` — tenant jako parametr - `api/orders/*.php`: `tenant_id = :tenant_id` w każdym SQL - `core/OrderEventPublisher.php`: `publishOrderLifecycle($pdo, $tenantId, ...)` — tenant jako parametr - `core/GatewayAuth.php`: `authenticateKey($pdo, $providedKey, $tenantFromPayload)` — tenant z key/payload - `sh_orders`, `sh_order_lines`, `sh_checkout_locks`, `sh_external_order_refs`, `sh_event_outbox`, `sh_webhook_endpoints`, `sh_gateway_api_keys`, `sh_rate_limits`, `sh_delivery_zones`, `sh_price_tiers`, `sh_promo_codes` — wszystkie mają `tenant_id` kolumnę **Refaktoring Single-Tenant:** - **Opcja A (pragmatyczna):** Hardcoded `tenant_id=1` wszędzie. Minimalne zmiany w kodzie, ale kolumna zostaje. Szybkie. - **Opcja B (czysta, zgodna z §I.2 Vixplan "bez flag tenancyjnych"):** Usunąć `tenant_id` ze wszystkich tabel i zapytań. Duży refaktoring (~50 plików, ~200 zapytań SQL), ale zgodne z rygorem. - **Rekomendacja:** Opcja B dla pilota Vilmax (zgodność z Vixplan), ale Opcja A jako szybki start MVP. ### 4.2 Hardcoding czasów (§II.3 Vixplan) — OMNICHANNEL - `api/online/engine.php` linia ~1496: `PromisedTimeEngine::calculate()` — fallback null gdy silnik niedostępny (OK, nie blokuje checkout) - `api/gateway/intake.php` linia 290: `$minPrepMinutes = (int)($tenantSettings['min_prep_time_minutes'] ?: 30)` — **hardcoded fallback 30 min** - `sh_tenant_settings` defaults (m017): `online_default_eta_min='30'` — hardcoded default - `modules/online/js/online_track.js`: `TRACK_POLL_MS = 10000` — hardcoded polling interval (OK, to nie czas realizacji) **Wniosek:** Czasy w omnichannel są **w większości dynamiczne** (PromisedTimeEngine + sh_tenant_settings), ale fallbacki hardcoded. Dla Vixhub: przenieść wszystkie stałe czasowe do konfiguracji BaseLinkera (§IV Vixplan — czas jako zmienna dynamiczna z BL). ### 4.3 Strict Types (§I.1 Vixplan) - `api/online/engine.php`: **MA `declare(strict_types=1)`** ✓ - `api/cart/CartEngine.php`: **BRAK `declare(strict_types=1)`** ✗ (ale grosze jako int, bezpieczne) - `api/cart/calculate.php`: **BRAK** ✗ - `api/gateway/intake.php`: **BRAK** ✗ - `api/orders/*.php`: mieszane (część ma, część nie) - `core/GatewayAuth.php`: **MA** ✓ - `core/OrderEventPublisher.php`: **MA** ✓ - `core/OrderStateMachine.php`: **MA** ✓ - `core/SettlementEngine.php`: **MA** ✓ - `core/PromisedTimeEngine.php`: **BRAK** (ale klasa, nie strict) — do dodania **Wniosek:** Kluczowe silniki core mają strict_types. API endpointy (cart, gateway, orders) — braki. Dla Vixhub: dokończyć do 100%. ### 4.4 Bezpieczeństwo Omnichannel - **Gateway v2:** X-API-Key (SHA-256 hash, hash_equals timing-safe), rate-limit sliding window, scope-based authorization (order:create, order:read, menu:read), source-key binding (key mobile_app nie puszcza jako aggregator_uber — key-reuse attack protection). **WZORCOWE.** - **Checkout locks:** lock_token UUID, cart_hash SHA-256, TTL 5min, consumed_at — zapobieganie double-tap. **WZORCOWE.** - **Guest tracking:** tracking_token (16 hex) + phone match — bez rejestracji, ale bezpieczne. **WZORCOWE.** - **Webhooks:** HMAC-SHA256 signing (X-Slicehub-Signature header). **WZORCOWE.** - **CredentialVault:** libsodium XChaCha20-Poly1305 dla API keys/secrets. **WZORCOWE.** - **Brak:** Apple Pay/Google Pay (flaga jest, implementacji brak — P1). --- ## 5. AKTUALIZACJA MATRYCY DOPASOWANIA (vs pierwotny raport) Pierwotny raport (vixhub_repo_first_audit_report.md §2) matryca dopasowania — **AKTUALIZACJA pozycji #8 i #9**: | # | Moduł Vixplan | Pierwotna ocena | **ZAKTUALIZOWANA ocena (po re-audit)** | |---|---------------|-----------------|----------------------------------------| | **8** | **Sklep Własny D2C (WordPress/WooCommerce)** | ZERO (0%) — "Całość od zera" | **ŚREDNIA (45%)** — SliceHub ma **kompletny natywny storefront** (PWA, checkout, tracker, koszyk, gateway, idempotency, rate-limit, webhooki). BRAK tylko: WordPress/WooCommerce (Vixplan wymaga WP), PayPo, Ratami, moduł próbek. **Decyzja:** (A) Użyć natywnego storefrontu SliceHub (szybkie, ale drift od Vixplan §VIII), lub (B) Wdrożyć WooCommerce + spiąć z backendem SliceHub przez Gateway v2 (zgodne z Vixplan, więcej pracy). | | **9** | **Catalog & Print PDF Engine (A4 High-Res)** | NISKA (15%) | **NISKA (15%)** — bez zmian. `api/marketing/deck_engine.php` to deck marketingowy, nie PDF A4. `archiver` w kebabkiller to ZIP. PDF A4 nadal od zera. | **Pozostałe pozycje matrycy (1-7, 10-12) — bez zmian**, re-audit nie odkrył nowych braków w tych modułach. --- ## 6. ZAŁĄCZNIKI ### 6.1 Pełna lista plików omnichannel (checklist importu) ``` [ ] slicehub_pro/api/online/engine.php (122 KB — storefront API, 12 akcji) [ ] slicehub_pro/api/online/sse.php (5.7 KB — SSE realtime) [ ] slicehub_pro/api/cart/CartEngine.php (47 KB — server-authoritative cart) [ ] slicehub_pro/api/cart/calculate.php (2.5 KB — cart preview endpoint) [ ] slicehub_pro/api/gateway/intake.php (28 KB — unified gateway v2) [ ] slicehub_pro/api/orders/audit.php (21 KB) [ ] slicehub_pro/api/orders/edit.php (49 KB — edycja, revert, reopen) [ ] slicehub_pro/api/orders/estimate.php (5.4 KB — PromisedTimeEngine) [ ] slicehub_pro/api/orders/get.php (6.8 KB) [ ] slicehub_pro/api/orders/get_for_edit.php (4.9 KB) [ ] slicehub_pro/api/orders/history.php (14 KB) [ ] slicehub_pro/api/orders/reopen.php (4.2 KB) [ ] slicehub_pro/api/orders/revert.php (14 KB) [ ] slicehub_pro/api/orders/sla_monitor.php (7.3 KB) [ ] slicehub_pro/api/orders/DeltaEngine.php (6.5 KB — kitchen_delta diff) [ ] slicehub_pro/core/GatewayAuth.php (API key auth + rate limit + idempotency) [ ] slicehub_pro/core/OrderEventPublisher.php (transactional outbox, 19 eventów) [ ] slicehub_pro/core/OrderStateMachine.php (3-pillar state machine) [ ] slicehub_pro/core/SettlementEngine.php (split-tender settlement) [ ] slicehub_pro/core/PromisedTimeEngine.php (dynamiczny czas realizacji) [ ] slicehub_pro/core/SlaThresholds.php (SLA progi) [ ] slicehub_pro/core/SequenceEngine.php (atomic order numbering) [ ] slicehub_pro/core/WebhookDispatcher.php (HMAC-SHA256 webhooks) [ ] slicehub_pro/core/Integrations/BaseAdapter.php (abstract adapter) [ ] slicehub_pro/core/Integrations/AdapterRegistry.php [ ] slicehub_pro/core/Integrations/IntegrationDispatcher.php [ ] slicehub/api/payments/settle.php (6.9 KB — osobny endpoint płatności, BRAK w slicehub_pro) [ ] slicehub_pro/modules/online/index.html (storefront PWA entry) [ ] slicehub_pro/modules/online/manifest.webmanifest [ ] slicehub_pro/modules/online/sw.js (service worker) [ ] slicehub_pro/modules/online/track.html (tracker page) [ ] slicehub_pro/modules/online/css/style.css (94 KB — mobile-first) [ ] slicehub_pro/modules/online/css/doorway.css [ ] slicehub_pro/modules/online/css/living-scene.css [ ] slicehub_pro/modules/online/css/track.css [ ] slicehub_pro/modules/online/js/online_api.js (API wrapper) [ ] slicehub_pro/modules/online/js/online_app.js (22 KB — storefront logic) [ ] slicehub_pro/modules/online/js/online_checkout.js (41 KB — 3-step checkout) [ ] slicehub_pro/modules/online/js/online_track.js (25 KB — tracker z mapą) [ ] slicehub_pro/modules/online/js/online_doorway.js [ ] slicehub_pro/modules/online/js/online_renderer.js [ ] slicehub_pro/modules/online/js/online_table.js [ ] slicehub_pro/modules/online/js/online_ui.js (41 KB — cart drawer, FAB) [ ] slicehub_pro/modules/online/js/surface/ModifierOrchestrator.js [ ] slicehub_pro/database/migrations/001_init_slicehub_pro_v2.sql (sh_orders, sh_order_lines, sh_delivery_zones, sh_price_tiers, sh_promo_codes, sh_product_mapping) [ ] slicehub_pro/database/migrations/008_delivery_ecosystem.sql (sh_driver_locations) [ ] slicehub_pro/database/migrations/009_delivery_state_machine.sql (3-pillar state machine) [ ] slicehub_pro/database/migrations/017_online_module_extensions.sql (sh_checkout_locks, tracking_token, online settings) [ ] slicehub_pro/database/migrations/026_event_system.sql (sh_event_outbox, sh_webhook_endpoints) [ ] slicehub_pro/database/migrations/027_gateway_v2.sql (sh_gateway_api_keys, sh_rate_limits, sh_external_order_refs) [ ] slicehub_pro/database/migrations/047_order_geocoding.sql [ ] slicehub_pro/database/migrations/054_order_line_combo_meta.sql [ ] slicehub_pro/database/migrations/068_order_metadata_notes.sql [ ] slicehub_pro/database/migrations/069_order_is_corrected.sql [ ] slicehub_pro/scripts/worker_webhooks.php (drain outbox → webhooks) [ ] slicehub_pro/scripts/worker_integrations.php (drain outbox → adapters) [ ] slicehub_pro/scripts/worker_notifications.php (drain outbox → notifications) ``` ### 6.2 Decyzja architektoniczna: Natywny Storefront vs WooCommerce **Vixplan §VIII wymaga WooCommerce.** SliceHub ma natywny storefront PWA. Dwie ścieżki: **Ścieżka A (zgodna z Vixplan):** Wdrożyć WordPress + WooCommerce jako frontend sklepu D2C. Backend = zaadaptowany silnik SliceHub (Gateway v2, CartEngine, OrderEventPublisher, KSeF). WooCommerce → POST /api/gateway/intake.php (source=`woocommerce`). Plus: zgodność z Vixplan, gotowe pluginy WooCommerce (PayPo, Ratami, InPost paczkomaty). Minus: dwa stacki (PHP SliceHub + WordPress), utrzymanie theme meblowego. **Ścieżka B (pragmatyczna, drift od Vixplan):** Użyć natywnego storefrontu SliceHub (modules/online PWA) jako sklep D2C. Plus: jeden stack, gotowe PWA z checkout/tracker, mniejszy koszt. Minus: drift od Vixplan §VIII, trzeba samemu dopisać PayPo/Ratami/InPost integracje (brak gotowych pluginów). **Rekomendacja:** Ścieżka A dla zgodności z Vixplan (WooCommerce + pluginy), ale Ścieżka B jako MVP szybki start (natywny storefront + ręczne integracje). Decyzja wymaga Approval Gate z Architecture Board. --- **RE-AUDIT ZAKOŃCZONY.** Audytor potwierdza wykonanie głębokiego prześwietlenia silosu Omnichannel/E-commerce w repozytoriach SliceHub. Poprzednia luka krytyczna została zaadresowana — znaleziono kompletny produkcyjny silnik storefront + gateway + cart + checkout + tracker + webhooki + idempotency + rate-limiting. Matryca reużycia zaktualizowana (pozycja #8: 0%→45%). Gap analysis dla sklepu dostarczona (PayPo, Ratami, WooCommerce, moduł próbek, InPost paczkomaty — do napisania). Następny krok: Approval Gate — decyzja natywny storefront vs WooCommerce.