Table des matières :
- Architecture d’intégration ERP e‑commerce : synchroniser en REST, propager en évènements
- Contrat d’échange : source de vérité, idempotence, versioning des schémas
- Webhooks : ingestion sécurisée, signature, retries, et pattern Outbox
- RabbitMQ : topologies AMQP, DLQ, retries et gestion de la pression
- Implémentation PrestaShop 9 (Symfony 6.4 / PHP 8.3) : module, hooks, Messenger AMQP
- Exploitation : osservabilité, sécurité, et critères de choix (API vs webhooks vs EDI)
Architecture d’intégration ERP e‑commerce : synchroniser en REST, propager en évènements
Une intégration ERP e‑commerce robuste n’est pas un « connecteur » monolithique qui pousse/aspire des données à intervalle fixe. C’est une architecture qui sépare clairement les flux synchrones (API REST pour lire/valider à la demande) et les flux asynchrones (évènements métier transportés par webhooks et/ou bus de messages). Le but n’est pas la cohérence immédiate partout, mais une cohérence suffisante et observable, avec des garanties explicites (au minimum at‑least‑once).
Concrètement : l’e‑commerce (ex. PrestaShop 9.x, PHP 8.3) reste le système d’exécution de la vente (panier, paiement, confirmation), tandis que l’ERP reste le système d’enregistrement (facturation, stocks « comptables », approvisionnement, numérotation, logistique). Les échanges se découpent typiquement en (1) API « pull » ERP→e‑commerce pour référentiels et (2) évènements e‑commerce→ERP pour commandes, paiements, retours, et (3) évènements ERP→e‑commerce pour stocks, prix, statuts d’expédition. Cette séparation évite de transformer chaque requête front en cascade d’appels ERP, ce qui est un anti‑pattern de performance.
Le point aveugle habituel (et la cause des désynchronisations) est le couplage temporel : si vous appelez l’ERP en HTTP dans le même thread que la validation de commande, vous importez ses latences, ses maintenances et ses erreurs dans le tunnel de conversion. Sur PrestaShop, ça se traduit par des timeouts, des paniers « gelés » et des commandes partiellement persistées. Si vous avez déjà optimisé vos pages critiques, vous savez que le moindre traitement parasite sur produit/panier explose la TTFB ; cf. Optimisation PrestaShop : accélérer pages produit et panier.
Pour cadrer ce type de chantier, partez d’un schéma simple :
- API : endpoints versionnés, idempotents, limités en payload (pagination, champs) pour les lectures et validations.
- Webhooks : notifications d’évènements HTTP push (souvent vers un endpoint d’ingestion).
- RabbitMQ : transport AMQP pour découpler ingestion, traitement, retries, et mises à jour (workers).
Une règle pratique pour décider « REST vs évènement » : si la réponse doit impacter immédiatement une décision utilisateur (afficher une disponibilité, recalculer un total, vérifier une éligibilité), faites du synchrone — mais sans dépendre de l’ERP pour finaliser le checkout. À l’inverse, si l’action est postérieure à la vente (facture, réservation logistique, écriture de stock comptable, génération EDI transporteur), publiez un évènement et traitez en asynchrone.
| Besoin côté e‑commerce | Mécanisme conseillé | Pourquoi | Piège à éviter |
|---|---|---|---|
| Afficher stock « web » / délai | REST (cache + rafraîchissement) | lecture à la demande | appeler l’ERP à chaque page produit |
| Créer une commande | Évènement order.created + outbox |
pas de blocage checkout | publier avant commit SQL |
| Confirmer paiement PSP | Évènement payment.captured |
retours PSP parfois tardifs | coupler au retour HTTP du PSP |
| Mise à jour statut expédition | Évènement shipment.updated |
répétitif, volumineux | polling toutes les minutes |
Mini‑scénario très courant (France / UE) : un marchand B2C lance une promo et subit un pic de commandes. Si la validation de commande attend un aller‑retour ERP (contrôle de crédit, disponibilité, création de tiers), la charge se répercute sur le tunnel de paiement (latence + erreurs). Une architecture évènementielle vous permet de laisser la vente se conclure, puis de traiter les exceptions de façon contrôlée (commande en attente, remboursement automatique, email client), au lieu de perdre la conversion.
Contrat d’échange : source de vérité, idempotence, versioning des schémas
Avant de parler RabbitMQ, il faut décider qui « fait foi » pour chaque objet. Sans ça, vous allez empiler des règles de merge incohérentes (et des tickets interminables). Sur PrestaShop, le catalogue et les stocks peuvent être pilotés par ERP/PIM, mais les paniers, paiements et certaines données client restent côté e‑commerce. Cette problématique est détaillée dans PrestaShop intégration ERP PIM : définir la source de vérité et les flux.
Un outil simple et efficace en atelier (MOA/MOE) : une matrice « source de vérité / système maître / latence acceptable ». Elle évite les malentendus du type « le stock doit être temps réel » (souvent faux) ou « le prix est partout le même » (rarement vrai en multi‑canal).
| Objet métier | Source de vérité (SoT) la plus fréquente | Direction principale | Latence acceptable (ordre de grandeur) |
|---|---|---|---|
| Produit (SKU, attributs) | PIM/ERP | ERP→e‑commerce | minutes à heures |
| Prix / grilles / promos | ERP (ou moteur pricing) | ERP→e‑commerce | minutes |
| Stock « web sellable » | ERP/WMS selon orga | ERP/WMS→e‑commerce | secondes à minutes (selon promesse) |
| Client (B2C) | e‑commerce (avec enrichissement ERP) | e‑commerce→ERP | minutes |
| Commande | e‑commerce | e‑commerce→ERP | secondes |
| Facture (numérotation) | ERP | ERP→e‑commerce (ou portail client) | minutes |
| Expédition (tracking) | WMS/TMS | WMS→e‑commerce | minutes |
| Retour / RMA | e‑commerce ou ERP | bidirectionnel | minutes à heures |
Ensuite, formalisez un contrat d’évènement. Dans la pratique, le JSON libre « au feeling » devient ingérable dès que vous avez 2 consommateurs (ERP + BI, ERP + OMS, etc.). Le standard CloudEvents est utile comme enveloppe : « CloudEvents is a specification for describing event data in common formats to provide interoperability across services, platforms and systems. » (CloudEvents). Même sans adopter CloudEvents à 100 %, reprenez au minimum : id (UUID), type, source, time, subject, datacontenttype, data, et des champs de traçabilité (correlation_id, causation_id).
Ajoutez aussi deux éléments souvent oubliés, mais très opérables :
- un champ
schema_version(ex.1.2) pour router/valider proprement ; - un champ
tenant/id_shoppour le multi‑boutique et les environnements (prod/préprod), afin d’éviter les erreurs de routage « silencieuses ».
L’autre point non négociable est l’idempotence. En distribué, vous allez avoir des doublons (retries HTTP, requeue AMQP, redelivery). La RFC 7231 définit l’idempotence ainsi : « A request method is considered “idempotent” if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request. » (section 4.2.2). Traduction opérationnelle : chaque évènement porte une clé unique, et chaque consumer enregistre un dedup store (table SQL, Redis, etc.) pour ignorer les redéliveries.
Un pattern concret (qui évite beaucoup de « double facturation » ou « double réservation de stock ») :
- l’évènement porte
event_id(UUID) etentity_id(ex.order_id) ; - le consumer écrit dans une table
processed_events(event_id, processed_at, handler)avant d’exécuter des effets externes non idempotents (création de pièce, appel API ERP…) ou bien dans la même transaction que l’effet, selon votre modèle ; - si l’évènement revient, il est ignoré (ou traité en no-op).
Enfin, versionnez vos schémas et vos endpoints. Pas « v2 » dans un coin, mais un vrai versioning compatible : ajouter des champs = backward compatible, renommer/supprimer = breaking change. Quand vous exposez une API PrestaShop pour un ERP, évitez d’exposer directement les ressources natives (products, orders) sans couche d’abstraction : vous figez des détails internes et vous multipliez les migrations. Pour une approche plus propre côté connecteur, voir Intégration API PrestaShop : créer un connecteur ERP sur mesure.
Webhooks : ingestion sécurisée, signature, retries, et pattern Outbox
Un webhook n’est pas un bus d’évènements ; c’est un mécanisme de notification HTTP. Il est parfait pour signaler « quelque chose s’est passé », mais il n’apporte ni file d’attente, ni backpressure, ni DLQ par défaut. Si vous faites du traitement lourd dans le handler de webhook, vous recréez le couplage temporel (et vous perdez des évènements lors des pics). La stratégie standard : le webhook ne fait qu’authentifier, valider, persister, puis pousser un message dans RabbitMQ (ou un stockage d’outbox) et répondre vite (2xx).
Côté sécurité, un webhook sans signature est un endpoint public qui accepte des actions métier. Il faut au minimum : TLS, liste d’IP si possible (insuffisant seul), et surtout signature HMAC + protection anti‑rejeu (timestamp, TTL). En pratique, la protection anti‑rejeu se fait en combinant :
- un header
X-Timestamp(ou équivalent) validé avec une tolérance (ex. ±5 minutes), - un
event_idunique stocké en déduplication (si déjà vu → rejet ou no‑op), - et idéalement une signature qui inclut le timestamp (sinon, le timestamp peut être modifié sans invalider la signature).
Exemple minimal en PHP 8.3 pour vérifier une signature de type X-Signature (à adapter au fournisseur) :
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$secret = $_ENV['WEBHOOK_SECRET'];
$computed = hash_hmac('sha256', $payload, $secret);
if (!hash_equals($computed, $signature)) {
http_response_code(401);
exit;
}
Sur PrestaShop, le cœur ne fournit pas un système de webhooks génériques et transactionnels. Vous finissez donc par écrire un module qui s’accroche à des hooks (ex. actionValidateOrder, actionProductSave, etc.) et qui émet des notifications. La difficulté est la garantie transactionnelle : si vous publiez l’évènement en RabbitMQ avant la fin du commit SQL, vous pouvez envoyer un évènement pour une commande qui ne sera jamais persistée (rollback). Le pattern Transactional Outbox est la solution pragmatique : vous écrivez l’évènement dans une table outbox dans la même transaction que l’écriture métier, puis un worker publie ensuite vers RabbitMQ.
Deux détails opérationnels qui améliorent énormément la maintenabilité de l’outbox :
- stocker le payload brut (ou une référence vers un blob/objet) + les métadonnées (type, shop, occurred_at) : cela permet de rejouer un évènement à l’identique ;
- stocker un hash (SHA‑256) du payload : utile pour diagnostiquer des divergences de mapping (« même event_id mais contenu différent » = bug).
Pour comprendre comment s’insérer proprement dans l’écosystème de hooks, cf. Hooks PrestaShop : comprendre les hooks dynamiques et les regex.
Enfin, prévoyez le comportement en échec : un webhook provider (PSP, ERP, OMS) retry souvent pendant des heures avec une politique qui lui est propre. Si votre endpoint renvoie 500 à cause d’une erreur de mapping, vous subissez une tempête de retries. La règle : répondez 2xx dès que l’évènement est durablement stocké (souvent 202 Accepted est plus honnête qu’un 200 OK), et poussez les erreurs métier dans une DLQ (file d’erreurs) pour traitement humain.
RabbitMQ : topologies AMQP, DLQ, retries et gestion de la pression
RabbitMQ sert à faire ce que HTTP ne fait pas : bufferiser, router, rejouer. Leur documentation résume le rôle sans ambiguïté : « RabbitMQ is a message broker: it accepts and forwards messages. » (RabbitMQ Tutorials). Dans un contexte ERP e‑commerce, vous utilisez RabbitMQ comme bus d’intégration entre producteurs (PrestaShop, ERP, PIM, WMS) et consommateurs (workers de synchronisation, indexation, reporting, anti‑fraude…).
Topologie recommandée (simple et efficace) :
- Exchange
topic(ex.ecom.events) avec des routing keys du typeorder.created,order.paid,stock.updated. - Une queue par consumer (ex.
erp.order.created,wms.order.created) avec binding sur le pattern souhaité. - Queues durables + messages persistants si vous voulez survivre à un restart propre.
- DLX (Dead Letter Exchange) pour isoler les messages qui échouent après N tentatives.
Deux précisions importantes pour éviter des surprises en production :
- Publisher confirms : côté producteur, utilisez des confirmations (selon votre librairie) pour savoir si RabbitMQ a bien pris en compte le message. Sans ça, en cas de micro‑coupure réseau, vous pouvez perdre des évènements sans le voir.
- Taille de message : évitez de transporter des « commandes complètes » de centaines de Ko. Soit vous envoyez un évènement léger + un lien vers une ressource API stable (pattern event-carried state transfer vs event notification), soit vous transportez un sous‑ensemble strictement nécessaire au consumer.
Ne confondez pas durabilité et disponibilité. Pour une vraie tolérance aux pannes, utilisez des quorum queues (Raft) plutôt que des classic mirrored queues (dépréciées depuis longtemps). C’est le bon compromis en 2026 pour éviter la corruption silencieuse et les comportements bizarres lors de partitions réseau.
La performance et la stabilité se jouent sur des détails : prefetch (QoS) pour éviter qu’un worker monopolise 10 000 messages, ack manuel, et stratégie de retry. Un retry « immédiat » en boucle détruit votre cluster (CPU + I/O) ; un retry « retardé » est plus sain. RabbitMQ n’a pas de délai natif dans AMQP 0-9-1, mais vous pouvez implémenter des retries avec TTL + DLX, ou utiliser le plugin delayed message exchange si votre politique d’exploitation l’autorise.
Un schéma de retry lisible (et facile à expliquer aux ops) :
- queue principale
erp.order.created(consommation normale) ; - en cas d’échec technique transitoire → dead-letter vers une queue
erp.order.created.retry.5m(TTL 5 minutes) ; - puis retour automatique vers la queue principale ;
- après X tentatives → dead-letter final vers
erp.order.created.dlqavec la raison de l’échec + l’exception.
La DLQ n’est pas optionnelle : c’est votre garde‑fou contre les messages poison (payload invalide, mapping absent, données ERP incohérentes). Et si vous opérez en contexte multi‑applications (ERP + WMS + BI), une DLQ par consumer est souvent plus exploitable qu’une DLQ globale, car la responsabilité de traitement est claire.
Enfin, acceptez l’éventuelle incohérence et modélisez-la. Le message doit être auto‑suffisant (ou pointer vers une ressource API stable) et chaque consumer doit être idempotent. « Exactly-once » est un slogan marketing dans la plupart des SI ; en pratique, vous faites du at‑least‑once + déduplication + traitements compensatoires.
Implémentation PrestaShop 9 (Symfony 6.4 / PHP 8.3) : module, hooks, Messenger AMQP
Sur PrestaShop 9 (stack Symfony 6.4, PHP 8.3), deux approches coexistent : (1) module « legacy » qui s’accroche aux hooks et utilise Db/ObjectModel, et (2) intégration plus Symfony (services, container, commandes, Messenger). Dans les faits, vous mélangez souvent les deux : les hooks déclenchent la création d’un évènement, et des services Symfony le publient/consomment. Pour le contexte API et outillage dev, voir aussi PrestaShop Admin API : environnement de développement Docker et Swagger et PrestaShop 9 : commerce headless, APIs et outils développeurs Symfony.
Côté publication, si vous avez Symfony Messenger disponible, RabbitMQ s’intègre proprement via un transport AMQP. Exemple de configuration conceptuelle (à adapter à votre arborescence PrestaShop et à votre conteneur) :
# config/packages/messenger.yaml
framework:
messenger:
transports:
rabbitmq_events:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
options:
exchange:
name: ecom.events
type: topic
queues:
erp_order:
binding_keys: ['order.*']
routing:
'App\\Message\\DomainEvent': rabbitmq_events
Puis côté code : un message DomainEvent (avec eventId, type, payload, occurredAt) + un handler côté ERP consumer (dans un autre service) ou côté e‑commerce si vous consommez des mises à jour ERP. L’intérêt de Messenger est double : vous uniformisez les handlers, et vous bénéficiez de middlewares (logging, retries, doctrine transaction) si vous êtes dans un vrai projet Symfony. Pour les détails de tuning (transports, concurrence, échecs), cf. Symfony Messenger : configuration avancée, choix du transport et performances.
Mais attention : Messenger ne résout pas la transaction métier PrestaShop par magie. Si votre hook actionValidateOrder écrit la commande, vous devez maîtriser quand le commit SQL est effectif. Sans outbox, vous aurez des trous. Une implémentation pragmatique (et compatible avec le cœur) : créer une table ps_integration_outbox (InnoDB), y persister l’évènement (statut pending), puis une commande CLI (cron) publie vers RabbitMQ et passe l’outbox à sent. Ce mécanisme est trivial à auditer et à reprocesser.
Pour rendre le tout « opérable », pensez votre outbox comme une file durable avec un minimum de métadonnées :
id(auto)event_id(UUID)event_type(string)aggregate_type/aggregate_id(ex.order/12345)payload_json(JSON)id_shop,id_lang,currency_isostatus(pending|sent|failed)attempts,last_error,available_at(pour backoff)created_at,sent_at
Et côté reprocessing, prévoyez dès le début une commande du type :
integration:outbox:publish --status=pending --limit=500integration:outbox:replay --event-type=order.created --from=2026-08-01
Dernier piège : le multiboutique et les contextes. Chaque évènement doit porter id_shop, id_lang, currency, et idéalement un tenant logique. Sinon, votre ERP reçoit des commandes sans contexte et reconstruit mal taxes, prix et stocks. Si vous synchronisez des prix, vous devrez aussi décider si vous envoyez du TTC, HT, ou des grilles par groupe (et accepter que PrestaShop gère mal certains cases complexes sans développement spécifique). Sur des marchands français/UE, c’est aussi ici que se cachent les divergences TVA (règles B2B/B2C, exonérations, arrondis) : le contrat de données doit expliciter qui calcule quoi et où.
Exploitation : osservabilité, sécurité, et critères de choix (API vs webhooks vs EDI)
Si vous ne mesurez pas l’intégration, vous ne la « maintenez » pas : vous la subissez. Minimaux à instrumenter : (1) lag de queue (messages en attente), (2) taux d’erreur par type d’évènement, (3) temps de traitement par handler, (4) taille moyenne des payloads, (5) nombre de redeliveries, (6) DLQ. RabbitMQ expose des métriques exploitables ; branchez-les sur Prometheus et alertez sur des SLO réalistes (ex. « queue erp_order > 10 000 messages pendant 10 min »). Pour la mise en place d’alertes, cf. Prometheus : configuration des alertes, métriques et requêtes PromQL et, côté monitoring node/service, Netdata : activer et configurer les collectors pour monitoring temps réel.
Une pratique simple (qui évite les « on a tout perdu ») : établir une capacité de reprise explicite. Exemple :
- RPO évènementiel : « aucun évènement perdu » (outbox durable + publisher confirms).
- RTO : « reprise en moins de 30 minutes » (procédure de redémarrage workers + replay outbox).
- Dégradation contrôlée : « on peut couper la consommation ERP sans couper la vente » (les commandes s’accumulent dans RabbitMQ + outbox).
Sur la sécurité, traitez l’intégration comme une surface d’attaque à part entière : secrets (rotation), TLS partout, permissions minimales RabbitMQ (vhost dédié, user par application, pas d’accès admin), validation stricte des payloads (JSON schema), et journalisation des accès. Les endpoints API/webhooks doivent être protégés contre l’abus (rate limit, WAF si vous avez), et les modules PrestaShop doivent être livrés avec une hygiène supply chain (SBOM, CI, scans). Pour une vue d’ensemble orientée PrestaShop, voir PrestaShop : sécuriser APIs et modules avec SBOM, WAF et CI/CD.
Ajoutez une contrainte souvent sous-estimée côté UE : minimisation des données et cloisonnement. En clair : évitez de diffuser des PII inutiles (adresse complète, téléphone, etc.) à des consumers qui n’en ont pas besoin (BI, moteur de recherche…). Un évènement order.created destiné à la facturation/logistique peut contenir l’identité et l’adresse ; un évènement order.paid destiné au reporting peut se limiter à des identifiants, montants, devise, pays, et métadonnées de paiement non sensibles. Cela réduit le risque, simplifie la conformité, et rend vos messages plus stables.
Enfin, le choix « API/webhooks/RabbitMQ » n’élimine pas les autres standards : certains ERP/EDI imposent EDIFACT/AS2/SFTP/PEPPOL, notamment pour B2B, facturation, avis d’expédition, etc. Ne forcez pas RabbitMQ là où un échange EDI est contractuel et audité ; encapsulez plutôt l’EDI dans un adaptateur qui publie des évènements internes. Sur ce sujet, référez-vous à Intégration ERP : standards EDI, EDIFACT, AS2, SFTP et PEPPOL et à l’arbitrage global présenté dans Intégration ERP : choisir API, webhooks, ETL ou iPaaS.
En contexte France, la réforme de la facturation électronique (e‑invoicing / e‑reporting) influence souvent l’architecture : l’ERP reste typiquement le système maître pour la numérotation et l’archivage probant, et l’e‑commerce doit se contenter d’afficher (ou de référencer) la facture produite par le SI finance.
Pour finir sur du concret, une checklist de mise en production (sans folklore) :
- Contrats : schémas versionnés, exemples de payloads, compatibilité documentée.
- Fiabilité : outbox + déduplication + DLQ + reprocess (script/commande).
- Perf : pagination côté API, limites payload, index DB (cf. Performance API : index, pagination et pool de connexions).
- Run : dashboards, alertes, procédure de replay, et capacité à « couper » un flux sans couper la vente.
- Gouvernance : matrice SoT signée, règles d’arrondi/TVA explicites, et gestion des corrections (compensation plutôt que « patch » manuel en base).
Si vous cherchez un exemple plus applicatif autour d’Odoo (stocks, paiement, logistique) côté PrestaShop, il y a un retour d’expérience connexe ici : Abonnement PrestaShop : intégration ERP Odoo, stocks, paiements et logistique.




