Shopify

Migration Shopify : les clients – guide complet 2026

Cet article est le premier d’une série dédiée à la migration d’un site ecommerce sur Shopify. Les autres articles traiteront de la migration du catalogue et de la migration des commandes.

Que vous migriez votre boutique depuis PrestaShop, WooCommerce ou Magento, les pièges sont sensiblement les mêmes, la cible Shopify étant identique. Nous illustrons avec PrestaShop quand c’est utile pour des points techniques précis, mais l’essentiel des recommandations s’applique aux autres solutions ecommerce du marché.

L’objectif d’une migration réussie est une transition fluide, sans interruption de service perceptible côté client, sans perte de données critiques (consentements, historique, segments), et qui prépare la croissance future de l’activité sur la nouvelle plateforme.

Pourquoi la migration clients est plus délicate qu’on le pense

Migrer une base clients ne se résume pas à transférer des emails et des noms d’une base à une autre. Sur une boutique en ligne active, chaque profil client est un nœud dans un écosystème complexe : il a un historique de commandes qui doit rester cohérent côté SAV et support client, il est rattaché à des segments marketing actifs qui pilotent les campagnes, il est associé à des consentements RGPD qui ne se transfèrent pas trivialement, il a un mot de passe qu’on ne pourra jamais migrer, il reçoit potentiellement des emails automatiques qui peuvent partir au mauvais moment, et il a peut-être un programme fidélité, des avis publiés, un compte abonnement actif sur le site.

Chaque point est un piège potentiel pour le commerçant. Et la plupart de ces pièges ne sont pas rattrapables après coup. C’est ce qui distingue une migration agence professionnelle d’un simple transfert de fichier CSV : la capacité à anticiper, à vérifier, et à corriger en amont plutôt que de découvrir les problèmes une fois la nouvelle boutique en production.

Comment un client est modélisé côté Shopify

Avant de plonger dans les pièges techniques, il est utile de comprendre comment Shopify représente un client. Le modèle est sensiblement différent de celui de PrestaShop, WooCommerce ou Magento, et beaucoup de surprises en migration viennent simplement de ce mapping mal compris.

Un client Shopify, c’est avant tout un email, qui sert d’identifiant principal et de moyen de connexion (avec les New Customer Accounts en mode passwordless, l’email reçoit un code à 6 chiffres à chaque connexion). À ça s’ajoutent un nom et un prénom, un numéro de téléphone optionnel, et une ou plusieurs adresses (livraison, facturation) avec une adresse par défaut. Toutes ces informations sont visibles dans la fiche client de l’admin Shopify.

Shopify ajoute ensuite une couche d’informations marketing : un statut de consentement email (SUBSCRIBED, UNSUBSCRIBED, PENDING, NOT_SUBSCRIBED), un statut de consentement SMS équivalent, une date de dernier changement de consentement, et un niveau d’opt-in (simple ou double). C’est cette couche qui pilote tout ce qui est marketing : qui reçoit les campagnes, qui est synchronisé vers Klaviyo ou Brevo, qui apparaît dans les segments de relance.

Vient ensuite la couche commerciale, calculée automatiquement par Shopify : montant total dépensé, nombre de commandes, dernière commande, date du dernier achat. Ces données ne se settent pas à la création du client, elles sont dérivées des commandes attachées au compte au fur et à mesure.

Pour tout ce qui ne rentre pas dans ces champs natifs (groupes clients PrestaShop, source d’acquisition, statut VIP, langue préférée, ancien ID de la plateforme source, etc.), Shopify propose deux mécanismes : les tags (chaînes de caractères libres attachées au client, utilisables pour segmenter) et les metafields (champs personnalisés avec un namespace, une clé, un type et une valeur). Une bonne migration utilise les deux à bon escient : les tags pour les groupes et catégories larges, les metafields pour les identifiants techniques et les données structurées.

Enfin, le client peut avoir une langue préférée (champ locale, code ISO comme fr, en, de) qui pilote la langue des emails transactionnels et de l’interface customer account, un identifiant unique généré par Shopify, et un statut de vérification d’email. Les Classic Customer Accounts avaient aussi un mot de passe, ce qui n’est plus le cas avec les New Customer Accounts passwordless.

Pour les développeurs : la structure GraphQL du customer Shopify

Pour qui va manipuler l’API GraphQL Admin (versions 2026-04 et plus récentes), voici les points essentiels à connaître sur la structure du customer. Cette section est plus technique et peut être survolée par les e-commerçants qui ne touchent pas au code.

Le customer Shopify est exposé via la mutation customerCreate (ou customerSet pour un upsert) avec un CustomerInput en entrée. Les champs scalaires email et phone qu’on trouve dans les vieux tutoriels sont dépréciés au profit d’objets structurés. Les champs principaux à connaître :

  • defaultEmailAddress.emailAddress remplace l’ancien champ email scalaire. Il contient aussi marketingState (SUBSCRIBED, UNSUBSCRIBED, PENDING, NOT_SUBSCRIBED), marketingOptInLevel (SINGLE_OPT_IN, CONFIRMED_OPT_IN, UNKNOWN) et marketingUpdatedAt (timestamp ISO 8601 du dernier changement). C’est aussi ici qu’on injecte le consentement préservé depuis l’outil emailing.
  • defaultPhoneNumber.phoneNumber remplace l’ancien champ phone scalaire, au format E.164 obligatoire. Mêmes sous-champs marketing pour le SMS.
  • addressesV2 (au lieu de addresses qui est déprécié en output) retourne la liste paginée des adresses. En input lors d’un customerCreate, on continue de passer un tableau addresses[] avec les champs address1, address2, city, provinceCode, countryCode (ISO 3166-1 alpha-2 obligatoire), zip, phone.
  • firstName, lastName, locale, tags, note, taxExempt sont des scalaires classiques.
  • metafields[] permet d’attacher des metafields à la création, chacun avec namespace, key, type (number_integer, single_line_text_field, date_time, etc.) et value (toujours stringifié).

Côté contraintes techniques importantes à connaître : email et phone sont uniques par boutique (Shopify normalise en interne avant comparaison), createdAt n’est pas settable (Shopify écrase systématiquement avec la date de l’import, workaround via metafield), customerUpdate ne supporte pas le marketing consent (il faut utiliser les mutations dédiées customerEmailMarketingConsentUpdate et customerSmsMarketingConsentUpdate).

Pour le lookup et l’idempotence, la query customerByIdentifier permet de retrouver un client par email, phone, ou metafield avec uniqueValues activé. C’est cette dernière option qui permet de pointer directement vers un client à partir de son ancien ID PrestaShop, WooCommerce ou Magento, sans maintenir de table de mapping côté outil de migration.

Le coût d’une mutation customerCreate enrichie (avec marketing consent, adresse, tags et metafield d’identifiant) est de 13 points sur l’API 2026-04, mesuré sur compte réel. Détail dans le piège n°9 dédié aux limites de l’API.

Notre approche

Chez Prestarocket, agence Shopify et partenaire Shopify basée à Lille, nous avons développé en interne un outil de synchronisation en PHP/Symfony qui gère spécifiquement les imports clients et commandes vers Shopify. Cet outil tourne par-dessus l’API GraphQL Admin de Shopify et offre plusieurs propriétés essentielles pour les migrations professionnelles. Peu importe la techno utilisée, ce qui compte, c’est la logique.

L’outil garantit une idempotence native : on peut relancer un import sans créer de doublons, grâce à un metafield client dédié qui maintient le lien entre l’identifiant source (PrestaShop, WooCommerce, Magento) et l’identifiant Shopify. La traçabilité est intégrale : chaque ligne importée est loguée avec son statut, son origine et son équivalent Shopify, ce qui permet de retrouver la source d’un problème en quelques secondes.

Le mode silencieux est forcé au niveau API plutôt qu’au niveau admin Shopify : webhooks customers/create désactivés en amont, side-effects côté apps tierces neutralisés via une procédure documentée, et marketing consent géré explicitement pour ne jamais déclencher d’email de confirmation par inadvertance. Cela élimine le risque qu’un paramètre coché par erreur côté interface administrateur Shopify déclenche un envoi massif. Les API tokens Shopify ne sont jamais stockés en clair, ils sont chiffrés en AES-GCM avec rotation possible.

Trois capacités plus avancées différencient l’outil des solutions standards du marché. Le calcul prédictif des coûts API estime à l’avance le coût en points de chaque mutation GraphQL et planifie l’envoi en fonction du bucket disponible, ce qui évite les erreurs de throttling et permet d’optimiser le débit en restant constamment juste sous la limite. Le support du bulk JSONL permet de traiter des centaines de milliers de clients en quelques heures plutôt que sur plusieurs jours pour les très gros volumes. La validation pré-envoi vérifie l’ensemble des champs côté Symfony avant génération du fichier d’import, ce qui réduit drastiquement le taux d’erreurs côté Shopify et évite le retravail manuel.

Côté méthode, nous découpons le processus de migration clients en cinq étapes : audit complet de la base existante (volumétrie, qualité, consentements, segments actifs), préparation des données et mapping (comment chaque champ source trouve sa place dans le modèle Shopify), préparation des intégrations cibles (Klaviyo, RGPD, programme fidélité, customer accounts), import progressif silencieux sur la nouvelle boutique Shopify en preview sans aucun envoi vers les clients, et bascule synchronisée le jour J avec un runbook précis et une communication client orchestrée.

Voyons maintenant les pièges concrets qui jalonnent ce parcours.

Piège n°1 : l’opt-in marketing et le RGPD

Voici un piège que peu d’articles documentent, et qui peut pourtant coûter très cher en France et en Europe. La gestion du consentement marketing est différente entre la plateforme source (PrestaShop, WooCommerce, Magento) et Shopify, et un transfert mal pensé peut soit inscrire des clients qui n’avaient pas consenti, soit faire perdre l’opt-in de clients qui l’avaient pourtant donné.

Chaque plateforme source a son propre modèle. PrestaShop stocke généralement le consentement sur deux champs distincts : l’inscription à la newsletter d’une part, et l’acceptation des « offres partenaires » d’autre part. WooCommerce utilise un user meta _wc_user_subscribed_to_newsletter ou des plugins tiers (MailPoet, Mailchimp for WooCommerce) qui ajoutent leurs propres champs. Magento stocke le consentement dans newsletter_subscriber avec un statut numérique (1=subscribed, 3=unsubscribed). Shopify, lui, utilise un modèle plus structuré avec des états standardisés (SUBSCRIBED, UNSUBSCRIBED, PENDING, NOT_SUBSCRIBED), un niveau d’opt-in (SINGLE_OPT_IN, CONFIRMED_OPT_IN, UNKNOWN) et un timestamp de consentement.

Le mapping doit être explicite et documenté. Notre règle : par défaut, on n’inscrit personne. Seuls les clients ayant un consentement actif et explicite sont marqués SUBSCRIBED sur Shopify. Tous les autres restent NOT_SUBSCRIBED. Mieux vaut une liste plus petite et propre qu’une liste polluée qui va dégrader vos taux de délivrabilité et exposer la marque à des plaintes CNIL.

L’outil de marketing automation comme source de vérité, pas la plateforme source

Si la boutique utilise un outil de marketing automation (Klaviyo, Brevo, Connectif, Mailchimp), il faut le considérer comme la source de vérité pour les consentements, pas la plateforme source. Le champ newsletter côté PrestaShop, WooCommerce ou Magento est généralement figé à la date d’inscription du client et ne reflète plus la réalité actuelle : il n’enregistre pas les unsubscribes via les emails marketing, ni les hard bounces, ni les marquages spam, ni les suppressions manuelles. L’outil emailing, en revanche, maintient un historique précis avec timestamp pour chaque profil, mis à jour en temps réel à chaque changement.

Le risque concret de prendre la plateforme source comme référence est triple. Réinscrire des unsubscribers : un client qui s’était désabonné via un email il y a 6 mois va se retrouver à nouveau marqué SUBSCRIBED côté Shopify, recevra des emails post-go-live, et te marquera comme spam. Réinscrire des hard bounces : les adresses qui n’existent plus, suppressed côté outil emailing, repartent en envoi. Ton bounce rate explose à la première campagne et ton ESP te suspend automatiquement. Casser l’historique RGPD : le consentUpdatedAt envoyé à Shopify devient la date de migration au lieu de la vraie date du consentement, ce qui rend la trace inutilisable en cas de contrôle CNIL.

L’approche correcte consiste à exporter les consentements depuis l’outil emailing en amont de la migration et à les croiser par email avec les clients de la plateforme source. Pour illustrer, prenons Klaviyo comme exemple détaillé puisque c’est le plus répandu sur nos projets. Concrètement, deux méthodes selon le volume. Pour moins de 50 000 profils, l’export CSV depuis Klaviyo (via Audience > Lists & Segments) suffit : on crée un segment qui contient tous les profils, on coche les colonnes Email, Phone Number, les properties contenant l’ID source (PrestaShop Customer ID, WooCommerce User ID, Magento Customer ID selon l’intégration en place), les Subscriptions pour le consentement email/SMS, et les Suppressions. Klaviyo envoie le fichier CSV par email quelques minutes plus tard. Pour les volumes supérieurs, l’API Klaviyo (/api/profiles/) permet une extraction scriptée avec un rate limit confortable (75 req/s en burst, 700/min en steady).

Le piège dans le fichier CSV Klaviyo : un profil peut avoir Email Marketing Consent: SUBSCRIBED mais une Suppression non vide (typiquement bounced ou user_unsubscribed). Klaviyo conserve l’état de consentement initial dans le state, mais empêche réellement l’envoi via la suppression. Si on prend seulement la colonne consent, on réinscrit ces profils. Il faut donc lire les deux colonnes et appliquer la règle : tout profil avec Suppressions non vide → UNSUBSCRIBED côté Shopify, indépendamment du state.

Les autres outils suivent une logique similaire avec leurs spécificités. Brevo (très présent sur le marché français) utilise un système de listes + statuts (Active, Hard Bounce, Unsubscribed, Spam Reporter, Blacklisted). Pour qu’un contact soit SUBSCRIBED côté Shopify, il doit être à la fois Active côté Brevo et présent dans la liste newsletter principale ; un statut Blacklisted ou EMAIL_BLACKLISTED: true impose UNSUBSCRIBED. Le timestamp à utiliser est DOUBLE_OPT_IN_DATE quand il est renseigné. Export possible via l’interface (Contacts > Listes > Exporter) ou via l’API (GET /v3/contacts, rate limit 400 req/min). Connectif fonctionne sur un modèle de contacts avec attributs personnalisés et activités enregistrées par workflow ; l’export via l’interface ou l’API (/contacts, rate limit 100 req/min) donne accès aux statuts d’opt-in et aux dates de consentement, et l’ID source est généralement stocké comme attribut custom selon la configuration de l’intégration PrestaShop, WooCommerce ou Magento. Mailchimp utilise un statut subscribed / unsubscribed / cleaned / pending au niveau audience, avec une timestamp_opt qui correspond au consentement ; export via Audience dashboard ou via l’API /lists/{list_id}/members.

Quel que soit l’outil utilisé, le principe reste identique : c’est l’état dans l’outil emailing qui pilote ce qu’on envoie à Shopify, jamais le champ newsletter figé de la plateforme source. Et toujours croiser le statut de consentement avec les éventuelles suppressions ou blacklist pour ne pas réinscrire des adresses qui ne devraient plus recevoir d’emails.

Pour les clients de la plateforme source qui n’ont pas de profil correspondant dans l’outil emailing (typiquement 5 à 10% : très anciens clients, comptes créés via des workflows qui ne synchronisent pas vers le marketing), on importe en NOT_SUBSCRIBED par défaut. C’est conservateur mais sain : ils pourront se réinscrire post-migration via les formulaires d’inscription s’ils le souhaitent.

Le piège central : ne jamais importer en PENDING

C’est probablement le piège le plus subtil et le plus mal connu de toute la migration clients. Si la boutique Shopify cible a le Double Opt-In activé dans Settings → Notifications (configuration recommandée pour la conformité RGPD/CNIL en France), Shopify envoie automatiquement un email de confirmation à chaque client créé avec marketingState: PENDING. Pour 30 000 clients importés en PENDING, c’est 30 000 emails de confirmation qui partent, exactement ce qu’on veut éviter en migration.

La logique correcte pour la migration, en croisant l’outil emailing et la plateforme source :

État dans l’outil emailingÀ mettre dans CustomerInput ShopifyEmail envoyé ?
Opt-in actif sans suppression/blacklistSUBSCRIBED + consentUpdatedAt historiqueNon
Opt-in actif mais suppression/blacklist (bounce, etc.)UNSUBSCRIBEDNon
Désinscrit / UnsubscribedUNSUBSCRIBEDNon
Jamais opt-in / Never subscribedChamp non set (= NOT_SUBSCRIBED)Non
Profil absent de l’outil emailingChamp non set (= NOT_SUBSCRIBED)Non

C’est juridiquement défendable et opérationnellement propre. Le client était opt-in confirmé dans l’outil emailing avec preuve d’horodatage, on le marque SUBSCRIBED sur Shopify avec ce même horodatage. Le double opt-in a déjà été fait au moment où il s’est inscrit, pas besoin de le redemander. Si on importait tout en PENDING « par sécurité », on enverrait des dizaines de milliers d’emails de confirmation, on perdrait 60 à 80% de la base marketing (taux moyen de re-confirmation très faible), et on dégraderait la sender reputation pour rien.

Désactiver le Double Opt-In Shopify pendant la fenêtre d’import

Même en appliquant rigoureusement la règle « jamais de PENDING », nous recommandons en plus de désactiver temporairement le Double Opt-In côté Shopify pendant toute la durée de la fenêtre d’import. C’est une approche de double sécurité (belt-and-suspenders) qui élimine le risque résiduel : si une seule ligne du flux d’import passe en PENDING par erreur (cas limite mal géré côté outil, valeur inattendue dans le CSV source, retry partiel après une coupure réseau), le Double Opt-In activé déclencherait l’envoi d’email de confirmation. Une seule mauvaise ligne sur 50 000 reste anecdotique, mais le coût est asymétrique : un email de confirmation envoyé à un client qui ne s’attend à rien, c’est un signalement spam potentiel.

Le workflow concret. Avant l’import : aller dans Settings → Customer accounts → Email marketing, et basculer « Marketing opt-in confirmation » en désactivé. On note la valeur initiale pour pouvoir la remettre. Pendant l’import : la fenêtre reste en single opt-in côté Shopify, ce qui n’a aucun impact sur les clients existants ni sur les inscriptions à venir une fois la migration terminée. Après vérification du résultat de l’import (idéalement le lendemain matin, après contrôle des compteurs et sondage manuel sur quelques profils) : remettre le Double Opt-In en activé pour que les nouvelles inscriptions post-go-live respectent la conformité RGPD/CNIL.

Cette désactivation temporaire n’a pas d’impact juridique : on n’importe que des clients dont le consentement a déjà été obtenu (via l’outil emailing source de vérité), avec leur timestamp historique préservé. Le Double Opt-In est un mécanisme de validation à l’inscription, pas un dispositif rétroactif. La conformité repose sur la preuve d’horodatage du consentement, pas sur le réenvoi systématique d’un email de confirmation.

Préserver le timestamp de consentement

Le timestamp de consentement doit être préservé. C’est lui qui prouve, en cas de contrôle CNIL, à quel moment le client a donné son accord. Sur Shopify, le champ consentUpdatedAt doit être populé avec la date originale du consentement, pas avec la date de l’import.

Notre outil Symfony gère ce mapping automatiquement : il récupère le timestamp de consentement depuis l’export de l’outil emailing (selon l’outil : consent_timestamp côté Klaviyo, DOUBLE_OPT_IN_DATE côté Brevo, attribut date custom côté Connectif, timestamp_opt côté Mailchimp) et l’injecte dans le consentUpdatedAt côté Shopify. Si l’outil emailing n’a pas de timestamp précis (cas rare), on fallback sur la date de création du profil dans cet outil, puis sur la date_add newsletter de la plateforme source en dernier recours. La plupart des outils de migration « clé en main » du marché écrasent ce champ avec la date courante, ce qui rend la trace inutilisable en cas d’audit.

Pour les marchands qui utilisent un Double Opt-In, il faut aussi préserver le marketingOptInLevel à CONFIRMED_OPT_IN. Pour le simple opt-in (case cochée à l’inscription sans confirmation par email), c’est SINGLE_OPT_IN. Cette valeur a une portée juridique, ne pas la mettre à UNKNOWN par facilité.

Côté SMS, même logique mais souvent plus délicate : les marchands ont rarement une trace propre du consentement SMS, et il est plus prudent de ne rien importer en SUBSCRIBED côté SMS et de relancer une opt-in campaign post-go-live pour ceux qui le souhaitent. Si l’outil emailing a une donnée SMS marketing fiable (avec timestamp), on peut l’importer, mais on reste plus conservateur que pour l’email.

Attention au piège : NOT_SUBSCRIBED vs UNSUBSCRIBED

C’est une distinction subtile mais lourde de conséquences, qu’on voit régulièrement mal traitée dans les outils de migration standard. Les deux états sonnent comme des synonymes (le client ne reçoit pas d’emails marketing) mais ils sont juridiquement et fonctionnellement très différents côté Shopify.

NOT_SUBSCRIBED signifie que le client n’a jamais opté pour le marketing. C’est l’état par défaut d’un client qui n’a jamais coché la case newsletter. Il peut être démarché pour s’inscrire (via un formulaire de capture sur la boutique, une popup, un lead magnet) parce qu’il n’a jamais exprimé de refus.

UNSUBSCRIBED signifie que le client s’est explicitement désabonné. Il a cliqué sur « se désabonner » dans un email, ou a demandé manuellement à être retiré. Côté Shopify, ce statut bloque toute nouvelle inscription via les formulaires de capture standard : si ce client se réinscrit lui-même via le footer, Shopify maintient le statut UNSUBSCRIBED pour respecter sa volonté explicite (sauf re-opt-in via un mécanisme actif côté Shopify Email ou Klaviyo qui demande une confirmation).

L’erreur classique dans les outils de migration mal calibrés : mapper « le client n’est pas dans la liste newsletter » → UNSUBSCRIBED par sécurité. C’est faux et c’est piégeux. Ces clients vont se retrouver figés dans un état « désabonné » qu’ils n’ont jamais demandé, et les popups newsletter de la nouvelle boutique ne réussiront pas à les réinscrire. Sur une base de 30 000 clients, ce sont potentiellement plusieurs milliers de contacts marketing qu’on perd irrémédiablement.

La règle correcte côté Shopify :

Situation sourceÉtat côté ShopifyPourquoi
Jamais inscrit, jamais demandé à l’êtreNOT_SUBSCRIBED (champ non renseigné)Peut être démarché plus tard
Inscrit actif côté outil emailingSUBSCRIBED + consentUpdatedAtReçoit les emails
Désabonné explicite (clic « se désabonner »)UNSUBSCRIBED + consentUpdatedAtRefus à respecter
Hard bounce / suppression techniqueUNSUBSCRIBED (ou laisser non renseigné selon politique)Email invalide
Inactif sans désabonnement formelNOT_SUBSCRIBEDPas de refus explicite, ré-engageable

Cette distinction est non seulement juridiquement plus exacte (RGPD : on ne retient pas un refus que le client n’a pas exprimé) mais elle préserve aussi le potentiel commercial de la base. Un client qui n’avait pas pris le temps de s’inscrire à la newsletter sur l’ancien site peut très bien s’y mettre sur le nouveau, si on lui en laisse la possibilité technique.

Notre outil Symfony croise donc trois sources pour décider du statut final côté Shopify : la liste des opt-in actifs côté outil emailing (Klaviyo, Brevo, Connectif, Mailchimp), la liste des unsubscribes explicites côté outil emailing (avec leur timestamp), et la liste de tous les autres clients de la base source. Ces derniers tombent en NOT_SUBSCRIBED par défaut, jamais en UNSUBSCRIBED.

Piège n°2 : les notifications transactionnelles qui spamment vos clients

Ce piège est documenté dans notre article sur la migration des commandes, mais il a un volet spécifique côté clients qui mérite d’être souligné ici. Quand on importe les commandes historiques (sujet de l’article connexe), Shopify et ses apps tierces peuvent générer des emails à destination des clients sur des achats parfois très anciens. Objectif : aucune interruption ni pollution dans l’expérience utilisateur pendant et après la migration.

Côté import des comptes clients spécifiquement, le risque ne vient plus de l’invitation automatique Shopify (qui n’existe plus avec les New Customer Accounts passwordless, voir piège en fin d’article), mais bien des side-effects à la création sur la nouvelle boutique. Trois sources principales d’emails parasites en migration. Les webhooks customers/create déclenchent toute app ou intégration externe abonnée à cet événement, sur 30 000 clients importés, c’est 30 000 webhooks émis qui peuvent chacun déclencher des actions en cascade. Les apps de welcome / onboarding se déclenchent sur la création d’un nouveau profil : Klaviyo notamment peut entrer un client dans un Welcome Series s’il est nouvellement créé dans le compte, ce qui pour 30 000 clients ferait 30 000 entrées simultanées dans le Welcome. Les apps de fidélité (Smile.io, LoyaltyLion, Yotpo Loyalty) calculent des points de bienvenue à l’inscription et attribuent des points « compte créé » à chaque profil importé.

Notre check-list de vérification avant tout import clients : désactivation temporaire du Double Opt-In côté Shopify (Settings → Customer accounts → Email marketing), désactivation des welcome flows Klaviyo (passage en Draft), désactivation des règles d’attribution de points de l’app fidélité, audit des webhooks customers/create actifs (Settings → Notifications → Webhooks), désactivation des workflows Shopify Flow déclenchés sur création de client, et import test avec un seul client (votre propre email) pour vérifier qu’aucun email ne part nulle part avant de lancer le batch complet.

Piège n°3 : Klaviyo et la continuité de la relation marketing

Klaviyo est l’outil le plus répandu chez nos clients, et sa gestion en migration mérite une attention particulière. Au-delà des notifications transactionnelles évoquées plus haut, il y a un enjeu structurel : préserver la continuité de la stratégie marketing existante sans repartir de zéro.

Garder le compte existant

Sauf cas particulier, nous recommandons de conserver le même compte Klaviyo lors d’une migration depuis PrestaShop vers Shopify. Les raisons sont concrètes : l’historique comportemental (events Placed Order, Viewed Product, Added to Cart accumulés sur des années) alimente les segments lifecycle (VIP, win-back, engaged 90j) qui pilotent les campagnes. Tout perdre signifie attendre plusieurs mois avant que les segments redeviennent significatifs.

Conserver le compte signifie aussi préserver la réputation de l’expéditeur (sender reputation), qui se construit lentement et qu’un nouveau compte met du temps à reconstruire, avec un risque immédiat de spam folder pendant la période de warming.

Le piège des métriques distinctes

Tous les flows existants sont triggered sur la métrique « Placed Order » rattachée à l’intégration PrestaShop. Quand on connectera Shopify, Klaviyo créera une nouvelle métrique « Placed Order » rattachée à l’intégration Shopify, distincte. Les deux coexistent dans le compte. Sans action explicite, les flows existants ne se déclencheront pas sur les nouvelles commandes Shopify, et vous aurez l’impression que vos automations sont cassées alors qu’elles sont juste pointées vers la mauvaise source.

C’est valable pour toutes les métriques e-commerce : Placed Order, Ordered Product, Fulfilled Order, Refunded Order, Cancelled Order, Started Checkout, Active on Site, Viewed Product, Added to Cart.

Notre stratégie : préparation à froid

Pendant que la prod PrestaShop tourne normalement, nous préparons en parallèle dans le même compte Klaviyo tous les nouveaux assets Shopify, en Draft. Le client peut continuer à envoyer ses campagnes et ajuster ses flows existants sans perturbation.

Concrètement, cela donne une cartographie complète des flows, segments et templates qui utilisent une métrique e-commerce, suivie d’un clonage de chaque flow avec un suffixe explicite ([Shopify], [New]). Les clones restent en Draft tant qu’ils ne sont pas activés. On met à jour les métriques de trigger : remplacer la métrique PrestaShop par la métrique Shopify équivalente. La métrique Shopify n’existant pas encore tant que l’intégration n’est pas active, on connecte Shopify à Klaviyo une à deux semaines avant le go-live (en gardant tous les flows en Draft) pour pouvoir préparer ce mapping à froid. On met à jour aussi les blocs dynamiques dans les templates (Abandoned Cart, Browse Abandon, Post-Purchase), les structures de données produit diffèrent entre PrestaShop et Shopify, les template tags utilisés dans les emails ne sont pas interchangeables. On termine par le clonage des segments avec conditions metric-based, en pointant sur les nouvelles métriques.

La bascule le jour J

Dans cet ordre strict : couper PrestaShop (mode maintenance), désactiver l’intégration PrestaShop dans Klaviyo (Disable, pas Delete, supprimer la métrique effacerait tout l’historique de Placed Order accumulé depuis des années), vérifier que l’historical sync Shopify est complète, activer les nouveaux flows Shopify du moins critique au plus critique, désactiver les anciens flows PrestaShop (passage en Draft, archivage seulement deux semaines plus tard après monitoring), test avec une vraie commande pour vérifier que les events arrivent dans la bonne métrique.

Le garde-fou universel

Sur tous les nouveaux flows Shopify, nous ajoutons un filtre « Triggered at is after [date du go-live] ». Cela empêche tout flow de se déclencher rétroactivement sur des events historiques re-syncés (notamment lors de l’import des commandes historiques traité dans notre article dédié). Ce filtre est retiré 30 jours après le go-live, une fois la stabilité confirmée.

Piège n°4 : les groupes clients et données enrichies

Au-delà des données de base (email, nom, adresses), une base clients PrestaShop contient souvent des informations enrichies qui pilotent la stratégie commerciale : groupe client, statut VIP, langue préférée, source d’acquisition, statut B2B, civilité, etc. Plusieurs de ces champs sont natifs côté PrestaShop mais n’ont pas d’équivalent natif côté Shopify.

Donnée PrestaShopPrésent natif ShopifySolution
Date de création compte (date_add)Non settable, écrasée à l’importMetafield custom.ps_created_at type date_time
Genre / civilité (id_gender)NonTag préfixé gender:male, gender:female
Groupes clients (ps_customer_group)Non (sauf B2B Plus)Tags préfixés ps-group:premium, ps-group:b2b
Langue préférée (id_lang)Oui, champ locale ISO 639Mapping table par projet vers fr, nl, en
Newsletter consentOuiMapping vers emailMarketingConsent
ID client PrestaShopNon (différent de l’ID Shopify)Metafield custom.ps_customer_id avec uniqueValues

La locale du client : souvent ignorée, pourtant structurante

Sur Shopify, chaque client a un champ locale (code ISO 639 : fr, en, de, es, it…) qui n’est pas qu’une donnée descriptive. C’est lui qui pilote plusieurs comportements visibles côté client final :

La langue des emails transactionnels Shopify : confirmation de commande, expédition, retour, password reset, demande d’avis. Si la boutique a plusieurs langues activées, Shopify choisit le template selon le locale du client. Sans valeur explicite, le défaut est en, ce qui est gênant pour un marchand français qui vend principalement en France mais a quelques clients belges ou suisses.

La langue de l’interface Customer Account (passwordless login, page compte, historique des commandes côté client) qui s’affiche dans la langue du client.

Le routing dans certaines apps comme Klaviyo (qui permet de segmenter par langue), Judge.me ou Loox (avis multilingues), ou les apps de fidélité avec emails localisés.

Pour un PrestaShop FR pur (un seul shop FR, clients tous francophones), c’est trivial mais ça reste à setter explicitement : tous les clients passent à fr à l’import. C’est une ligne dans notre outil Symfony qui force la valeur, mais sans ce setting, on tombe sur le défaut Shopify en et certains clients reçoivent leur confirmation de commande en anglais.

Pour les marchands multi-langues (PrestaShop avec FR, NL, EN actifs, ou WooCommerce avec WPML/Polylang, ou Magento multi-stores), c’est nettement plus structurant. La table ps_customer.id_lang chez PrestaShop, le user meta wpml_admin_language_for_edit ou icl_admin_language chez WooCommerce, ou le store_id chez Magento, doivent être mappés vers le locale Shopify correspondant. Notre outil Symfony maintient une table de correspondance par projet (ex. : id_lang=1 → fr, id_lang=2 → nl, id_lang=3 → en) qui est validée avec le marchand pendant la phase de mapping.

À noter : la locale est aussi remontée côté outil emailing pour piloter les flows multilingues. Si Klaviyo est en place avec des Welcome series différentes en FR et NL, le client va recevoir le flow correspondant à sa locale Shopify (via la sync native). Une mauvaise locale à l’import = un client français qui reçoit ses emails marketing en néerlandais pendant des mois avant qu’on s’en aperçoive. Et ce risque est d’autant plus grand que la locale n’est pas affichée dans l’interface admin Shopify standard : ni sur la fiche client, ni en colonne de la liste clients (le champ n’est même pas disponible dans le sélecteur de colonnes). Pour la vérifier, il faut passer par l’export CSV (colonne Customer Locale), par l’API GraphQL, ou par un Customer Segment filtré sur customer.locale. C’est une raison de plus pour bien la setter à l’import et pour produire dans le rapport de migration une stat de répartition des locales : un mauvais mapping passe complètement inaperçu côté admin jusqu’au premier ticket SAV d’un client qui a reçu sa confirmation de commande dans la mauvaise langue.

La stratégie des tags préfixés

Pour les groupes clients PrestaShop, plutôt que de créer des champs custom complexes ou de perdre la donnée, nous utilisons des tags préfixés avec une convention claire : ps-group:premium, ps-group:b2b, ps-group:revendeur. Le préfixe a trois avantages : il distingue visuellement dans l’admin Shopify les tags d’origine PrestaShop des tags ajoutés via Shopify ou Klaviyo, il permet un filtrage facile via les Customer Segments Shopify (condition customer_tags STARTSWITH ‘ps-group:’), et il rend possible un cleanup futur sécurisé si on veut retirer les tags legacy après quelques années.

Cette même logique s’étend à d’autres données : gender:male, acq:google-ads, lang:fr, marketing:vip-2024. Chaque préfixe forme un namespace qui évite les collisions et permet des segments précis du type « clients VIP français en B2B » via customer_tags CONTAINS ‘ps-group:b2b’ AND customer_tags CONTAINS ‘lang:fr’ AND customer_tags CONTAINS ‘marketing:vip-2024’.

Le metafield d’identifiant pour l’idempotence

Le point le plus important techniquement : créer un metafield client custom.ps_customer_id avec la capability uniqueValues activée, qui stocke l’ID PrestaShop d’origine. Cela permet trois choses essentielles. Lookup direct depuis l’ID PrestaShop vers le client Shopify via customerByIdentifier, sans avoir à maintenir une table de mapping côté Symfony. Idempotence native : avant de créer un client, on vérifie s’il existe déjà via cet ID. Si oui, customerUpdate ; sinon, customerCreate. Traçabilité indéfinie : même des années après la migration, le SAV peut retrouver un client par son ancien ID PrestaShop si un ticket fait référence à un numéro historique.

La capability uniqueValues empêche techniquement deux clients Shopify d’avoir le même ps_customer_id, ce qui élimine les risques de désynchronisation. Sans cette capability, l’unicité n’est pas garantie au niveau base de données.

L’enjeu n’est pas tant technique que stratégique : la marque utilise-t-elle réellement tous ses segments PrestaShop, ou s’agit-il de scories accumulées au fil des années ? L’audit de base clients est l’occasion de faire le tri. Importer 47 segments PrestaShop dont seuls 6 sont activement utilisés génère du bruit et de la confusion post-migration. Notre méthode : lister tous les segments / groupes / tags PrestaShop, demander au client lesquels servent réellement à des actions marketing ou opérationnelles, et n’importer que ceux-là. Les autres sont archivés (export CSV gardé en backup) mais pas portés dans Shopify.

Piège n°5 : les contraintes d’unicité et les doublons silencieux

Shopify impose deux contraintes d’unicité strictes au niveau Customer, documentées officiellement dans la doc API : l’email doit être unique par boutique, et le téléphone doit être unique par boutique au format E.164. La doc Shopify est explicite : « Attempting to assign the same phone number to multiple customers returns an error » et « Attempting to assign the same email address to multiple customers returns an error ».

Ces deux contraintes sont sources de pièges spécifiques en migration depuis des bases legacy.

L’unicité du téléphone, le piège silencieux

Sur les bases PrestaShop legacy françaises, on rencontre régulièrement des cas pathologiques sur le téléphone. Des doublons de comptes avec même numéro : un même utilisateur a créé deux comptes au fil des années (oubli de mot de passe, achat sous une autre adresse), avec parfois des emails différents mais le même téléphone. Des numéros par défaut pour clients newsletter : certains marchands ont défini un numéro factice (genre 0000000000 ou 0123456789) pour les inscriptions newsletter sans téléphone réel, résultat, des centaines de clients partagent le même numéro. Des fake numbers à l’inscription : clients ayant rempli le champ téléphone obligatoire avec n’importe quoi, parfois identique entre eux.

Sur une base de 50 000 clients PrestaShop, on trouve typiquement 0,5 à 2% de conflits d’unicité téléphone au moment de l’import vers Shopify. Sans gestion proactive côté outil, ces conflits génèrent des userErrors silencieuses et des centaines de lignes en échec dans le rapport.

Notre stratégie est de détecter les doublons en amont, côté Symfony, avant de générer le JSONL. Pour chaque numéro normalisé en E.164, on compte les occurrences. Pour ceux qui apparaissent plus d’une fois, on garde le numéro sur le client le plus récent ou actif et on vide le champ phone sur les autres (les laisser en customers sans phone, ce qui est parfaitement valide côté Shopify). Tout est loggué pour audit client. En parallèle, on filtre les numéros de toute évidence factices : séquences répétitives, numéros tronqués, numéros qui ne passent pas la validation libphonenumber::isValidNumber().

Important à savoir : Shopify normalise en interne avant de comparer. Les formats +33 6 12 34 56 78, +33612345678, et 0033612345678 sont tous équivalents pour la contrainte d’unicité. Donc deux clients avec ces différents formats seront considérés comme dupliqués, même si vos données source les distinguent.

L’unicité de l’email

Pour l’email, le cas est plus simple à gérer puisque la déduplication est généralement plus propre côté PrestaShop (l’email est souvent défini comme unique dans le schéma). Mais on rencontre quand même des cas tordus : clients ayant deux comptes avec des variations de casse (John@Example.com vs john@example.com, considérés identiques par Shopify) ou avec des espaces parasites. La normalisation côté outil (lowercase + trim) résout ces cas.

Reste les vrais doublons : un même client avec un compte particulier et un compte professionnel sur la même adresse. Là, c’est une décision métier à prendre avec le marchand : fusionner, garder le plus actif, créer une variation d’email avec un alias +…

Piège n°6 : les adresses multiples par client

Un client PrestaShop peut avoir plusieurs adresses (livraison, facturation, professionnelle, secondaire). La plupart des outils de migration « clé en main » n’importent que l’adresse principale. C’est un piège silencieux : le client se connecte sur la nouvelle boutique, va sur son compte, et constate que ses 3 adresses enregistrées n’en sont plus qu’une. Ce qui paraît être un détail technique génère en réalité de la frustration et des tickets SAV (« où sont passées mes adresses ? »).

Selon le contexte, deux stratégies possibles. Stratégie 1 : importer toutes les adresses via le tableau addresses du CustomerInput. Plus complet, mais plus complexe à valider et à maintenir en cas de re-run. Stratégie 2 : n’importer qu’une adresse principale (typiquement l’adresse de livraison la plus récente) et laisser le client ajouter les autres au besoin via son compte. Plus simple, plus rapide, et cohérent avec l’idée que le client va « renouveler » sa relation à la marque sur la nouvelle boutique.

Notre recommandation par défaut est la stratégie 2 pour les boutiques B2C standards, sauf demande explicite du marchand. La stratégie 1 reste pertinente pour le B2B où plusieurs adresses de livraison coexistent légitimement.

Dans tous les cas, validation côté outil avant l’envoi : countryCode en ISO 3166-1 alpha-2 obligatoire (PrestaShop stocke souvent le nom complet « France », à mapper vers « FR »), validation du zip selon le pays (regex par pays), normalisation UTF-8 des accents (les « é » qui deviennent « Ã© » en encoding cassé sont fréquents sur les vieilles bases), troncature des champs trop longs.

Piège n°7 : le lien avec la migration des commandes et du catalogue

La migration des clients ne peut pas être pensée indépendamment de celle des commandes et du catalogue produit. L’historique d’achat alimente les segments lifecycle, les events Klaviyo, les calculs de LTV, et la perception client de la continuité du service.

Trois questions structurantes à trancher en amont. Importe-t-on l’historique des commandes complet ou seulement une fenêtre récente ? Importer les 5 dernières années permet de préserver les segments long terme mais alourdit la migration et augmente le risque de doublons d’events Klaviyo. Importer seulement les 12 derniers mois simplifie tout mais casse certains segments lifecycle. Les statuts de commandes anciennes doivent-ils être importés ? Une commande « fulfilled » il y a 3 ans n’a pas besoin de redéclencher des automations. Notre outil force le statut final à l’import et désactive les triggers de fulfillment. Le lien client-commande doit être préservé. C’est trivial à dire, complexe à exécuter quand les IDs changent entre les deux plateformes. Notre outil maintient le lien via le metafield ps_customer_id côté client et un metafield équivalent côté commande, ce qui permet de reconstituer la relation même en cas de re-run partiel.

Pour les détails techniques sur la migration des commandes elle-même, nous renvoyons à notre article dédié sur le sujet, qui couvre aussi les sujets connexes essentiels d’un projet de refonte : redirections 301 des anciennes URLs vers les nouvelles, préservation du référencement naturel et du trafic organique via Google Search Console, mapping du catalogue produit, gestion des taxes et de l’expédition, et configuration du tunnel d’achat dans la nouvelle boutique Shopify.

Piège n°8 : les limites de l’API Shopify

Sur les petites bases clients (quelques milliers de profils), la question des limites API ne se pose pas. Sur les bases moyennes (50 000 à 500 000 clients) et grandes (plus d’un million), c’est un facteur structurant qui peut transformer une migration de quelques heures en migration de plusieurs jours.

Comprendre le rate limiting Shopify GraphQL

L’API GraphQL Admin utilise un système de « leaky bucket » calculé en points plutôt qu’en nombre de requêtes. Chaque combinaison app + boutique dispose d’un bucket qui se vide à un rythme constant, le restore rate, exprimé en points par seconde. Selon la documentation officielle Shopify (shopify.dev/docs/api/usage/limits), les limites du restore rate par plan sont les suivantes :

Plan ShopifyRestore rateBucket size approximatif
Standard / Shopify100 points/s2 000 points
Advanced200 points/s4 000 points
Plus1 000 points/s20 000 points

Chaque mutation coûte un nombre de points qui dépend de sa complexité, et une seule query ne peut jamais coûter plus de 1000 points même si le bucket est plein. Les bucket sizes ne sont pas explicitement listés dans la doc officielle ; les valeurs ci-dessus suivent la convention historique « 20 secondes de restore » observée dans la communauté développeur. Pour les valeurs réelles d’une boutique donnée, le mieux est de mesurer via le champ extensions.cost.throttleStatus retourné par toute requête GraphQL.

Le coût réel d’un customerCreate de migration

Mesure réelle effectuée sur l’API 2026-04 : une mutation customerCreate enrichie pour migration (avec marketing consent, adresse complète, tags, et metafield d’identifiant) coûte exactement 13 points GraphQL. La décomposition est cohérente avec la philosophie Shopify : 10 points pour le coût de base de la mutation (qui couvre les effets de bord serveur, déclenchement de webhooks, écritures en base), 1 point pour l’objet customer retourné, 1 point pour le sous-objet defaultEmailAddress, 1 point pour le metafield. Les scalaires retournés (id, email, firstName, etc.) sont gratuits.

Cela permet de calculer le débit théorique selon le plan, en utilisant les limites officielles Shopify :

Volume de clientsPlan Standard (100 pts/s)Plan Advanced (200 pts/s)Plan Plus (1 000 pts/s)
10 000~22 minutes~11 minutes~2 minutes
50 000~1,8 heures~54 minutes~11 minutes
100 000~3,6 heures~1,8 heures~22 minutes
500 000~18 heures~9 heures~1,8 heures
1 000 000~36 heures~18 heures~3,6 heures

Ces chiffres sont théoriques (régime établi sans burst, sans erreur). En pratique, prévoir une marge de 20 à 30% pour les retries, les pics de complexité, et l’activité parallèle des autres apps installées sur la boutique. À noter que les boutiques de développement (dev stores) peuvent avoir des limites plus basses que ce qui est annoncé pour leur tier, toujours vérifier sur la boutique cible réelle avant de planifier précisément.

Pourquoi le calcul prédictif change tout

Le mode de fonctionnement standard est réactif : on envoie la requête, on lit le throttleStatus dans la réponse (maximumAvailable, currentlyAvailable, restoreRate), et on ajuste la cadence pour la prochaine requête. C’est ce que font les outils de migration et les bibliothèques disponibles (shopify-api-node, @shopify/shopify-api-js, luminarix/laravel-shopify-graphql). Le problème de cette approche est qu’elle est nécessairement conservative : pour ne pas dépasser, on garde une marge de sécurité, et on perd en débit. Sur une grosse migration, cette marge cumulée peut faire perdre plusieurs heures.

L’approche prédictive consiste à estimer à l’avance le coût de la mutation qu’on va envoyer, et à ne l’envoyer que si le bucket disponible (calculé localement à partir du dernier throttleStatus observé et du temps écoulé depuis) le permet. Cela permet de coller au plus près de la limite sans la dépasser, et d’optimiser le débit global.

À notre connaissance, il n’existe pas d’application de migration grand public dédiée au calcul prédictif des coûts API Shopify. Le système natif Shopify retourne les coûts dans extensions.cost, avec breakdown par champ via le header Shopify-GraphQL-Cost-Debug=1, mais c’est de l’observation post-call. Une extension VS Code par Nextools existe pour les Shopify Functions uniquement. Un projet open source de réplication de l’algorithme existe en Ruby mais reste partiel et non maintenu. Les bibliothèques officielles et tierces gèrent le retry automatique en mode réactif, pas la prédiction.

C’est précisément cette absence d’outillage standard qui nous a poussés à développer notre propre logique de calcul prédictif intégrée à notre outil de synchro Symfony. L’algorithme estime le coût de chaque mutation à partir d’une table de coûts maintenue à jour par mesure empirique, en croisant avec le throttleStatus retourné par les requêtes précédentes pour ajuster en continu.

Le bulk JSONL pour les très gros volumes

Avec les limites actuelles, les seuils où le bulk JSONL devient stratégique sont les suivants : à partir de 50 000 clients sur plan Standard, à partir de 100 000 sur plan Advanced, et seulement à partir de 500 000 à 1 000 000 sur plan Plus. En dessous de ces volumes, les mutations individuelles avec calcul prédictif suffisent largement et offrent un meilleur contrôle (granularité du retry, debugging facilité, feedback temps réel).

Le principe du bulk JSONL : générer un fichier JSONL contenant une mutation par ligne, l’uploader vers Shopify via stagedUploadsCreate, puis lancer un bulkOperationRunMutation qui traite tout en asynchrone côté Shopify, sans consommer le quota normal de l’application.

Avantages : pas de rate limit pendant l’exécution (le bulk tourne en parallèle du quota normal), exécution en quelques heures même pour des centaines de milliers de clients, possibilité de paralléliser plusieurs bulks (clients + commandes + products en simultané depuis l’API 2026-01). Limites : fichier JSONL plafonné à 100 MB, durée maximale de 24h par bulk, certaines mutations annexes pas supportées en bulk (notamment les customerEmailMarketingConsentUpdate séparés, mais le marketing consent est gérable directement dans le customerCreate).

Notre outil bascule automatiquement entre mutations individuelles et bulk JSONL selon le volume détecté, ce qui évite l’overhead bulk inutile sur les petites migrations tout en utilisant la puissance bulk pour les gros volumes. Sur une migration multi-jours en mode « fil de l’eau », on combine les deux : bulk pour le batch initial massif, mutations individuelles pour les ajustements quotidiens et les sync incrémentales.

Piège n°9 : les limitations natives Shopify

Plusieurs champs natifs côté Shopify ne se comportent pas comme on s’y attend, et il faut les connaître pour ne pas perdre silencieusement de la donnée.

Le champ createdAt n’est pas settable. Quel que soit le système d’import (REST, GraphQL, Matrixify, Cart2Cart), Shopify écrase systématiquement la date de création avec la date du moment de l’import. Tous les createdAt côté Shopify seront identiques, ce qui empêche toute analyse de cohorte par mois d’inscription côté natif Shopify. Workaround : stocker la vraie date d’inscription dans un metafield custom.ps_created_at type date_time. Côté Klaviyo, ce metafield se sync comme profile property et permet de reconstituer les segments lifecycle basés sur l’ancienneté du compte.

Les champs email et phone ont été restructurés. Les versions récentes de l’API GraphQL Admin remplacent les champs scalaires email et phone au niveau du customer par des objets dédiés defaultEmailAddress et defaultPhoneNumber qui encapsulent l’adresse, son statut marketing, et son historique de validation. À adapter dans le bloc de retour de toutes les mutations customer.

customerUpdate ne supporte pas le marketing consent. Pour modifier le consent marketing d’un client existant, il faut utiliser les mutations dédiées customerEmailMarketingConsentUpdate et customerSmsMarketingConsentUpdate. Conséquence pour la sync incrémentale quotidienne : trois mutations par client modifié dans le pire cas (update du customer + update email consent + update SMS consent). À optimiser avec un système de diff côté Symfony qui ne déclenche que les mutations strictement nécessaires.

Le numéro de commande Shopify ne préserve pas la séquence PrestaShop. Shopify démarre par défaut à 1001, ou continue la séquence existante de la boutique cible. Si vos clients connaissent leurs commandes par leur ancien numéro (#28 543), il faut soit préfixer les commandes migrées (LEG-28543), soit stocker l’ancien numéro dans un metafield custom pour le SAV. Sujet traité plus en détail dans notre article sur la migration des commandes.

Piège n°10 : l’authentification, et la fin des mots de passe

On termine par un sujet qui inquiète souvent les marchands au démarrage d’un projet de migration : « qu’est-ce qui va se passer pour les mots de passe de mes clients ? ». La réponse simple : les mots de passe ne se migrent jamais (c’est une contrainte cryptographique, les hashes sont salés différemment entre PrestaShop, Shopify, WooCommerce et Magento). Mais Shopify a récemment changé son système de comptes clients, ce qui modifie complètement la donne sur cette question.

Le passage aux New Customer Accounts (passwordless)

Le 26 février 2026, Shopify a officiellement déprécié les Classic Customer Accounts (annonce officielle). Concrètement, toute nouvelle boutique, et toute boutique existante qui n’avait pas encore activé les Classic Customer Accounts, utilise désormais les New Customer Accounts. Pour une migration depuis PrestaShop, WooCommerce ou Magento, qui se traduit toujours par la création ou l’activation d’une nouvelle boutique Shopify, on est donc forcément sur le nouveau système.

Les New Customer Accounts fonctionnent en mode passwordless : le client saisit son email et reçoit un code à 6 chiffres à usage unique. Plus de mot de passe à gérer, plus de « mot de passe oublié », plus de tickets support pour comptes verrouillés. Le changelog officiel met également en avant d’autres bénéfices natifs : store credit intégré, retours self-service, support B2B, et personnalisations via app blocks no-code. Les Classic Customer Accounts continuent de fonctionner sur les boutiques qui les utilisaient déjà, mais sans mises à jour ni support technique ; une sunset date sera annoncée plus tard en 2026.

Cette évolution simplifie une partie du problème historique de la migration. Avec l’ancien système, il fallait envoyer une « invitation à activer le compte » à chaque client importé pour qu’il définisse un nouveau mot de passe, d’où le paramètre historique send_email_invite côté ancienne API REST. Avec le passwordless, ce mécanisme disparaît purement et simplement : pas de mot de passe à créer, pas d’invitation à envoyer. La mutation customerCreate de l’API GraphQL Admin (que nous utilisons dans notre outil Symfony) ne déclenche aucune invitation automatique. C’est un point qu’on voit encore régulièrement mentionné dans de vieux tutoriels de migration, mais qui n’est plus pertinent depuis la transition vers les New Customer Accounts.

Communiquer le changement à vos clients

Le vrai sujet maintenant, c’est la communication clients sur le passage au passwordless. Lors d’une migration, le client qui revient sur la nouvelle boutique va découvrir que son ancien mot de passe ne fonctionne plus, mais aussi que la procédure habituelle « mot de passe oublié » a disparu. À la place, il saisit son email, reçoit un code à 6 chiffres, et se connecte. Pour une partie de la clientèle (notamment les profils plus âgés ou peu digitaux), c’est désorientant.

Notre approche en quatre temps. Communication anticipée : une campagne email partira 48 à 72h avant la bascule, expliquant clairement que la nouvelle boutique arrive avec une nouvelle façon de se connecter, plus simple et plus sécurisée. Le ton doit être positif (nouveauté) et pédagogique (expliquer le passwordless), pas excusif. Page d’aide dédiée : tutoriel court avec captures d’écran expliquant le nouveau flow de connexion. Message contextuel dans la page de connexion : un encart pédagogique directement intégré au thème Shopify, affiché aux visiteurs non connectés au moment où ils cliquent pour se connecter. C’est le seul canal qui touche 100% des clients qui reviennent organiquement sur le site, sans dépendre du taux d’ouverture des emails. Préparation du support client : briefing de l’équipe SAV pour gérer les demandes « comment je me connecte ? » sans créer de friction et assurer une assistance fluide pendant la transition.

Côté apps tierces, attention aussi : les Storefront API customer mutations (customerCreate, customerUpdate, customerAccessTokenCreate pour les anciens comptes) sont également en cours de déprécation. Si la boutique actuelle utilisait un custom storefront ou des intégrations spécifiques basées sur ces mutations, il faut les migrer vers le Customer Account API. À auditer pour chaque app tierce du client pendant la phase de mapping.

Validations à faire avant l’envoi

Shopify rejette en silence ou avec des messages cryptiques les données mal formées. Une partie significative de la qualité d’une migration tient à la validation pré-envoi côté outil, qui transforme les rejets Shopify en transformations automatiques côté Symfony quand c’est possible, et en rapport d’erreurs exploitable quand ce n’est pas le cas.

Tableau de validation par champ

ChampContrainte ShopifyValidation côté outilCas pathologiques PrestaShop
emailFormat RFC 5321, unique par boutiqueRegex + filter_var + check unicité dans le batchEmails avec espaces, doublons, fakes (test@test.com)
phoneFormat E.164, unique par boutiqueNormalisation libphonenumber-php avec country FRNuméros locaux français (06 12 34 56 78), fakes (0000000000)
firstName / lastNameMax 50 caractèresTrim + check longueur + UTF-8 valideNoms en MAJUSCULES, valeurs ., -, « client »
tagsMax 250 par client, 255 chars par tagSplit + trim + dédup + slugTags avec virgules, noms de groupes contenant des espaces
addresses[0].countryCodeISO 3166-1 alpha-2 obligatoireMapping nom → code via tableau interneStockage en nom complet (« France » au lieu de « FR »)
addresses[0].zipValidé selon le paysRegex par pays (FR : ^\d{5}$)Codes étrangers mal saisis, « 00000 », espaces
emailMarketingConsent.consentUpdatedAtISO 8601 UTCDate du consentement original PrestaShopDate NULL ou écrasée par défaut à now()
metafields[].valueToujours un stringCast en string + format selon type42891 envoyé en integer plante

Le rapport pré-migration

Notre outil produit, avant tout import, un rapport de validation segmenté en trois catégories : validés (importables tels quels), avec warnings (importables après normalisation automatique : téléphones reformatés en E.164, noms normalisés en Title Case, codes postaux nettoyés, adresses tronquées), et bloqués (non importables sans intervention manuelle : emails invalides, codes pays non mappables, doublons d’identifiants source, dates de consentement futures).

Sur une base typique de 50 000 clients PrestaShop, on observe généralement entre 90 et 95% de clients validés directement, 5 à 8% avec warnings traités automatiquement, et 1 à 2% bloqués nécessitant une décision client (corriger côté PrestaShop avant l’import, accepter la perte, traitement manuel post-migration).

C’est exactement le genre de delivrable qui justifie une migration agence professionnelle plutôt qu’un outil clé en main : le client voit et valide en amont les cas limites, plutôt que de découvrir 1000 erreurs après la migration en se demandant ce qui s’est passé.

Comparatif technique avec les outils du marché

Plutôt qu’une critique gratuite, ce tableau positionne honnêtement chaque approche selon son cas d’usage. Matrixify (disponible sur le Shopify App Store) est un excellent outil généraliste qui couvre énormément de besoins Shopify standards, particulièrement quand on a déjà des données propres. Les outils type Cart2Cart ou LitExtension sont des services de migration conçus pour des migrations rapides « clic-clac » sans personnalisation poussée, ils prennent en charge plusieurs plateformes sources (PrestaShop, WooCommerce sur WordPress, Magento, Wix, Shopify) avec un forfait par boutique. Notre outil agence est conçu pour les migrations PrestaShop, WooCommerce et Magento legacy avec données sales, contraintes RGPD strictes, et besoins d’idempotence multi-jours.

CapacitéMatrixifyOutils standards (Cart2Cart, LitExtension)Notre outil Symfony
Normalisation auto des téléphones (E.164)Non, rejette les invalides, à corriger en ExcelNonOui via libphonenumber, 95 %+ rattrapés
Validation des codes postaux par paysNonNonOui par regex pays
Mapping countryCode auto (nom → ISO)PartielPartielOui exhaustif
Préservation du consentUpdatedAt RGPDNon, date d’import par défautNonOui, date PrestaShop préservée
Détection des doublons phone en amontNonNonOui, pre-flight audit
Préservation date d’inscriptionLimitation Shopify (impossible nativement)Limitation ShopifyWorkaround via metafield ps_created_at
Mapping ID source ↔ ID ShopifyManuel via ExcelManuelNatif via metafield ps_customer_id avec uniqueValues
Idempotence (re-run safe)Partielle (par email)NonNative via metafield + lookup
Calcul prédictif des coûts APINonNonOui
Bulk JSONL avec validation pré-envoiNon, Excel uploaded directementNonOui, validation côté Symfony
Rapport pré-migration détailléDry-run basiqueNonOui, segmenté validés/warnings/bloqués
Désactivation systématique des notificationsManuelManuelForcé au niveau API

Ces différences ne sont pas anodines sur des migrations de plusieurs centaines de milliers de clients depuis des bases legacy : elles déterminent si la migration prend une semaine ou un mois, et si le taux d’erreurs final est de 0,1% ou de 5%.

Notre runbook communication client

La technique ne suffit pas. Une migration réussie côté client repose autant sur la communication que sur l’exécution technique. Voici la séquence que nous mettons en place avec nos clients pour garantir une transition fluide et préserver l’expérience utilisateur jusqu’à la mise en ligne de la nouvelle boutique Shopify.

J-7 : annonce générale sur les canaux du marchand (newsletter, réseaux sociaux). Ton positif : « Nouveau site, nouvelle expérience ». Pas d’excuses, pas de mention de « migration » qui inquiète.

J-3 : email préparatoire à la base clients. Annonce claire de la date de bascule, explication pédagogique du nouveau flow de connexion passwordless, présentation des bénéfices clients (nouvelle UX, nouveaux services, fonctionnalités améliorées).

J-1 : rappel court (notamment pour les abonnés récents qui auraient manqué le premier message).

J0, H+2 heures après le go-live : email de lancement avec lien direct vers la nouvelle boutique et tutoriel court sur la connexion par email + code.

J+1 : message de remerciement aux premiers clients ayant commandé sur la nouvelle boutique. Crée un effet de communauté autour du lancement et améliore le taux de conversion sur la première semaine.

J+7 : enquête de satisfaction courte (3 questions max) auprès des clients ayant utilisé le nouveau site. Permet de capter rapidement les frictions perçues et d’ajuster avant que l’effet de nouveauté ne s’estompe.

Cette communication doit être préparée et validée avant la migration technique. La rédiger dans l’urgence le jour J garantit des messages de mauvaise qualité.

Ce que notre outil interne change

La plupart des outils de migration disponibles sur le marché font le travail de transfert brut. Ils ne gèrent pas finement les enjeux que nous avons décrits dans cet article : mapping du consentement RGPD avec préservation du timestamp, normalisation automatique des téléphones via libphonenumber, désactivation systématique des notifications au niveau API, idempotence native via metafield avec capability uniqueValues, calcul prédictif des coûts API, bulk JSONL avec validation pré-envoi, détection des doublons phone en amont, ou rapport de validation segmenté avant tout import.

Notre outil de synchro développé en Symfony comble ces manques. Il a été pensé spécifiquement pour les enjeux des migrations e-commerce françaises (RGPD, structures B2B fréquentes, multi-langues, mentions légales), et il évolue projet après projet pour intégrer les pièges qu’on découvre. Le calcul prédictif des coûts API en particulier nous a permis de réduire significativement le temps de migration sur les bases de plusieurs centaines de milliers de clients, là où les outils standards plafonnent en débit ou multiplient les erreurs.

Cet outil n’est pas un produit commercial vendu séparément, c’est un asset agence qui nous permet de proposer un niveau de qualité de migration que les outils standards du marché ne permettent pas d’atteindre.

Migration clients dans un projet de refonte global

La migration des clients s’inscrit rarement seule : elle fait généralement partie d’un projet de refonte e-commerce complet, qui comprend aussi le choix d’un thème Shopify (gratuit ou payant), la personnalisation du design, la création des pages et du contenu éditorial, la configuration du tunnel d’achat et des moyens de paiement, le paramétrage des taxes et de l’expédition, la migration ou le rachat du nom de domaine pour préserver l’URL existante, la mise en place des redirections 301 depuis l’ancien site pour préserver le trafic organique, la prise en main du back office Shopify par l’équipe du commerçant, l’intégration éventuelle avec un ERP ou un PIM pour la synchronisation des stocks et du catalogue, et bien sûr la sauvegarde complète de l’ancienne plateforme avant toute opération.

Plusieurs de ces sujets sont traités dans notre guide complet sur la migration depuis PrestaShop, WooCommerce ou Magento vers Shopify. Mais quel que soit le périmètre exact du projet, l’audit du site existant est toujours la première étape. Cet audit permet de quantifier précisément le volume de données à transférer, d’identifier les apps tierces actives, de cartographier les redirections nécessaires, de valider la stratégie sur les comptes clients, et d’estimer un planning réaliste. Sans cet audit complet en amont, on découvre les problèmes pendant le projet, ce qui est généralement plus coûteux que de les anticiper.

Côté outils complémentaires, la migration peut nécessiter l’usage de Google Search Console (pour suivre l’impact SEO et soumettre le nouveau sitemap), d’extensions Shopify dédiées à l’analytics, et d’apps spécifiques pour les avis, le programme de fidélité, ou la gestion du point de vente physique si le commerçant fait aussi de la vente en magasin. Chaque app du Shopify App Store qu’on ajoute à la nouvelle boutique doit être validée pour son comportement à l’import (notifications, webhooks, déclenchements automatiques) avant le go-live.

Conclusion

Migrer une base clients vers Shopify n’est pas un acte technique anodin. C’est un moment où la marque se présente sous un nouveau visage à ses clients existants, et où la qualité de l’exécution conditionne la perception de cette nouveauté. C’est aussi un point d’inflexion pour la croissance de l’activité : une boutique en ligne refondue avec une expérience utilisateur améliorée, un tunnel d’achat fluide et une interface administrateur Shopify plus simple à gérer au quotidien crée les conditions d’une meilleure performance commerciale dans les mois qui suivent la mise en production.

Les pièges sont nombreux et la plupart ne sont rattrapables qu’à un coût élevé : une fois 30 000 emails parasites envoyés, on ne peut pas les rappeler. Une fois la sender reputation dégradée, il faut des semaines pour la reconstruire. Une fois le consentement marketing perdu, il faut relancer des opt-in campaigns coûteuses.

L’investissement dans la préparation, dans le bon outillage, et dans la communication client en amont est ce qui transforme une migration risquée en non-événement. C’est ce que nous visons sur chaque projet, en tant qu’agence Shopify expert et partenaire Shopify accompagnant les marques sur l’ensemble du processus : audit complet préalable, préparation des données, importation des données vers la nouvelle boutique, transition fluide jour J, et stabilisation post-go-live.

Si vous envisagez de migrer vers Shopify depuis votre plateforme actuelle (PrestaShop, WooCommerce, Magento ou Wix) et que cet article a soulevé des questions sur votre projet de migration, n’hésitez pas à nous contacter pour un audit préalable du site web existant. Un échange en amont peut faire gagner des semaines de stabilisation post-go-live et garantir une migration sécurisée, sans perte de données ni interruption de service.

Cet article fait partie d’une série de guides consacrés aux migrations vers Shopify. Voir aussi notre guide dédié à la migration du catalogue, des commandes, et à la gestion des redirections et du référencement naturel.

Prestarocket est une agence Shopify et PrestaShop basée à Lille, France. Nous accompagnons les commerçants et marques dans leurs projets e-commerce : développement de thèmes Shopify, création de boutiques en ligne, modules sur mesure, migrations depuis PrestaShop, WooCommerce ou Magento, optimisation de la performance et du taux de conversion.