38

How to incorporate a form on our website to validate the VIES? I find information to validate it through the website of the European Union.

http://ec.europa.eu/taxation_customs/vies/vieshome.do

And what interest me is validate directly from the form of payment data from my website.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Minucioso
  • 587
  • 1
  • 5
  • 11
  • I'm not sure if you still require it, but I have an extensive composer package build that will allow you to validate VAT ID's before you send it over to the EU VIES service, offers heartbeat service to see if the SOAP service is available and even offers service-side validation of trader name, company type, address, postcode and city. Fully tested with PHPUnit and 100% code coverage. See [dragonbe/vies](https://github.com/DragonBe/vies) for details. – DragonBe Oct 21 '18 at 14:17
  • @DragonBe funny enough, but right now we require PHP 5.4, yours is 7.1+ :-/ – Kevin Kopf Nov 16 '18 at 21:14
  • Just a quick remark, as the webpage is constantly changing: If you want to get tthe CONSULTATION NUMBER (code) as well, you must provide both the requester and the member VAT numbers, they can be the same. If you provide only the "memberStateCode" and "number" fields, you wont get the consultation number. This is important as you must provide this to the tax authorities on some cases to prove that you actually checked the number. So i strongly advice to get the HTTPS content and save the html for future use with the consultation number. (you can bzip it to save space) – Gipsz Jakab Nov 03 '21 at 03:52

11 Answers11

105

Actually, the VIES database can be queried via their API.
They support only the SOAP protocol, but that should be sufficient.

Here's a simple example:

$client = new SoapClient("http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl");
var_dump($client->checkVat(array(
  'countryCode' => $countryCode,
  'vatNumber' => $vatNo
)));

Here's the WSDL: http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl

There are multiple providers of APIs which are based on the original one, but provide it using different protocols. Simply told, they act like translators - using json with your application and connect using SOAP to the original API. These have a major problem with the connection timeout.

There are times where the VIES database is responding slowly, thus requiring more time to return a response. This should be considered while designing your application.

Nikola Petkanski
  • 4,724
  • 1
  • 33
  • 41
  • I've been searching for a official documentation on this soap api, do have a link to the docu? – Pablo May 21 '14 at 12:59
  • 1
    Well, if you open the link in a browser you can check the content of the "xsd:documentation" node. – Nikola Petkanski Jun 18 '14 at 16:43
  • http://ec.europa.eu/taxation_customs/vies/faq.html#item_16 - Q16 has some info on the SOAP interface. – kasimir Oct 30 '14 at 12:48
  • 10
    Shocking reliability of this service! Trying to implement to comply with the new EU VAT laws, but get "SERVER_BUSY" errors all the time. – Andrew Newby Jan 15 '15 at 09:28
  • 9
    You might expect this SOAP interface to just check a central database of VAT numbers but that doesn't exist. Every country manages its own database and the European Commission is just dispatching queries. The queries don't even stay on web services all the way, they're carried over a legacy Customs network (CCN/CSI) and the remote system can be really awful in some countries. It even happens that an entire country drops out of the system for days or even weeks ! – Eric Darchis Nov 05 '15 at 10:56
  • 1
    Is there a way to connect with SSL? – Blackbam Apr 01 '16 at 16:47
  • I am not aware of one. You may send them a question at taxud-viesweb@ec.europa.eu. Please, post the reply you were given. – Nikola Petkanski Apr 07 '16 at 16:28
  • this "service" is just annoying crapy. – Steve Dec 03 '18 at 10:58
  • Update 2022: The service stopped working for me a while ago. I was only able to find a new link: http://ec.europa.eu/taxation_customs/vies/checkVatTestService.wsdl but the accepted params seem to have changed too and I cannot get it to work. Has anyone experienced something similar? – S. Tea Oct 04 '22 at 13:32
30

If for some reasons you can't use SOAP on your server (no available, whatever) then file_get_contents is your friend.

The implementation below does not depend on SOAP, Curl, XMLParser (Simple or not). It is standard PHP code that should work on any PHP version you may have.

The function returns the following items:

  • countryCode
  • vatNumber
  • requestDate
  • valid
  • name
  • address

Well, I hope it helps :-)

<?php
DEFINE ( 'VIES_URL', 'http://ec.europa.eu/taxation_customs/vies/services/checkVatService' );

/**
 * VIES VAT number validation
 *
 * @author Eugen Mihailescu
 *        
 * @param string $countryCode           
 * @param string $vatNumber         
 * @param int $timeout          
 */
function viesCheckVAT($countryCode, $vatNumber, $timeout = 30) {
    $response = array ();
    $pattern = '/<(%s).*?>([\s\S]*)<\/\1/';
    $keys = array (
            'countryCode',
            'vatNumber',
            'requestDate',
            'valid',
            'name',
            'address' 
    );

    $content = "<s11:Envelope xmlns:s11='http://schemas.xmlsoap.org/soap/envelope/'>
  <s11:Body>
    <tns1:checkVat xmlns:tns1='urn:ec.europa.eu:taxud:vies:services:checkVat:types'>
      <tns1:countryCode>%s</tns1:countryCode>
      <tns1:vatNumber>%s</tns1:vatNumber>
    </tns1:checkVat>
  </s11:Body>
</s11:Envelope>";

    $opts = array (
            'http' => array (
                    'method' => 'POST',
                    'header' => "Content-Type: text/xml; charset=utf-8; SOAPAction: checkVatService",
                    'content' => sprintf ( $content, $countryCode, $vatNumber ),
                    'timeout' => $timeout 
            ) 
    );

    $ctx = stream_context_create ( $opts );
    $result = file_get_contents ( VIES_URL, false, $ctx );

    if (preg_match ( sprintf ( $pattern, 'checkVatResponse' ), $result, $matches )) {
        foreach ( $keys as $key )
            preg_match ( sprintf ( $pattern, $key ), $matches [2], $value ) && $response [$key] = $value [2];
    }
    return $response;
}

print_r ( viesCheckVAT ( 'RO', '19386256' ) );
?>
Eugen Mihailescu
  • 3,553
  • 2
  • 32
  • 29
24

They have recently (Sept. 2022) been able to implement a new REST API. I found that out by inspecting the request of their own web form. The API URL is:

https://ec.europa.eu/taxation_customs/vies/rest-api/ms/[country]/vat/[vat]

So for example Vat ID = DE122268496

https://ec.europa.eu/taxation_customs/vies/rest-api/ms/DE/vat/122268496

Result:

{
  "isValid" : true,
  "requestDate" : "2022-09-15T13:02:47.009Z",
  "userError" : "VALID",
  "name" : "---",
  "address" : "---",
  "requestIdentifier" : "",
  "vatNumber" : "122268496",
  "viesApproximate" : {
    "name" : "---",
    "street" : "---",
    "postalCode" : "---",
    "city" : "---",
    "companyType" : "---",
    "matchName" : 3,
    "matchStreet" : 3,
    "matchPostalCode" : 3,
    "matchCity" : 3,
    "matchCompanyType" : 3
  }
}
ESP32
  • 8,089
  • 2
  • 40
  • 61
4

This websites sends an ordinary HTML form to a server.

The simplest solution is to pass the params in the URL and use file_get_contents to get the response.

The response can then be parsed to extract the information you want. Not tested, but shows the idea:

$country1 = 'PL';
$country2 = 'PL';
$vatnum1 = '123456';
$vatnum2 = '789012';

//Prepare the URL
$url = 'http://ec.europa.eu/taxation_customs/vies/viesquer.do?ms='.$country1.'&iso='.$country1.'&vat='.$vatnum1.'&name=&companyType=&street1=&postcode=&city=&requesterMs='.$country2.'&requesterIso='.$country2.'&requesterVat='.$vatnum2.'&BtnSubmitVat=Verify';

$response = file_get_contents($url);
// Do sth with the response
echo $response;
Mariusz Jamro
  • 30,615
  • 24
  • 120
  • 162
  • Thanks a lot, i used the function preg_match on $response to compare if the number is valid. Now i need a valid number for try if it really works. The CODE: if (preg_match("/No, invalid VAT number/i", "$response")) { echo "Invalid number."; } else { echo "Valid number."; } – Minucioso Feb 06 '12 at 11:23
  • 1
    This looks easy enough. However if their website ever changes it will break. Is there any official API? – Johan van den Broek Mar 22 '12 at 14:37
  • 11
    @Johan This article describes how to use the VIES VAT SOAP web service: http://www.webmastersdiary.com/2011/12/php-vies-vat-number-validation-european.html – Les Carbonaro Jul 06 '12 at 11:26
  • 1
    @Les Carbonaro That is a very good reply. I would recommend that approach over the original answer. – Johan van den Broek Jan 08 '13 at 10:49
  • 3
    @LesCarbonaro here an updated Link: http://www.webmastersdiary.com/blog/php-vies-vat-number-validation-european-vat-id/ (sorry, but the blog got some updates since then) – ptmr.io Apr 28 '14 at 13:50
  • so for what do we need the key and the identifier – Ananthu K Kumar Jul 18 '23 at 14:18
2

Here is a full stack validation javascript based validation script with regex + modulo per country:

http://www.braemoor.co.uk/software/vat.shtml

Download link: http://www.braemoor.co.uk/software/downloads/jsvat.zip

Using webservices is sometime not an option.

Also see PHP ports for EU VAT validation:

EDIT: Please note, that links above refer to unofficial validation libraries. That means some algos are not officially published, other may change in time. Libraries referred above might be helpful if utilizing official VIES webservices is not possible (e.g. intranet without online access, VIES offline etc.). If the validation using these libs passes then EU VAT has a good chance say 90-95% that it could be valid, but definitely not 100%. Only VIES can ensure 100% validity.

lubosdz
  • 4,210
  • 2
  • 29
  • 43
  • 1
    Better use https://github.com/se-panfilov/jsvat for it - it's based on braemoor's lib, but rewrite with modern standarts and tests, and also addede to bower and npm – S Panfilov Feb 18 '16 at 04:20
  • 2
    Your lib claims to make math magic to determine which VAT Nr is valid and which isn't. This means that (a) it relies on non-official VAT algorithms, as they are not officially published (some are confidential), and they could become obsolete and (b) the fact that a number is valid does not mean that it is actually allocated. Only the official VIES service can check that, because it goes to the Member States' databases. I'm not saying you did a bad job, but you should be clearer about the limitations :) – Xr. Feb 06 '17 at 10:17
2

This is a composer package, which wraps the VIES WebService. You can use this fast and easy: https://github.com/drahosistvan/vatvalidation

ISTI
  • 201
  • 1
  • 4
1

You can use now js lib for european VAT validation:

jsvat - small library to check validity of EU VAT number

Based on braemoor's vat lib, but rewrite with modern js standarts (braemoor, for exapmpe stil use .eval()) and added to bower and npm registry. Also with unit tests.

bower i jsvat --save

or

npm i jsvat --save

S Panfilov
  • 16,641
  • 17
  • 74
  • 96
1

I needed this in ruby, so here is my code

  • gem install savon
  • add this method to your controller

    def validateVAT
      # create a client for the service
      client = Savon.client(wsdl: 'http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl')
      response = client.call(:check_vat, message: {countryCode: params[:country_code], vatNumber: params[:vat]})
      render json: { valid: response.body[:check_vat_response][:valid] }, status: :ok
    end
    
Ivan Stojkovic
  • 653
  • 1
  • 8
  • 15
0

I used the REST service at http://vatid.eu, but since they were blocked by VIES (I guess due to high volume of requests), I have switched to http://apilayer.net/.

The free plan is 100 requests per month, so I have implemented a small cache and I generate my own table of valid VAT numbers (renewable every year), so I don't spend requests checking the same VAT number more than once. Suits my needs ;)

Tarrakis
  • 371
  • 3
  • 12
  • 3
    FYI it's blocked because they're trying to monetise the data. From the ToS: "Any retransmission of the contents of this site, whether for a commercial purpose or otherwise [...] is expressly forbidden." – Xr. Feb 24 '17 at 07:38
  • 2
    That’s odd... vatid.eu was free, and now they are blocked. vatlayer.com are currently monetizing, and they are running. Why could that be? – Tarrakis Feb 27 '17 at 19:10
0

Here my solution in node:

import axios from "axios";
import { JSDOM } from "jsdom";

export async function checkVatNumber(vatIdentificationNumber: string) {
  const regexVat = new RegExp(/([A-Z]{2})([0-9A-Za-z\+\*\.]{2,12})/);
  const match = vatIdentificationNumber.match(regexVat);

  if (!match || match.length !== 3) return null;

  const [fullVat, countryCode, vatNumber] = match;

  let xmls = `
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
      <soapenv:Header/>
      <soapenv:Body>
      <tns1:checkVatApprox xmlns:tns1="urn:ec.europa.eu:taxud:vies:services:checkVat:types">
        <tns1:countryCode>${countryCode}</tns1:countryCode>
        <tns1:vatNumber>${vatNumber}</tns1:vatNumber>
      </tns1:checkVatApprox>
      </soapenv:Body>
    </soapenv:Envelope>
  `;

  const response = await axios.post(
    "http://ec.europa.eu/taxation_customs/vies/services/checkVatService",
    xmls,
    { headers: { "Content-Type": "text/xml" } }
  );

  const dom = new JSDOM(response.data, { contentType: "application/xml" });
  const valid = dom.window.document.querySelector("valid")?.textContent;
  const traderName =
    dom.window.document.querySelector("traderName")?.textContent;
  const traderCompanyType =
    dom.window.document.querySelector("traderCompanyType")?.textContent;
  const traderAddress =
    dom.window.document.querySelector("traderAddress")?.textContent;

  return { fullVat, valid, traderName, traderCompanyType, traderAddress };
}

Usage:

const vatData = await checkVatNumber("IE6388047V");
console.log({ vatData });

// vatData: {
//   fullVat: 'IE6388047V',
//   valid: 'true',
//   traderName: 'GOOGLE IRELAND LIMITED',
//   traderCompanyType: '---',
//   traderAddress: '3RD FLOOR, GORDON HOUSE, BARROW STREET, DUBLIN 4'
// }

You have to install these dependencies:

npm install axios jsdom
npm i --save-dev @types/jsdom // <-- if using Typescript

Obviously you want to add some error handling here and there but you get the gist ;)

Binajmen
  • 486
  • 2
  • 6
  • 18
-2

There is also a API for the VIES database. https://vatlayer.com/documentation.

I'm looking for a work around for splitting the address information. Now you can get it but it is one string.

Wouter Schoofs
  • 966
  • 9
  • 13