« Fer Forgé Pro » vend du matériel de quincaillerie à des professionnels dans toute l’Union européenne, et jusqu’ici, le numéro de TVA intracommunautaire saisi au paiement n’était vérifié par aucun contrôle automatique : une simple expression régulière garantissait le format, pas la validité réelle du numéro auprès des administrations fiscales. Ce tutoriel construit la vérification qui manquait, en s’appuyant sur le service officiel VIES de la Commission européenne, sans aborder ici la question distincte de l’autoliquidation qui en découle.
Le VIES (VAT Information Exchange System) expose un service web permettant de vérifier en temps réel qu’un numéro de TVA intracommunautaire est actif dans les registres d’un État membre. Techniquement, il s’agit d’un service SOAP, pas REST, ce qui demande un peu plus de rigueur côté implémentation PHP que la plupart des API modernes.
Étape 1 : ajouter le champ numéro de TVA au tunnel de commande
Sur un compte professionnel, le champ existe déjà généralement via une extension B2B, mais pour cet exemple, on l’ajoute nativement au formulaire de facturation via le filtre woocommerce_billing_fields, avec une classe CSS dédiée pour le cibler facilement côté script :
add_filter( 'woocommerce_billing_fields', 'ffp_ajouter_champ_tva' );
function ffp_ajouter_champ_tva( $champs ) {
$champs['billing_vat_number'] = array(
'type' => 'text',
'label' => 'Numéro de TVA intracommunautaire',
'required' => false,
'class' => array( 'form-row-wide' ),
'priority' => 35,
);
return $champs;
}
Étape 2 : appeler le service VIES depuis PHP
PHP fournit nativement l’extension SOAP via la classe SoapClient. Le WSDL officiel du service se trouve à l’adresse https://ec.europa.eu/taxation_customs/vies/services/checkVatService.wsdl. Il est indispensable d’entourer l’appel d’un bloc try/catch : le service est connu pour ses interruptions fréquentes, en particulier tôt le matin lors des opérations de maintenance des administrations nationales.
function ffp_verifier_tva_vies( $pays, $numero ) {
try {
$client = new SoapClient(
'https://ec.europa.eu/taxation_customs/vies/services/checkVatService.wsdl',
array( 'connection_timeout' => 5 )
);
$reponse = $client->checkVat( array(
'countryCode' => $pays,
'vatNumber' => $numero,
) );
return (bool) $reponse->valid;
} catch ( SoapFault $e ) {
return null; // service indisponible, pas "numéro invalide"
}
}

Étape 3 : distinguer « invalide » de « service indisponible »
C’est le piège central de cette intégration : une exception SOAP ne signifie pas que le numéro de TVA est faux, elle signifie souvent que le service VIES est momentanément hors ligne. Bloquer la commande dans ce cas précis reviendrait à empêcher un client parfaitement légitime de payer, à cause d’une panne qui ne le concerne pas. La fonction retourne donc explicitement trois états, pas deux : valide, invalide, ou indéterminé.
La règle métier retenue chez Fer Forgé Pro
- Numéro valide selon VIES : validation immédiate, aucune friction
- Numéro invalide selon VIES : message d’avertissement, mais commande non bloquée, avec vérification manuelle a posteriori
- Service indisponible : commande acceptée normalement, une alerte interne signale le cas pour contrôle manuel différé
Étape 4 : mettre en cache le résultat
Interroger VIES à chaque affichage de page ou à chaque tentative de paiement serait à la fois lent et inutilement gourmand pour un service public partagé par toute l’Union européenne. Le résultat de la vérification est mis en cache via l’API Transients de WordPress, pendant six heures, une durée jugée raisonnable pour un numéro qui ne change pratiquement jamais en cours de journée.
function ffp_verifier_tva_avec_cache( $pays, $numero ) {
$cle_cache = 'ffp_vies_' . md5( $pays . $numero );
$resultat = get_transient( $cle_cache );
if ( false === $resultat ) {
$resultat = ffp_verifier_tva_vies( $pays, $numero );
set_transient( $cle_cache, $resultat, 6 * HOUR_IN_SECONDS );
}
return $resultat;
}
Vérifications avant mise en production
Le point le plus facilement négligé : tester le comportement du tunnel de commande avec le service VIES volontairement rendu injoignable, en simulant un timeout réseau. C’est le seul moyen de vérifier que le repli fonctionne réellement et ne bloque personne un jour de panne du service européen.
Un contrôle externe qui bloque une vente quand il tombe en panne n’est pas un contrôle, c’est une nouvelle cause de panne déguisée en conformité.
En résumé
Brancher VIES sur un tunnel WooCommerce B2B demande moins de code que prévu, mais exige une discipline claire sur la gestion des trois états possibles du résultat. Traiter une indisponibilité de service comme un numéro invalide est l’erreur la plus fréquente, et la plus coûteuse commercialement, de ce type d’intégration.