Delphi Clinic C++Builder Gate Training & Consultancy Delphi Notes Weblog Dr.Bob's Webshop
Dr.Bob's Delphi Notes Dr.Bob's Delphi Clinics Dr.Bob's Delphi Courseware Manuals
 Dr.Bob Examines... #142
See Also: Dr.Bob's Delphi Papers and Columns
This article is translated to Serbo-Croatian language by Jovana Milutinovich from

Checking EU VAT numbers via SOAP
In this article, I'll describe how to check European VAT numbers using a SOAP Web Services with Delphi XE2. The same techniques can be used with older versions of Delphi as well (but I'm just using the latest-of-the-latest at the time of writing).

EU Rules
Before we start to "talk" technical, let me first explain the need: why do I need to check the validity of the VAT number of a company in Europe? This has to do with the European Union and the VAT rules for the EU.
As you all probably know, I'm a reseller of Embarcadero Delphi and RAD Studio (as well as other software tools such as Prism / Oxygene, RemObjects SDK, and Hydra from RemObjects Software, TMS IntraWeb Components from TMS Software, and Advantage Database Server from Sybase). Apart from these software tools, that are sold in electronic (ESD - Electronic Software Download) format, I also sell Delphi courseware manuals in PDF format.
For all electronic deliveries, I have to charge the local (Dutch) amount of 19% VAT for persons or companies in the European Union. Outside the EU, I do not have to charge VAT. However, I also do not have to charge 19% VAT for companies in the EU (except for my own country The Netherlands) that provide their full company name and VAT number. In these cases, I have to put their VAT number (as well as mine) on the invoice, and refer to EU rules for "VAT shifting". I also have to report these VAT numbers (and the amounts) to the local Dutch tax office every quarter (possibly so they can cross-reference it with the local tax office of the associated EU countries).
This is where is gets tricky: if I accidentally report an incorrect VAT number, I have to pay the VAT myself. So, to cut a long story short: it's important for me to verify the VAT number of every order from the EU to ensure that I do not have to charge 19% VAT.

VAT Number validation
The process of checking the validity of a VAT number is not that hard, but requires information from the different EU countries. There is a website that can can help, and offers an interacive online form to check the VAT number. However, that requires some manual input, which is not as easy (especially if the rest of the ordering and delivery process is automated).
Fortunately, the above website also publishes a SOAP web service that we can call. The WSDL for this web service is available at, and it contains a useful documentation tag with the following contents:

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.
With that information and the WSDL location, we can start building our VAR number checker...

VAT Number Validation Web Service
Start Delphi - I'm using XE2, but any recent version should be good enough. Since I'm using Delphi XE2, I'll create a FireMonkey application, so I can cross-compile the resulting client as a Mac OS X application and run it on my Mac Mini (just because I can).
So, after having started Delphi XE2, I do File | New - Other, and select the "FireMonkey HD Application" icon from the Object Repository:

This will produce a new FireMonkey HD Application. I've saved the form in MainForm.pas and the project itself in CheckVAT.dpr. The Caption of the MainForm is set to "CheckVAT", and I've placed a TEdit (called edVAT) and a TButton (called btnVAT) on the form:

Note that I made sure to place the TEdit and TButton on the Form, and not on each other (see the Structure View on the left side to confirm).

The next step consists of importing the Web Service. This is done using the WSDL Importer, found in the Object Repository. So, do File | New - Other again, and this time from the Web Services cateogory, double-click on the WSDL Importer:

In the dialog that follows, we can enter the WSDL URL, which is as mentioned before.

Now press Next, Next and Finish to produce the import unit for the checkVatPortType web service. The interface definition for checkVatPortType is as follows:

  checkVatPortType = interface(IInvokable)

    // Cannot unwrap:
    //     - More than one strictly out element was found
    function  checkVat(const parameters: checkVat): checkVatResponse; stdcall;

    // Cannot unwrap:
    //     - More than one strictly out element was found
    function  checkVatApprox(const parameters: checkVatApprox): checkVatApproxResponse; stdcall;

Never mind the "cannot unwrap" warnings, we can just create an instance of the "checkVat" input parameter and get the "checkVatResponse" back as result when calling the checkVat method.

The checkVat request structure is defined as follows:

  checkVat = class(TRemotable)
    FcountryCode: string;
    FvatNumber: string;
    constructor Create; override;
    property countryCode: string  read FcountryCode write FcountryCode;
    property vatNumber:   string  read FvatNumber write FvatNumber;

Note that we should enter two different fields here: the countryCode as well as the vatNumber. So my VAT number "NL 195846266.B01" becomes "NL" for the countryCode, and "195846266B01" (without the dot or space) as the vatNumber.

The checkVatResponse response structure is defined as follows:

  checkVatResponse = class(TRemotable)
    FcountryCode: string;
    FvatNumber: string;
    FrequestDate: TXSDate;
    Fvalid: Boolean;
    Fname_: string;
    Fname__Specified: boolean;
    Faddress: string;
    Faddress_Specified: boolean;
    procedure Setname_(Index: Integer; const Astring: string);
    function  name__Specified(Index: Integer): boolean;
    procedure Setaddress(Index: Integer; const Astring: string);
    function  address_Specified(Index: Integer): boolean;
    constructor Create; override;
    destructor Destroy; override;
    property countryCode: string   read FcountryCode write FcountryCode;
    property vatNumber:   string   read FvatNumber write FvatNumber;
    property requestDate: TXSDate  read FrequestDate write FrequestDate;
    property valid:       Boolean  read Fvalid write Fvalid;
    property name_:       string   Index (IS_OPTN or IS_NLBL)
      read Fname_ write Setname_ stored name__Specified;
    property address:     string   Index (IS_OPTN or IS_NLBL)
      read Faddress write Setaddress stored address_Specified;

  companyTypeCode =  type string;

Note that I'm not really interested in all these result fields. In fact, I only want to make sure the VAT number is a valid VAT number, and for that I should check the valid property (and optionally the vatNumber return field, as well as the address for example).

VAT Number Validation
The code to check the VAT number, in the OnClick event of the TButton, is as follows (note that we need to free both the "inp" and "outp" when we're done):

  procedure TFormVAT.btnVATClick(Sender: TObject);
    WS: checkVatPortType;
    inp: checkVat;
    outp: checkVatResponse;
    WS := GetcheckVatPortType;
    inp := checkVat.Create;
      inp.countryCode := Copy(edVAT.Text,1,Pos(#32,edVAT.Text)-1);
      inp.vatNumber := Copy(edVAT.Text,Pos(#32,edVAT.Text)+1,255);
      outp := WS.checkVat(inp);
      if outp.valid then
        ShowMessage('OK: ' + outp.name_ + #13#10 + outp.address)
      else ShowMessage(outp.vatNumber + ' not OK')

And the result can be seen below (just before finishing the complete VAT number and clicking on the "Check VAT" button):

Delphi XE2 SOAP Clients can compile and work on Windows as well as Mac OS X. And since I made a FireMonkey HD Application, it's easy to add another Platform Target to the project group, and recompile the application for Mac OS X. Running on my Mac Mini, the resulting project can be seen below:

Using this little application, I can now verify VAT numbers on Windows and Mac OS X, calling the VAT number web service.

In this article, I've described how to check European VAT numbers using a SOAP Web Services with Delphi XE2. I also demonstrated that Delphi XE2 SOAP clients can compile and work in both Windows and Mac OS X.

This article is translated to Serbo-Croatian language by Jovana Milutinovich from, and it's translated to the Punjabi language by the Bydiscountcodes Team.

This webpage © 2011-2018 by Bob Swart (aka Dr.Bob - All Rights Reserved.