« Invalid or duplicate order ID. » Ce message, renvoyé par la quasi-totalité des passerelles de paiement sérieuses (Stripe, PayPal, Ingenico, et bien d’autres), n’a rien d’un bug côté passerelle malgré les apparences. Il signale presque toujours qu’un identifiant déjà transmis lors d’une tentative précédente est réutilisé, ce que la passerelle refuse par sécurité pour éviter un double débit.
Symptôme : un refus qui semble aléatoire
Le message n’apparaît pas sur toutes les commandes, ce qui complique le diagnostic : il touche surtout les commandes qui ont connu une première tentative de paiement échouée ou abandonnée, suivie d’une nouvelle tentative avec le même identifiant de commande WooCommerce. Sur un site à fort trafic, ce cas de figure reste statistiquement rare mais jamais nul, d’autant plus fréquent que le taux d’abandon en cours de paiement est élevé.
Diagnostic : où se situe la réutilisation d’identifiant
La cause racine se trouve dans la manière dont l’intégration de la passerelle construit l’identifiant transmis lors de l’appel API. Beaucoup d’intégrations, y compris certaines maison développées rapidement, transmettent directement $order->get_id() comme référence de transaction sans y ajouter de composant variable :
// Ce qui provoque le problème : réutilisation du même ID
// à chaque nouvelle tentative de paiement sur la même commande
$reference_transaction = $order->get_id();
$reponse = $client_passerelle->creerPaiement([
'order_id' => $reference_transaction,
'amount' => $order->get_total(),
]);
Si le client abandonne une première tentative de paiement (fermeture d’onglet, retour arrière) puis relance le paiement depuis la même commande WooCommerce, le second appel API porte exactement le même order_id que le premier, aux yeux de la passerelle. Certaines passerelles tolèrent cette répétition, d’autres la rejettent explicitement pour prévenir un double débit accidentel.

Correctif : distinguer numéro de commande et référence de transaction
La correction consiste à générer une référence de transaction unique à chaque tentative de paiement, tout en conservant le lien avec le numéro de commande WooCommerce pour la réconciliation comptable :
function generer_reference_transaction_unique(WC_Order $order): string
{
$tentative = (int) $order->get_meta('_nombre_tentatives_paiement');
$tentative++;
$order->update_meta_data('_nombre_tentatives_paiement', $tentative);
$order->save();
return sprintf('%d-t%d', $order->get_id(), $tentative);
}
Cette référence composite (numéro de commande suivi d’un numéro de tentative) reste lisible pour un rapprochement comptable manuel tout en garantissant l’unicité attendue par la passerelle à chaque nouvel essai.
Prévenir la récidive
- Ne jamais transmettre l’identifiant de commande brut comme référence de transaction unique si un client peut relancer un paiement plusieurs fois sur la même commande
- Journaliser chaque tentative avec sa référence exacte, utile pour retrouver l’historique en cas de réclamation client
- Tester explicitement le scénario d’abandon puis de reprise de paiement avant la mise en production d’une nouvelle passerelle
Prévention à long terme
Ce type d’erreur reste invisible en phase de recette si les tests ne couvrent que le chemin nominal (un paiement réussi du premier coup). Ajouter systématiquement un scénario de test avec échec puis reprise de paiement, pour toute nouvelle intégration de passerelle, aurait révélé le problème avant la mise en production plutôt qu’après les premiers signalements clients.
En résumé
« Invalid or duplicate order ID » pointe presque toujours vers une confusion entre numéro de commande WooCommerce et référence de transaction transmise à la passerelle. Générer une référence unique par tentative de paiement, tout en gardant le lien avec la commande d’origine, résout durablement ce type d’incident sans toucher à la configuration de la passerelle elle-même.