E-commerce

Tracking livraison côté boutique : statuts de commande, e-mail et notifications WhatsApp

15 دقائق للقراءة

Un client qui a payé sa commande Mobile Money à 21h32 veut savoir, le lendemain matin, où en est son colis. Il ne veut pas appeler le service client, il veut un statut clair sur son téléphone et idéalement un message WhatsApp automatique quand le coursier sera devant son portail. Ce niveau de service est devenu la norme à Dakar, Abidjan, Cotonou et Lomé, porté par les habitudes prises avec Glovo, Yango et Jumia Food. Une boutique e-commerce qui ne fournit pas un statut de commande visible et un tracking en temps réel perd des ventes répétées.

Ce tutoriel se concentre sur la couche tracking côté boutique : comment relier les statuts de commande à la réalité opérationnelle du coursier, comment notifier le client à chaque transition, comment exposer une page publique de suivi et comment mesurer la qualité de service. Le tutoriel est transposable à WooCommerce, Shopify et Medusa avec les ajustements de syntaxe propres à chaque plateforme. Pour le volet stack logistique globale (choix du transporteur, optimisation des tournées, application coursier), reportez-vous au guide complémentaire Stack logistique livraison pour PME.

Pré-requis

  • Une boutique en production avec WooCommerce 9.x, Shopify Basic ou Medusa 2.x
  • Au moins un coursier identifié (interne, partenaire externe Yango Delivery / Glovo Business / Eden Logistique / Tinda, ou flotte propre)
  • Un compte WhatsApp Business avec accès à l’API Cloud (Meta) ou via un agrégateur (Vonage, Twilio, 360dialog)
  • Un service SMTP transactionnel pour les e-mails (Brevo, Postmark, Mailgun, Amazon SES)
  • Une compréhension de base des webhooks signés et des cron jobs WordPress

Les six états canoniques d’une commande à livrer

Avant de toucher au code, posez un modèle d’états clair. Une commande physique parcourt typiquement six états distincts entre le clic « Payer » et la signature du bon de livraison. Chacun déclenche une action côté boutique et une notification côté client.

État Sens opérationnel Notification client
1 — Payée Paiement confirmé, commande à préparer E-mail récapitulatif
2 — En préparation Commande prise par l’équipe d’expédition E-mail « Votre colis se prépare »
3 — Prête à expédier Colis emballé, en attente de coursier Optionnelle (envoyer si délai > 1h)
4 — Confiée au coursier Coursier a récupéré le colis SMS ou WhatsApp avec nom coursier + numéro
5 — En cours de livraison Coursier en route vers le client WhatsApp avec ETA et lien tracking
6 — Livrée Colis remis au client, signature reçue E-mail de confirmation + demande d’avis

Deux états parallèles complètent ce modèle : Échec de livraison (client absent, refus, mauvaise adresse) et Retour (colis renvoyé à l’expéditeur). Le tutoriel Gérer les retours et réclamations efficacement couvre ces cas en détail.

Étape 1 — Modéliser les statuts dans WooCommerce

WooCommerce fournit nativement sept statuts (pending, processing, on-hold, completed, cancelled, refunded, failed) qui ne couvrent pas le détail logistique. Vous devez enregistrer des statuts personnalisés via le hook init et le filtre wc_order_statuses. Placez le code suivant dans un mini-plugin itsc-tracking pour ne pas polluer le thème.

add_action('init', function () {
    register_post_status('wc-prep', array(
        'label'                     => 'En préparation',
        'public'                    => true,
        'show_in_admin_status_list' => true,
        'label_count'               => _n_noop('En préparation (%s)',
                                              'En préparation (%s)'),
    ));
    register_post_status('wc-ready', array('label' => 'Prête à expédier', /* … */ 'public' => true));
    register_post_status('wc-handed', array('label' => 'Confiée au coursier', /* … */ 'public' => true));
    register_post_status('wc-onroute', array('label' => 'En cours de livraison', /* … */ 'public' => true));
});

add_filter('wc_order_statuses', function ($statuses) {
    $new = array();
    foreach ($statuses as $key => $label) {
        $new[$key] = $label;
        if ($key === 'wc-processing') {
            $new['wc-prep']    = 'En préparation';
            $new['wc-ready']   = 'Prête à expédier';
            $new['wc-handed']  = 'Confiée au coursier';
            $new['wc-onroute'] = 'En cours de livraison';
        }
    }
    return $new;
});

WooCommerce préfixe chaque statut par wc- en interne mais l’affiche sans préfixe dans l’admin. Une fois ce code chargé, les nouveaux statuts apparaissent dans le sélecteur de l’onglet « Commandes » et dans la page d’édition. Le test concluant : ouvrir une commande, descendre sur la métabox « Statut » et y voir vos quatre lignes ajoutées dans le bon ordre.

Étape 2 — Modéliser les statuts dans Shopify

Shopify n’autorise pas la création de statuts de commande personnalisés au sens strict. Le moteur propose Fulfillment status (unfulfilled, partial, fulfilled, restocked) et Order status (open, archived, cancelled). Pour suivre les états logistiques intermédiaires, deux techniques cohabitent :

  • Tags de commande. Vous ajoutez les tags prep, ready, handed, onroute via l’API ou l’admin, et vous filtrez les commandes par tag dans le back-office.
  • Metafields. Vous créez un metafield logistics.state avec une valeur enum. C’est plus propre, plus structuré, exposable au storefront via Liquid.

L’API Shopify Admin GraphQL expose la mutation orderUpdate pour modifier les tags et metafields. Voici un exemple côté Node.js avec la lib officielle Shopify.

// Mise à jour du metafield logistics.state via Admin GraphQL
const mutation = `
  mutation orderUpdate($input: OrderInput!) {
    orderUpdate(input: $input) { order { id } userErrors { field message } }
  }
`;
await admin.graphql(mutation, {
  variables: {
    input: {
      id: "gid://shopify/Order/1234567890",
      metafields: [{
        namespace: "logistics",
        key: "state",
        value: "handed",
        type: "single_line_text_field"
      }],
      tags: ["handed"]
    }
  }
});

La table de correspondance entre vos six états canoniques et la combinaison Shopify (fulfillment status + tag/metafield) doit être documentée au démarrage du projet : l’équipe support doit lire dans Shopify Admin sans ambiguïté l’état réel de chaque commande. Le test concluant : un changement de metafield apparaît dans le filtre « Search » du dashboard Shopify (tag:handed).

Étape 3 — Modéliser les statuts dans Medusa

Medusa v2 expose le module Fulfillment qui gère nativement la création de fulfillments et leur progression. Pour des états personnalisés, deux options : étendre le statut via un metadata field sur la commande, ou créer un module d’extension qui ajoute un état logistique séparé. La voie metadata est suffisante pour 80% des cas.

// API endpoint custom POST /admin/orders/:id/logistics-state
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
import { Modules } from "@medusajs/framework/utils";

export async function POST(req: MedusaRequest, res: MedusaResponse) {
  const orderService = req.scope.resolve(Modules.ORDER);
  const orderId = req.params.id;
  const { state } = req.body as { state: "prep" | "ready" | "handed" | "onroute" | "delivered" };

  await orderService.updateOrders([{
    id: orderId,
    metadata: { logistics_state: state, logistics_state_at: new Date().toISOString() }
  }]);

  res.json({ ok: true, state });
}

Côté admin, vous ajoutez un widget Medusa Admin (système de widgets v2) qui affiche le state et expose les boutons de transition. Côté storefront, vous interrogez la metadata pour afficher l’état au client. Validation : appeler l’endpoint depuis Postman avec le token admin et vérifier la mise à jour côté base PostgreSQL avec SELECT metadata FROM "order" WHERE id = ?.

Étape 4 — Brancher un coursier ou un partenaire externe

La transition de l’état « Prête à expédier » à « Confiée au coursier » correspond à un événement opérationnel concret : le coursier prend physiquement le colis. Trois mécanismes structurent la liaison entre la boutique et le coursier.

  • Application coursier propre. Le coursier dispose d’une PWA ou d’une app native qui liste ses tournées du jour et lui permet de cocher « pris en charge », « livré », « échec ». Le tutoriel App coursier PWA avec mode offline détaille la construction de cette app en SvelteKit.
  • API partenaire. Yango Delivery, Glovo Business, Eden Logistique, Tinda exposent des APIs B2B qui permettent de pousser une commande comme demande de livraison. Vous appelez leur endpoint POST /v1/deliveries avec adresse, contact, montant à encaisser, puis vous écoutez leur webhook pour les transitions.
  • Webhook entrant manuel. Pour un partenaire qui n’a pas d’API, exposez un endpoint POST /api/courier/update protégé par un secret partagé. Le partenaire pousse des updates via un Google Sheet, une intégration n8n, ou un simple curl programmé.

Côté WooCommerce, le hook qui pilote la transition s’appelle $order->update_status('handed', 'Confié au coursier X'). Côté Shopify, c’est la mutation GraphQL orderUpdate vue à l’étape 2. Côté Medusa, c’est l’endpoint custom de l’étape 3.

Étape 5 — Notifications client par e-mail

Chaque transition d’état doit déclencher un e-mail bien rédigé. La règle d’or : un e-mail court avec le statut, l’horodatage, le numéro de commande et un bouton vers la page publique de suivi. Pas de discours marketing dans ces transactionnels, ils doivent atterrir en boîte de réception primaire.

Sur WooCommerce, le hook woocommerce_order_status_changed permet d’intercepter chaque transition. Vous enregistrez des templates dans wp-content/themes/votre-theme/woocommerce/emails/ et vous appelez WC_Emails à chaque besoin.

add_action('woocommerce_order_status_changed', function ($order_id, $from, $to, $order) {
    $templates = array(
        'prep'    => 'emails/prep.php',
        'ready'   => 'emails/ready.php',
        'handed'  => 'emails/handed.php',
        'onroute' => 'emails/onroute.php',
    );
    if (!isset($templates[$to])) return;

    $mailer = WC()->mailer();
    $subject = 'Votre commande #' . $order->get_order_number() . ' : ' . $to;
    $body = wc_get_template_html($templates[$to], array('order' => $order));
    $mailer->send($order->get_billing_email(), $subject, $body, $mailer->get_headers(), array());
}, 10, 4);

L’envoi via un SMTP transactionnel (Brevo, Postmark, Mailgun) est indispensable : un envoi via le mail() PHP du VPS atterrit en spam dans 90% des cas. Configurez SPF, DKIM et DMARC sur votre domaine. Le tutoriel Plausible Analytics partage les outils d’observabilité utiles pour mesurer le taux de délivrabilité côté front.

Étape 6 — Notification WhatsApp

WhatsApp Business est le canal de notification le plus efficace à Dakar, Abidjan ou Bamako : taux d’ouverture supérieur à 95%, lecture immédiate, possibilité de répondre. L’API officielle est WhatsApp Cloud API, exposée par Meta. Vous créez une application Meta, vous y attachez un numéro WhatsApp dédié à votre boutique, vous validez vos templates de messages auprès de Meta, puis vous envoyez des messages programmatiques.

Voici un exemple Node.js minimal pour envoyer une notification de transition onroute avec une variable dynamique. Cet exemple suppose que le template logistics_onroute a déjà été approuvé par Meta.

// notify-whatsapp.js
async function notifyOnRoute(orderNumber, customerPhone, courierName, eta) {
  const res = await fetch(`https://graph.facebook.com/v23.0/${process.env.PHONE_NUMBER_ID}/messages`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.WHATSAPP_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      messaging_product: 'whatsapp',
      to: customerPhone, // format E.164 ex +221771234567
      type: 'template',
      template: {
        name: 'logistics_onroute',
        language: { code: 'fr' },
        components: [{
          type: 'body',
          parameters: [
            { type: 'text', text: orderNumber },
            { type: 'text', text: courierName },
            { type: 'text', text: eta },
          ],
        }],
      },
    }),
  });
  return res.json();
}

Le template Meta approuvé doit ressembler à : « Bonjour, votre commande #{{1}} est en route. Notre coursier {{2}} arrive vers {{3}}. Tenez-vous prêt. » Pour explorer plus loin, le tutoriel Chatbot WhatsApp piloté par Claude pour PME détaille le passage de simples notifications à un chatbot conversationnel complet pour le support après-vente.

Étape 7 — Page publique de suivi

Le client doit pouvoir consulter l’état de sa commande sans se reconnecter, à partir d’un lien partagé dans l’e-mail ou le WhatsApp. La convention propre : une URL du type https://votre-domaine/suivi/{order_id}/{order_key}order_key est un secret aléatoire généré à la création (déjà disponible sur WooCommerce via get_order_key(), équivalent côté Shopify et Medusa).

// WooCommerce — endpoint custom dans un thème enfant
add_action('init', function () {
    add_rewrite_rule('^suivi/([0-9]+)/([a-z0-9_]+)/?$',
                     'index.php?suivi_order=$matches[1]&suivi_key=$matches[2]', 'top');
});

// Sans cet enregistrement, get_query_var() retourne toujours une chaine vide
add_filter('query_vars', function ($vars) {
    $vars[] = 'suivi_order';
    $vars[] = 'suivi_key';
    return $vars;
});

add_action('template_redirect', function () {
    $order_id  = absint(get_query_var('suivi_order'));
    $order_key = sanitize_text_field(get_query_var('suivi_key'));
    if (!$order_id) return;

    $order = wc_get_order($order_id);
    if (!$order || $order->get_order_key() !== $order_key) {
        status_header(404); exit;
    }

    include get_stylesheet_directory() . '/templates/suivi.php';
    exit;
});

Sans l’enregistrement du query_vars, get_query_var() renvoie systématiquement une chaîne vide et votre template_redirect ne se déclenche jamais. Dernier point critique : pour que WordPress prenne en compte la nouvelle règle de réécriture, vous devez vider le cache de réécriture une fois. Le plus simple : visiter Réglages > Permaliens > Enregistrer les modifications dans l’admin. Le plus propre : appeler flush_rewrite_rules() dans un register_activation_hook du mini-plugin, en gardant à l’esprit que cette fonction est coûteuse et ne doit jamais tourner à chaque chargement de page.

Le template suivi.php affiche une timeline visuelle avec les six étapes, met en valeur l’étape courante et expose les horodatages des transitions passées. C’est aussi l’endroit logique pour intégrer un mini-formulaire « signaler un problème » qui crée un ticket dans votre back-office, et pour afficher le numéro WhatsApp du coursier quand l’état est handed ou onroute.

Étape 8 — KPIs et tableau de bord opérationnel

Une boutique e-commerce sérieuse mesure sa qualité de service de livraison. Quatre indicateurs sont à suivre quotidiennement.

KPI Définition Cible raisonnable
Délai préparation Temps entre Payée et Prête à expédier < 4 h ouvrées
Délai livraison J0 % de commandes livrées le jour même > 60% sur Dakar, Abidjan, Cotonou
Taux d’échec de livraison Commandes non livrées au premier passage < 15%
Satisfaction client post-livraison Note moyenne sur le mail après livraison > 4,5 / 5

Pour ces métriques, il faut horodater chaque transition d’état. Sur WooCommerce, ajoutez une order note à chaque woocommerce_order_status_changed et exploitez la table wp_comments filtrée sur comment_type = 'order_note' en SQL pour calculer les écarts. Sur Medusa, exploitez la colonne logistics_state_at ajoutée à l’étape 3. Le tutoriel Configurer goals et funnels e-commerce dans Plausible Analytics partage la philosophie d’instrumentation côté front (les KPIs commerciaux), à coupler avec ces KPIs opérationnels côté back.

Erreurs fréquentes

Erreur Conséquence Bonne pratique
Statuts personnalisés ajoutés sans tester les hooks WooCommerce existants Email transactionnel processing envoyé en plus de votre prep Désactiver les e-mails natifs concernés, ou rediriger via filtre woocommerce_email_recipient_*
Notification WhatsApp envoyée sans template approuvé Meta Message bloqué, voire suspension du numéro Soumettre chaque template à validation, varier les utility messages vs marketing
Page publique de suivi sans order_key Numéro de commande énumérable, fuite de PII Toujours exiger un secret par commande, jamais l’ID seul
Webhook coursier non signé Possibilité d’injection de transitions frauduleuses Signer chaque webhook avec HMAC SHA-256 et secret partagé
Cron WordPress désactivé sans alternative Transitions automatiques bloquées Désactiver wp-cron interne et configurer un vrai cron système toutes les 5 min
E-mails transactionnels via mail() PHP Délivrabilité catastrophique SMTP transactionnel (Brevo, Postmark) avec SPF, DKIM, DMARC alignés

FAQ

Faut-il vraiment ajouter des statuts personnalisés ou peut-on rester sur les statuts natifs ? Sur une boutique à moins de 30 commandes par mois, les statuts natifs suffisent (processing, completed). Au-delà, le détail logistique évite des dizaines d’appels client par semaine.

Quelle solution WhatsApp pour démarrer ? WhatsApp Cloud API directement chez Meta a basculé en juillet 2025 d’une tarification par conversation à une tarification par message. Les messages de catégorie service envoyés dans la fenêtre de 24 heures qui suit un message client restent gratuits, et les utility messages envoyés en réponse au client le sont aussi. Les messages utility et marketing initiés par votre boutique (notifications de transition de statut, relances) sont facturés au message avec des paliers dégressifs au volume. Comptez quelques centimes d’euro par utility message envoyé dans la plupart des pays africains, et au-delà de 1 centime d’euro pour les marketing messages. Les agrégateurs (Vonage, Twilio, 360dialog) ajoutent une couche tarifaire en échange d’un confort opérationnel.

Et le SMS ? Le SMS reste pertinent pour les clients qui ne sont pas sur WhatsApp ou pour les notifications critiques. Comptez 25 à 35 FCFA par SMS chez Orange Senegal Business ou Texteo. Préférez WhatsApp en canal principal et SMS en fallback.

Doit-on utiliser un plugin existant comme Advanced Shipment Tracking ? Pour WooCommerce, ce plugin est solide mais centré sur les transporteurs internationaux (DHL, UPS, FedEx). Pour des coursiers locaux, votre code custom donne plus de précision.

Comment gérer les paiements à la livraison ? Vous ajoutez un statut cash-pending avant delivered et vous demandez au coursier d’encaisser via mobile money à la remise. Le tutoriel Paiement Wave à la livraison détaille ce flux spécifique.

Lectures complémentaires

Trois tutoriels prolongent celui-ci sur les axes d’une boutique en ligne complète : choix de plateforme, paiement en zone CFA, suivi opérationnel et acquisition organique.

Ressources et références

À combiner avec : brancher Orange Money sur WooCommerce ou Shopify pour offrir un parcours d’achat sans friction.

Mise en production : avant le go-live, parcours go-live Wave Business 2026.

Service ITSkillsCenter

Site ou application web sur mesure

Conception Pro + Nom de domaine 1 an + Hébergement 1 an + Formation + Support 6 mois. Accès et code livrés. À partir de 350 000 FCFA.

Demander un devis
Publicité