Navigation

Related Articles

Back to Latest Articles

Valider un numéro de TVA intracommunautaire


Samuel Breton
Valider un numéro de TVA intracommunautaire

Aujourd’hui il est courant d’avoir à développer des sites avec abonnement, mise à disposition de service payant en ligne et autre, à l’échelle internationale et à des professionnels.

Or il se trouve que dans une situation pareille intervient la notion de TVA, où la responsabilité du développeur (ou de son employeur) peut être mise en jeu (ainsi que celle du client, mais ça…).

Lors du paiement, un client professionnel (une société, entreprise, personne morale quoi) devra saisir son numéro de TVA ainsi que son pays pour que le montant de la TVA correspondant soit calculé puisqu’il change d’un pays à l’autre.
Le site http://ec.europa.eu/ propose un webService permettant de vérifier cela.

Moi, je vous propose un validator personnalisé pour valider le numéro de TVA entré dès la validation du formulaire, basé sur ce webService.

Voici l’adresse du wsdl : http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl.

Le validator

Je pars du principe que l’utilisateur saisit son numéro de TVA et son code pays dans 2 champs différents. Le validator devra alors faire un contrôle en prenant en compte les 2 champs, à l’instar du validator Doctrine qui vérifie la concordance entre le username et le password au login. Donc il héritera de la classe sfValidatorSchema.

Commençons par voir la méthode __construct :

public function __construct($vat_number = 'vat_number', $country = 'country', $options = array(), $messages = array())
{
  $this->addOption('vat_number', $vat_number);
  $this->addOption('country', $country);
 
  $this->addOption('throw_global_error', false);
 
  $this->messages = array_merge(
    $this->messages,
    array(
      'invalid_syntax'=>'Your VAT Number syntax is not correct. You should have something like this: BE805670816B01',
      'invalid_country'=>'Your VAT Number is not valid for the selected country.',
      'invalid'=>sprintf('Invalid VAT Number. Check the validity on the customer VAT Number via <a href="%s">Europa VAT Number validation webservice</a>', 'http://ec.europa.eu/taxation_customs/vies/lang.do?fromWhichPage=vieshome'),
    )
  );
 
  parent::__construct(null, $options, $messages);
}

Ici on définit l’intitulé par défaut des champs contenant le numéro de TVA (vat_number) et le code du pays (country).
Puis on définit les messages des différentes erreurs pouvant être générées :
– mauvaise syntaxe
– pays qui ne correspond pas
– numéro invalide.

Voyons maintenant le doClean et la méthode appelant le webService :

public function doClean($values)
  {
    if (null === $values)
    {
      $values = array();
    }
 
    if (!is_array($values))
    {
      throw new InvalidArgumentException('You must pass an array parameter to the clean() method');
    }
 
    $vatnumber  = isset($values[$this->getOption('vat_number')]) ? $values[$this->getOption('vat_number')] : null;
    $country = isset($values[$this->getOption('country')]) ? $values[$this->getOption('country')] : null;
 
    //on récupère la validité du numéro de TVA via le webService
    $valid = $this->haleValidateVAT(array('vatnumber' => $vatnumber, 'country' => $country));
 
    //si le résultat n'est pas valide, on throw l'erreur correspondante
    if (!$valid['result'])
    {
      throw new sfValidatorError($this, $valid['error'], array('value' => $vatnumber));
 
      if ($this->getOption('throw_global_error'))
      {
        throw $error;
      }
 
      //l'erreur s'applique sur le champ vat_number
      throw new sfValidatorErrorSchema($this, array($this->getOption('vat_number') => $error));
    }
 
    //si valide, on retourne les valeurs
    return $values;
  }
 
  /**
   * vérifie la validité du numéro de TVA en prenant en compte le pays donné
   *
   * @param array $args
   * @return array
   */
  protected function haleValidateVAT($args = array()) {
    if ( '' != $args['vatnumber'] )
    {
      // on sérialize le numéro TVA
      $vat_number 	= str_replace(array(' ', '.', '-', ',', ', '), '', $args['vatnumber']);
      // on récupère le code pays
      $countryCode 	= substr($vat_number, 0, 2);
      //on récupère le numéro TVA
      $vatNumber 		= substr($vat_number, 2);
 
      //on vérifie la syntaxe du numéro
      if (strlen($countryCode) != 2 || is_numeric(substr($countryCode, 0, 1)) || is_numeric(substr($countryCode, 1, 2)))
      {
        $error = array('result' => false, 'error'=>'invalid_syntax');
        return $error;
      }
 
      //on vérifie que le pays correspond bien au pays indiqué dans le numéro de TVA
      if ( $args['country'] != $countryCode )
      {
        $error = array('result' => false, 'error'=>'invalid_country');
        return $error;
      }
 
      //appelle le webservice
      $client = new SoapClient("http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl");
      $params = array('countryCode' => $countryCode, 'vatNumber' => $vatNumber);
      $result = $client->checkVat($params);
 
      //vérifie la validité et renvoie l'erreyr correspondante
      if ( !$result->valid )
      {
        $error = array('result' => false, 'error'=>'invalid');
        return $error;
      }else{
        return array('result' => true);
      }
    }
    return array('result' => false, 'error'=>'required');
  }

Testons

Pour tester tout simplement, voici un petit formulaire contenant uniquement les champs pays et numéro de TVA :

class TestVATForm extends sfForm
{
  public function configure()
  {
    $this->widgetSchema['vat_number'] = new sfWidgetFormInput();
    $this->widgetSchema['country'] = new sfWidgetFormInput();
 
    $this->validatorSchema['vat_number'] = new sfValidatorString();
    $this->validatorSchema['country'] = new sfValidatorString();
 
    $this->validatorSchema->setPostValidator(new sfValidatorVAT());
 
    $this->widgetSchema->setNameFormat('tva_form[%s]');
  }
}

Voici l’action :

class defaultActions extends sfActions
{
  public function executeTestTVA(sfWebRequest $request)
  {
    $this->form = new TestVATForm();
 
    if($request->isMethod('post'))
    {
      $this->form->bind($request->getParameter($this->form->getName()));
 
      if($this->form->isValid())
      {
        $this->getUser()->setFlash('notice', 'TVA ok');
        $this->redirect('default/testTVA');
      }else{
        $this->getUser()->setFlash('error', 'TVA ko');
      }
    }
  }
}

Et pour finir, voici la vue (toute bête) :

<form action="<?php echo url_for('default/testTVA') ?>" method="post">
  <table>
    <?php echo $form ?>
    <tr>
      <td></td>
      <td>
        <input type="submit" value="Tester" />
      </td>
    </tr>
  </table>
</form>

Et voilà !

Show Comments (11)

Comments

  • Sylvio

    Merci pour ce validator.
    Il peut y’avoir des exceptions lever par par checkVat quand on lui envoi des caractères qui lui plaisent pas donc je conseille de faire :

    try
    {
    $result = $client->checkVat($params);
    }
    catch(Exception $e)
    {
    $error = array(‘result’ => false, ‘error’=>’invalid’);
    return $error;
    }

    • Article Author
  • Validation d’un numéro siret : sfValidatorSiret Blog Symfony – Lexik Montpellier

    […] comme vous avez pu le voir avec les récents posts, nous avons eu pas mal de gestion de facturation: conversion de devises, validation d’un numéro de tva intracommunautaire. […]

    • Article Author
  • Jmmm

    C’est cool les gars, vous faites du bon boulot ;]

    Merci !

    • Article Author
  • rasson

    Bonjour, et tt d’abord merci pour cet article.
    Je suis entrain d’implémenter cette fonction sur mon site (en php) et j’ai un petit soucis au moment ou je fais : « $result = $client->checkVat($params); »

    Soap est bien installé mais j’ai l’impression que la fonction checkVat() n’est pas reconnue.
    Fait-elle partie d’une class additionnel?

    Merci d’avance.
    Benoit.

    • Article Author
  • Hams

    Super script, vraiment génial, merci !

    • Article Author
  • Frédérick

    Bonjour,
    ce code semble génial, mais e n »arrive pas à le coordoner ave mon php.
    Quelqu’un peut me guider?

    Merci d’avance

    • Article Author
  • Philippe

    Bonjour et merci pour le code.

    Il semble que l’adresse du service ait changé.
    C’est désormais http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl – services a été enlevé entre vies et checkVatService.

    Sinon, après avoir activé l’extension SOAP de PHP, ça a marché du premier coup !

    Pour info, la référence de tout ça est à http://ec.europa.eu/taxation_customs/vies/vieshome.do – cf notamment la FAQ numéro 16.

    • Article Author
  • thomas

    @Philippe
    Merci pour ton message, le code a été mis à jour.

    Pour info il n’y a qu’une seule ligne à modifier:
    $client = new SoapClient(« http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl »);

    • Article Author
  • Stéphane

    Pour ma part l’implémentation et l’intégration de la nouvelle URL ont été rapides mais je bloque sur une réponse systématiquement négative de la part du webservice. Pourtant j’utilise un N° TVA valide ( FR69481884088 validé par le formulaire de l’UE), voici le contenu de response qui devrait être un tableau :
    « HTTP/1.1 200 OK Date: Wed, 16 Mar 2011 17:31:55 GMT Content-Type: text/xml Last-Modified: Wed, 24 Mar 2010 16:19:44 GMT Accept-Ranges: bytes Content-Length: 10382 Connection: close Specific disclaimer for this service —————————————– The objective of this Internet site is to allow persons involved in the intra-Community supply of goods or of services to obtain confirmation of the validity of the VAT identification number of any specified person, in accordance to article 27 of Council Regulation (EC) No. 1798/2003 of 7 October 2003. Any other use and any extraction and use of the data which is not in conformity with the objective of this site is strictly forbidden. Any retransmission of the contents of this site, whether for a commercial purpose or otherwise, as well as any more general use other than as far as is necessary to support the activity of a legitimate user (for example: to draw up their own invoices) is expressly forbidden. In addition, any copying or reproduction of the contents of this site is strictly forbidden. The European Commission maintains this website to enhance the access by taxable persons making intra-Community supplies to verification of their customers VAT identification numbers. Our goal is to supply instantaneous and accurate information. However the Commission accepts no responsibility or liability whatsoever with regard to the information obtained using this site. This information: – is obtained from Member States databases over which the Commission services have no control and for which the Commission assumes no responsibility; it is the responsibility of the Member States to keep their databases complete, accurate and up to date; – is not professional or legal advice (if you need specific advice, you should always consult a suitably qualified professional); – does not in itself give a right to exempt intra-Community supplies from Value Added Tax; – does not change any obligations imposed on taxable persons in relation to intra-Community supplies. It is our goal to minimise disruption caused by technical errors. However some data or information on our site may have been created or structured in files or formats which are not error-free and we cannot guarantee that our service will not be interrupted or otherwise affected by such problems. The Commission accepts no responsibility with regard to such problems incurred as a result of using this site or any linked external sites. This disclaimer is not intended to limit the liability of the Commission in contravention of any requirements laid down in applicable national law nor to exclude its liability for matters which may not be excluded under that law. Usage: The countryCode input parameter must follow the pattern [A-Z]{2} The vatNumber input parameter must follow the [0-9A-Za-z\+\*\.]{2,12} In case of problem, the returned FaultString can take the following specific values: – INVALID_INPUT: The provided CountryCode is invalid or the VAT number is empty; – SERVICE_UNAVAILABLE: The SOAP service is unavailable, try again later; – MS_UNAVAILABLE: The Member State service is unavailable, try again later or with another Member State; – TIMEOUT: The Member State service could not be reach in time, try again later or with another Member State; – SERVER_BUSY: The service can’t process your request. Try again latter. VALID INVALID »
    Si une ame charitable avait de quoi éclairer ma lanterne ….

    • Article Author
  • Stéphane

    Pour ma part l\’implémentation et l\’intégration de la nouvelle URL ont été rapides mais je bloque sur une réponse systématiquement négative de la part du webservice. Pourtant j\’utilise un N° TVA valide ( FR69481884088 validé par le formulaire de l\’UE), voici le contenu de response qui devrait être un tableau :
    \"HTTP/1.1 200 OK Date: Wed, 16 Mar 2011 17:31:55 GMT Content-Type: text/xml Last-Modified: Wed, 24 Mar 2010 16:19:44 GMT Accept-Ranges: bytes Content-Length: 10382 Connection: close Specific disclaimer for this service —————————————– The objective of this Internet site is to allow persons involved in the intra-Community supply of goods or of services to obtain confirmation of the validity of the VAT identification number of any specified person, in accordance to article 27 of Council Regulation (EC) No. 1798/2003 of 7 October 2003. Any other use and any extraction and use of the data which is not in conformity with the objective of this site is strictly forbidden. Any retransmission of the contents of this site, whether for a commercial purpose or otherwise, as well as any more general use other than as far as is necessary to support the activity of a legitimate user (for example: to draw up their own invoices) is expressly forbidden. In addition, any copying or reproduction of the contents of this site is strictly forbidden. The European Commission maintains this website to enhance the access by taxable persons making intra-Community supplies to verification of their customers VAT identification numbers. Our goal is to supply instantaneous and accurate information. However the Commission accepts no responsibility or liability whatsoever with regard to the information obtained using this site. This information: – is obtained from Member States databases over which the Commission services have no control and for which the Commission assumes no responsibility; it is the responsibility of the Member States to keep their databases complete, accurate and up to date; – is not professional or legal advice (if you need specific advice, you should always consult a suitably qualified professional); – does not in itself give a right to exempt intra-Community supplies from Value Added Tax; – does not change any obligations imposed on taxable persons in relation to intra-Community supplies. It is our goal to minimise disruption caused by technical errors. However some data or information on our site may have been created or structured in files or formats which are not error-free and we cannot guarantee that our service will not be interrupted or otherwise affected by such problems. The Commission accepts no responsibility with regard to such problems incurred as a result of using this site or any linked external sites. This disclaimer is not intended to limit the liability of the Commission in contravention of any requirements laid down in applicable national law nor to exclude its liability for matters which may not be excluded under that law. Usage: The countryCode input parameter must follow the pattern [A-Z]{2} The vatNumber input parameter must follow the [0-9A-Za-z\\+\\*\\.]{2,12} In case of problem, the returned FaultString can take the following specific values: – INVALID_INPUT: The provided CountryCode is invalid or the VAT number is empty; – SERVICE_UNAVAILABLE: The SOAP service is unavailable, try again later; – MS_UNAVAILABLE: The Member State service is unavailable, try again later or with another Member State; – TIMEOUT: The Member State service could not be reach in time, try again later or with another Member State; – SERVER_BUSY: The service can\’t process your request. Try again latter. VALID INVALID\"
    Si une ame charitable avait de quoi éclairer ma lanterne ….

    • Article Author
  • franck

    le script beug chez moi, il existe plus simple ?

    • Article Author

Recevez nos articles