Un abonné à un service de box de produits d’entretien écologiques avait changé de carte bancaire entre deux renouvellements. Sa nouvelle carte, soumise à l’authentification forte 3D Secure, avait refusé la validation lors d’un renouvellement automatique — un scénario banal, censé déclencher la séquence de relance standard de WooCommerce Subscriptions. Sauf que rien ne s’est passé : aucun e-mail de relance envoyé, aucune nouvelle tentative programmée, l’abonnement simplement figé au statut en attente de paiement pendant six semaines, jusqu’à ce que le client contacte le support de son côté.
Ce cas ne traite pas la mise en place initiale de l’authentification forte (SCA), déjà couverte par ailleurs, mais le diagnostic d’un comportement de relance qui, dans ce cas précis, ne se déclenchait tout simplement jamais.
Symptôme : un abonnement figé, sans erreur visible
Rien dans les journaux d’erreur PHP ne signalait de problème. La commande de renouvellement existait bien, au statut échoué, ce qui aurait normalement dû déclencher la séquence de relance automatique de l’extension. Pourtant, la table de planification des relances ne contenait aucune tâche programmée pour cet abonnement, alors que d’autres abonnements ayant échoué pour un motif de fonds insuffisants suivaient, eux, parfaitement le calendrier de relance habituel.
Diagnostic : deux chemins d’échec bien distincts
La séquence de relance automatique de WooCommerce Subscriptions se déclenche sur le hook woocommerce_subscription_payment_failed, déclenché lui-même lorsqu’une passerelle appelle explicitement WC_Subscriptions_Manager::process_subscription_payment_failure_on_child_order() après un refus de paiement classique. Le problème : un refus 3D Secure ne remonte pas par ce chemin chez la passerelle utilisée sur ce projet. Il remonte par un événement distinct, déclenché de façon asynchrone une fois la fenêtre d’authentification du client expirée sans validation, traité uniquement par un webhook séparé que le code d’intégration existant ignorait purement et simplement.
// Chemin suivi pour un refus de paiement classique (fonctionnait)
add_action( 'woocommerce_order_status_failed', function( $order_id ) {
$abonnements = wcs_get_subscriptions_for_order( $order_id );
foreach ( $abonnements as $abonnement ) {
WC_Subscriptions_Manager::process_subscription_payment_failure_on_child_order( $abonnement, $order_id );
}
} );
// Chemin suivi pour un refus 3D Secure (ignoré jusque-là)
// -> webhook "payment_intent.payment_failed" avec cause "authentication_required"
// jamais relié à la logique de relance de l'abonnement

Le correctif : relier l’événement manquant à la relance
La correction a consisté à écouter également l’événement webhook spécifique à l’échec d’authentification forte, et à déclencher manuellement le même traitement de relance que celui utilisé pour un refus classique, plutôt que de laisser cette famille d’échecs hors du parcours standard.
add_action( 'ak_webhook_paiement_intent_echoue', function( $intention ) {
$code_erreur = $intention['last_payment_error']['code'] ?? '';
if ( 'authentication_required' !== $code_erreur ) {
return;
}
$order_id = ak_retrouver_commande_depuis_intention( $intention['id'] );
$order = wc_get_order( $order_id );
$order->update_status( 'failed', 'Échec de la 3D Secure, authentification non complétée.' );
$abonnements = wcs_get_subscriptions_for_order( $order_id );
foreach ( $abonnements as $abonnement ) {
WC_Subscriptions_Manager::process_subscription_payment_failure_on_child_order( $abonnement, $order_id );
}
} );
Prévention pour les futures intégrations de passerelle
- Lister explicitement, pour chaque passerelle de paiement intégrée, tous les motifs d’échec possibles côté prestataire, pas seulement le refus de carte classique
- Vérifier que chaque motif d’échec finit par appeler
update_status( 'failed', ... )sur la commande concernée - Tester le scénario d’authentification forte expirée en environnement de test, un cas facile à simuler mais souvent oublié des jeux de test
Pourquoi ce cas est passé inaperçu si longtemps
Le volume de clients concernés par un renouvellement de carte nécessitant une authentification forte restait faible comparé au volume global d’échecs de paiement classiques, ce qui a masqué le problème statistiquement pendant plusieurs mois avant qu’un signalement client isolé ne permette de remonter jusqu’à la cause structurelle.
Un échec de paiement silencieux, sans aucune erreur technique visible, est souvent le signe d’un chemin d’événement entier non couvert, pas d’un simple bug ponctuel.
En résumé
Toutes les causes d’échec de paiement ne remontent pas par le même chemin d’événement côté passerelle, en particulier depuis la généralisation de l’authentification forte. Un abonnement figé sans aucune relance, sans aucune erreur dans les journaux, doit toujours faire suspecter un événement de passerelle non relié à la logique standard de WooCommerce Subscriptions plutôt qu’un problème de configuration classique.