Symptôme. Sur un catalogue WooCommerce de 100 000 références synchronisé chaque nuit depuis un ERP fournisseur, plusieurs clients signalent avoir payé un article à un prix, puis reçu une facture à un tarif différent le lendemain. Aucune erreur dans les journaux, aucun code d’erreur PHP, aucune alerte du serveur : le site a fonctionné normalement pendant toute la synchronisation, ce qui rend le problème d’autant plus difficile à repérer.
Diagnostic
La synchronisation nocturne traite les 100 000 fiches par lots de 500, via une tâche wp_schedule_event() qui appelle wc_update_product_stock() et met à jour le champ _regular_price et _sale_price produit par produit. Le traitement complet dure environ deux heures. Pendant cette fenêtre, une fiche déjà traitée affiche le nouveau prix, une fiche pas encore traitée affiche encore l’ancien, et surtout, une fiche en cours de traitement peut, dans de rares cas, être consultée exactement entre la mise à jour du prix régulier et celle du prix soldé, ce qui produit un prix affiché temporairement incohérent, ni l’ancien ni le nouveau.
Le vrai problème n’est donc pas la durée de la synchronisation en elle-même — deux heures pour 100 000 fiches est un rythme raisonnable — mais l’absence totale de signal pour le client pendant cette fenêtre. Rien ne distingue, du point de vue de l’acheteur, un prix stable d’un prix en cours de recalcul.
Correctif
La solution consiste à marquer explicitement chaque fiche comme « en cours de resynchronisation » au tout début de son traitement, et à ne retirer ce marqueur qu’une fois toutes les métadonnées de prix cohérentes entre elles. Le marquage se fait via update_post_meta() sur une clé dédiée, jamais via le statut de publication du produit lui-même, qui doit rester inchangé pour ne pas affecter le référencement ni la disponibilité du produit dans les flux d’export.
function agence_synchroniser_lot_produits( array $ids_produits ) {
foreach ( $ids_produits as $product_id ) {
update_post_meta( $product_id, '_resync_en_cours', 1 );
}
foreach ( $ids_produits as $product_id ) {
$donnees_erp = agence_recuperer_donnees_erp( $product_id );
update_post_meta( $product_id, '_regular_price', $donnees_erp['prix_ht'] );
update_post_meta( $product_id, '_price', $donnees_erp['prix_ttc'] );
if ( ! empty( $donnees_erp['prix_solde'] ) ) {
update_post_meta( $product_id, '_sale_price', $donnees_erp['prix_solde'] );
}
// Une fois toutes les métas cohérentes, on lève le marqueur
delete_post_meta( $product_id, '_resync_en_cours' );
}
wc_delete_product_transients( $product_id );
}

Remplacer l’affichage, pas le bloquer
Le filtre woocommerce_get_price_html permet d’intercepter l’affichage du prix pour chaque produit et de le remplacer par un indicateur temporaire tant que le marqueur de resynchronisation est présent, sans jamais toucher aux données de prix elles-mêmes ni bloquer la navigation.
add_filter( 'woocommerce_get_price_html', function ( $price_html, $product ) {
if ( get_post_meta( $product->get_id(), '_resync_en_cours', true ) ) {
return '<span class="prix-en-maj">' .
esc_html__( 'Tarif en cours de mise à jour, actualisation sous quelques minutes', 'agence-boutique' ) .
'</span>';
}
return $price_html;
}, 10, 2 );
add_filter( 'woocommerce_is_purchasable', function ( $is_purchasable, $product ) {
if ( get_post_meta( $product->get_id(), '_resync_en_cours', true ) ) {
return false;
}
return $is_purchasable;
}, 10, 2 );
Bloquer l’achat en même temps que l’affichage du prix, via woocommerce_is_purchasable, est indispensable : afficher un message d’attente tout en laissant le bouton d’ajout au panier actif reviendrait à recréer exactement le problème initial, avec une étape de confusion supplémentaire pour le client.
Prévention
Trois mesures évitent que ce type d’incident ne se reproduise, y compris si la logique de synchronisation évolue par la suite :
- Toujours poser le marqueur d’indisponibilité avant de commencer à écrire la moindre métadonnée de prix, jamais après
- Traiter les lots par transaction logique complète : toutes les métadonnées d’un produit sont mises à jour ensemble, ou aucune
- Surveiller la durée réelle de la fenêtre de resynchronisation avec une alerte si elle dépasse le double du temps habituel, signe qu’un lot est resté bloqué avec des produits marqués indéfiniment indisponibles
Un prix incohérent affiché une seconde suffit à générer une réclamation ; mieux vaut un message d’attente explicite pendant deux heures qu’un prix faux pendant deux minutes.
En résumé
Ce correctif ne cherche pas à accélérer la synchronisation elle-même, mais à rendre visible son état transitoire. Sur un catalogue de cette taille, la fenêtre de mise à jour ne disparaîtra jamais complètement ; ce qui doit disparaître, c’est le risque qu’un client voie un prix qui n’est déjà plus vrai sans qu’aucun signal ne l’en informe.