Module Web-Orders
:::info Module UI — logique sur SRV-APPS
Le module Laravel WebOrders de l'intranet est une interface utilisateur : les webhooks WooCommerce et le transfert SCP sont gérés par la Laravel API de SRV-APPS (192.168.1.17:8002), pas par l'intranet.
→ Architecture de SRV-APPS : Vue d'ensemble SRV-APPS
→ Référence API : API SRV-APPS
:::
Rôle
Recevoir les commandes passées en ligne via les boutiques WooCommerce, les parser, les stocker en base, et les transférer vers le NAS ERP pour traitement par le système 4D.
Ce module est distinct de l'EDI Engine : il traite les commandes B2C et B2B en ligne (format XML WooCommerce), là où l'EDI Engine traite les commandes B2B fournisseurs (PDF, XLS, EDI).
Architecture
WooCommerce (boutique)
│
│ POST XML — User-Agent: WooCommerce/WordPress
▼
Laravel API (/opt/medithau/api/)
POST /api/web-orders/webhook/{source}
│
├─ Validation User-Agent
├─ Validation XML bien formé
├─ Vérification token (dans le corps XML)
├─ WebOrder::create (status: received)
└─ dispatch TransferWebOrderJob
│
▼
Queue "web-orders" (Laravel Horizon)
│
▼
TransferWebOrderJob::handle()
├─ WebOrderParser::parse(xml) → parsed_data
├─ update (status: transferring)
├─ Écriture XML → /opt/medithau/web-orders/incoming/{source}/order_*.xml
├─ exec python transfer_web_order.py --source {source} --file {file}
│ └─ SCP → NAS ERP /volume1/DATAS/4DDocuments/WebOrders/{FTP|OFTP}/
└─ update (status: transferred | failed)
Deux sources de boutiques
| Source | Boutique | Dossier NAS destination |
|---|---|---|
main | Boutique principale | /volume1/DATAS/4DDocuments/WebOrders/FTP/ |
ostrealia | Boutique Ostrealia | /volume1/DATAS/4DDocuments/WebOrders/OFTP/ |
Modèle — table web_orders
| Colonne | Type | Description |
|---|---|---|
id | UUID (PK) | Identifiant unique |
source | enum(main, ostrealia) | Boutique d'origine |
filename | string(100) | Nom du fichier XML (order_{Ymd_His}_{hash8}.xml) |
xml_content | longText | Corps XML brut reçu du webhook |
parsed_data | JSON | Données parsées par WebOrderParser (voir ci-dessous) |
status | enum | Cycle de vie (voir ci-dessous) |
error | string nullable | Message d'erreur (max 500 chars) |
received_at | timestamp | Date/heure de réception |
transferred_at | timestamp nullable | Date/heure de transfert réussi |
Pas de created_at / updated_at Laravel — $timestamps = false.
Cycle de statut
[*] → received
↓ (job démarré)
transferring
↓ SCP OK ↓ SCP échoué (3 tentatives)
transferred failed
↓ (POST /retry)
received (rejeu)
Services
WebOrderParser
App\Services\WebOrders\WebOrderParser::parse(string $xml): ?array
Parse le XML WooCommerce et retourne un tableau structuré ou null si le XML est invalide.
Structure de parsed_data :
parsed_data
├── order_number (NumCommandeweb)
├── order_date (datecommande)
├── delivery_date (Datedelivraison)
├── delivery_type (TypedeLivraison)
├── shipping_method (Shipping_Method_Title)
├── payment_method (ModeReglement)
├── client_role (RoleClient)
├── note (Commandenote)
├── billing {}
│ ├── last_name / first_name / company
│ ├── email / phone
│ └── address / zip / city / country
├── shipping {}
│ ├── last_name / first_name / company
│ ├── phone
│ └── address / zip / city / country
├── lines[]
│ ├── reference (ref ERP — Reference)
│ ├── name (NomProduit)
│ ├── qty (Nbre)
│ ├── unit_price (PrixUnitaire)
│ └── line_total (qty × unit_price, recalculé)
├── totals {}
│ ├── shipping (Order_Shipping)
│ ├── refund (Order_Refund)
│ └── total (Order_Total)
└── coupons[] (codes promo)
TransferWebOrderJob
App\Jobs\WebOrders\TransferWebOrderJob
- Queue :
web-orders - Tentatives : 3 (
$tries = 3) - Timeout : 60 secondes
- En cas d'échec sur les 3 tentatives :
failed()remet le statut àfailed
Le job appelle un script Python externe pour le transfert SCP :
{EDI_ENGINE_VENV_PYTHON} /opt/medithau/agents/edi-engine/tools/transfer_web_order.py \
--source {source} --file {xmlPath}
Routes
Déclarées dans /opt/medithau/api/routes/api.php.
| Méthode | Route | Auth | Description |
|---|---|---|---|
| POST | /api/web-orders/webhook/main | Signature webhook | Réception commande boutique principale |
| POST | /api/web-orders/webhook/ostrealia | Signature webhook | Réception commande Ostrealia |
| POST | /api/web-orders/debug | aucune | Log les headers/body (temporaire, diagnostic) |
| GET | /api/web-orders | Bearer token | Liste des commandes (100 dernières, filtrables) |
| GET | /api/web-orders/{id} | Bearer token | Détail complet (sans xml_content) |
| POST | /api/web-orders/{id}/retry | Bearer token | Relancer une commande failed |
Filtres disponibles sur GET /api/web-orders
?source=main|ostrealia?status=received|transferring|transferred|failed
La réponse liste retourne directement order_number, client, company, total, delivery_date, lines_count depuis parsed_data — sans re-parser le XML.
Commandes Artisan
web-orders:parse-existing
php artisan web-orders:parse-existing
php artisan web-orders:parse-existing --force # re-parser même les commandes déjà parsées
Backfill de parsed_data pour les commandes existantes qui ont xml_content mais pas encore parsed_data. Traitement par chunks de 50. Utile après une migration ou une modification du parser.
Configuration
Fichier : /opt/medithau/api/config/web_orders.php
| Clé | Variable .env | Valeur par défaut |
|---|---|---|
sources.main.remote_path | — | /volume1/DATAS/4DDocuments/WebOrders/FTP/ |
sources.ostrealia.remote_path | — | /volume1/DATAS/4DDocuments/WebOrders/OFTP/ |
ssh.host | WEB_ORDERS_SSH_HOST | 192.168.1.13 |
ssh.port | WEB_ORDERS_SSH_PORT | 34322 |
ssh.user | WEB_ORDERS_SSH_USER | edi |
ssh.key | WEB_ORDERS_SSH_KEY | /opt/medithau/config/ssh/id_rsa_weborders |
incoming_dir | WEB_ORDERS_INCOMING_DIR | /opt/medithau/web-orders/incoming |
archive_dir | WEB_ORDERS_ARCHIVE_DIR | /opt/medithau/web-orders/archive |
webhook_token | WEB_ORDERS_WEBHOOK_TOKEN | (vide — désactive la vérification) |
python | EDI_ENGINE_VENV_PYTHON | /opt/medithau/agents/edi-engine/.venv/bin/python |
Structure des fichiers
/opt/medithau/web-orders/
├── incoming/
│ ├── main/ ← XML en transit (avant SCP)
│ └── ostrealia/
└── archive/
├── main/ ← XML archivés après transfert réussi
└── ostrealia/
Sécurité
- User-Agent : le webhook vérifie que l'UA contient
woocommerceouwordpress(insensible à la casse). Toute autre source reçoit un HTTP 403. - Token webhook : si
WEB_ORDERS_WEBHOOK_TOKENest défini, il est comparé à$parsed->Order->tokenviahash_equals()(résistant aux timing attacks). Si la variable n'est pas définie, la vérification est désactivée. - Validation XML :
simplexml_load_string()aveclibxml_use_internal_errors(true). Un XML mal formé retourne HTTP 400. - Auth API : les routes de lecture/retry nécessitent le Bearer token
EDI_ENGINE_API_TOKEN(middlewareEdiEngineToken). - Clé SSH dédiée : le transfert SCP web-orders utilise
id_rsa_weborders, distincte de la clé EDI Engine, pour limiter les permissions en cas de compromission.
Cas particuliers
- Retry : seules les commandes en statut
failedpeuvent être relancées. Tenter un retry sur une commandetransferredoutransferringretourne HTTP 409. - Payload vide : retourne HTTP 400 avant tout traitement.
- Webhook debug :
POST /api/web-orders/debuglog les headers, query params et les 500 premiers caractères du body dans/opt/medithau/logs/web_orders_debug.log. Route temporaire à supprimer après identification du format plugin WooCommerce. - Connexion NAS ERP : utilise les mêmes variables SSH que l'EDI Engine (
ERP_SSH_IP,ERP_SSH_PORT) en fallback siWEB_ORDERS_SSH_HOST/PORTne sont pas définis.