11

I have an ASP.NET website that needs to check a user-supplied VAT. The VIES Service can be used for that which exposes a SOAP API.

I need a dead-simple example on how to validate a VAT using this service. In PHP, it's these 4 lines: https://stackoverflow.com/a/14340495. For C#, I have found some articles from 2010 that do not work or are tens or even hundreds lines of "wrappers", "helper services" etc.

I don't need any of that, can someone provide PHP-like four-liner that would check VAT in C#? Thank you.

Community
  • 1
  • 1
Borek Bernard
  • 50,745
  • 59
  • 165
  • 240

6 Answers6

12

Here is a self-sufficient (no WCF, no WSDL, ...) utility class that will check the VAT number and get information on the company (name and address). It will return null if the VAT number is invalid or if any error occurred.

Update 2022/10/17 : I have added namespace handling to parse the response as it seems the EU web service was modified. If you need the old version, just look at the history

// sample calling code
Console.WriteLine(EuropeanVatInformation.Get("FR89831948815"));

...

public class EuropeanVatInformation
{
    private EuropeanVatInformation() { }

    public string CountryCode { get; private set; }
    public string VatNumber { get; private set; }
    public string Address { get; private set; }
    public string Name { get; private set; }
    public override string ToString() => CountryCode + " " + VatNumber + ": " + Name + ", " + Address.Replace("\n", ", ");

    public static EuropeanVatInformation Get(string countryCodeAndVatNumber)
    {
        if (countryCodeAndVatNumber == null)
            throw new ArgumentNullException(nameof(countryCodeAndVatNumber));

        if (countryCodeAndVatNumber.Length < 3)
            return null;

        return Get(countryCodeAndVatNumber.Substring(0, 2), countryCodeAndVatNumber.Substring(2));
    }

    public static EuropeanVatInformation Get(string countryCode, string vatNumber)
    {
        if (countryCode == null)
            throw new ArgumentNullException(nameof(countryCode));

        if (vatNumber == null)
            throw new ArgumentNullException(nameof(vatNumber));

        countryCode = countryCode.Trim();
        vatNumber = vatNumber.Trim().Replace(" ", string.Empty);

        const string ns = "urn:ec.europa.eu:taxud:vies:services:checkVat:types";
        const string url = "http://ec.europa.eu/taxation_customs/vies/services/checkVatService";
        const string xml = @"<s:Envelope xmlns:s='http://schemas.xmlsoap.org/soap/envelope/'><s:Body><checkVat xmlns='" + ns + "'><countryCode>{0}</countryCode><vatNumber>{1}</vatNumber></checkVat></s:Body></s:Envelope>";

        try
        {
            using (var client = new WebClient())
            {
                var doc = new XmlDocument();
                doc.LoadXml(client.UploadString(url, string.Format(xml, countryCode, vatNumber)));
                var response = doc.SelectSingleNode("//*[local-name()='checkVatResponse']") as XmlElement;
                if (response == null || response["valid", ns]?.InnerText != "true")
                    return null;

                var info = new EuropeanVatInformation
                {
                    CountryCode = response["countryCode", ns].InnerText,
                    VatNumber = response["vatNumber", ns].InnerText,
                    Name = response["name", ns]?.InnerText,
                    Address = response["address", ns]?.InnerText
                };
                return info;
            }
        }
        catch
        {
            return null;
        }
    }
}
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • 2
    Three years on, this was the most reliable answer for me. Thanks! – Steve Owen Jan 06 '21 at 17:12
  • 1
    This is also a valid approach if you are deploying to OpenShift. We had issues using connected services in this hosting scenario. Going "raw" path works. – Michal Ja Aug 06 '21 at 11:08
  • Trying to use this anwser and for the most part it works. I mean I gets the result from the service as expected but I is not able to resolve the response["valid"]?.InnerText != "true". There is a 'valid' element in the response but code still evaluates to false. Why is that? It is as if the 'response' does not match the expected result? – Holm76 Aug 16 '22 at 12:29
  • @Holm76 - It's a late answer to your comment but I have updated the answer. – Simon Mourier Oct 17 '22 at 09:08
  • 1
    I want to give this another upvote nearly two years on because I've spent the last two hours trying to figure out why this had stopped working, and found your answer again. Thanks for the recent fix! – Steve Owen Nov 15 '22 at 10:39
11

The simplest way I found is just to send an XML and parse it when it comes back:

var wc = new WebClient();
var request = @"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:urn=""urn:ec.europa.eu:taxud:vies:services:checkVat:types"">
    <soapenv:Header/>
    <soapenv:Body>
      <urn:checkVat>
         <urn:countryCode>COUNTRY</urn:countryCode>
         <urn:vatNumber>VATNUMBER</urn:vatNumber>
      </urn:checkVat>
    </soapenv:Body>
    </soapenv:Envelope>";

request = request.Replace("COUNTRY", countryCode);
request = request.Replace("VATNUMBER", theRest);

String response;
try
{
    response = wc.UploadString("http://ec.europa.eu/taxation_customs/vies/services/checkVatService", request);
}
catch
{
    // service throws WebException e.g. when non-EU VAT is supplied
}

var isValid = response.Contains("<valid>true</valid>");
Vland
  • 4,151
  • 2
  • 32
  • 43
Borek Bernard
  • 50,745
  • 59
  • 165
  • 240
6

On .NET platform it is common to consume a Web service so that we generate a proxy class. This can usually be done using Visual Studio "Add Web Reference" where you just fill in a path to the WSDL. An alternative is to generate source classes using wsdl.exe or svcutil.exe.

Then just consume this class and verifying VAT becomes a one-liner:

DateTime date = new checkVatPortTypeClient().checkVat(ref countryCode, ref vatNumber, out isValid, out name, out address);

Generating proxy provides strongly typed API to consume whole service and we don't need to manually create soap envelope and parse output text. It is much more easier, safer and generic solution than yours.

Community
  • 1
  • 1
Pavel Hodek
  • 14,319
  • 3
  • 32
  • 37
  • I guess this answer is outdated since the service does not provide a checkVat function anymore. – Verbe May 04 '17 at 19:02
  • 1
    @Ben So the OP answer is outdated too. But my answer is still valid because you can simply regenerate proxy class and properly update calling code. – Pavel Hodek May 29 '17 at 08:24
5

Updated: I've published this as a NuGet library.

https://github.com/TriggerMe/CSharpVatChecker

var vatQuery = new VATQuery();
var vatResult = await vatQuery.CheckVATNumberAsync("IE", "3041081MH"); // The Squarespace VAT Number

Console.WriteLine(vatResult.Valid); // Is the VAT Number valid?
Console.WriteLine(vatResult.Name);  // Name of the organisation
James Woodall
  • 725
  • 7
  • 15
1

Based on Pavel Hodek's:

  1. Make sure you have the Microsoft WCF Web Service Reference Provide extension installed for visual studio (I'm using VS 2017 Community).
  2. In the solution explorer right click on Connected Services > Add Connected Service
  3. Select the WCF extension.
  4. Type in the URL provided by VIES http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl to generate the Service class from the wsdl.

  5. Press Go and select the service, give a proper name for the namespace e.g. Services.VATCheck

  6. Press Finish, which will create a new folder and a file called reference.cs in the Connected Services rename the file to VATCheck which will also rename the Class.

In a controller use the following code to invoke the call, make sure it's async (it can take awhile to eventually load all the data)

    public async Task<IActionResult> CheckVAT()
    {
        var countryCode = "BE";
        var vatNumber = "123456789";

        try
        {
            checkVatPortType test = new checkVatPortTypeClient(checkVatPortTypeClient.EndpointConfiguration.checkVatPort, "http://ec.europa.eu/taxation_customs/vies/services/checkVatService");
            checkVatResponse response = await test.checkVatAsync(new checkVatRequest { countryCode = countryCode, vatNumber = vatNumber });
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.Message);
        }

        return Ok();
    }

Please note that you can clean-up this call, but that's totally up to you.

Verbe
  • 634
  • 7
  • 13
  • probably the best answer as it uses the supported method provided by the EU who is providing the wsdl. this highlights the documentation that's missing stating you need to provide the port and url, this is where all the developers get stuck – Walter Verhoeven Mar 24 '20 at 08:45
0
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BTWCheck.eu.europa.ec;    

namespace BTWCheck
{
     class Program
    {
        static void Main(string[] args)
        {
            // VS 2017
            // add service reference -> button "Advanced" -> button "Add Web Reference" ->
            // URL = http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl 

            string Landcode = "NL";
            string BTWNummer = "820471616B01"; // VAT nr BOL.COM 

            checkVatService test = new checkVatService();
            test.checkVat(ref Landcode, ref BTWNummer, out bool GeldigBTWNr, out string Naam, out string Adres);

            Console.WriteLine(Landcode + BTWNummer + " " + GeldigBTWNr);
            Console.WriteLine(Naam+Adres);
            Console.ReadKey();

        }
    }
}
Joep
  • 1