Skip to content

Commit

Permalink
implement #4: add support for TIN database
Browse files Browse the repository at this point in the history
  • Loading branch information
syjer committed Apr 2, 2021
1 parent 6fd479b commit 9db9e97
Show file tree
Hide file tree
Showing 11 changed files with 538 additions and 168 deletions.
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# vatchecker: a basic java library for fetching VAT information from the VIES webservice
# vatchecker: a basic java library for fetching VAT information from the VIES webservice and TIN webservice

[![Maven Central](https://img.shields.io/maven-central/v/ch.digitalfondue.vatchecker/vatchecker.svg)](http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22vatchecker%22)
[![Build Status](https://img.shields.io/github/workflow/status/digitalfondue/vatchecker/Java%20CI%20with%20Maven)](https://github.com/digitalfondue/vatchecker/actions?query=workflow%3A%22Java+CI+with+Maven%22)


A small java client for calling the European "VAT Information Exchange System" (VIES) webservice for validating the VAT numbers. See http://ec.europa.eu/taxation_customs/vies/ .
A small java client for calling
- the European "VAT Information Exchange System" (VIES) webservice for validating the VAT numbers. See http://ec.europa.eu/taxation_customs/vies/ .
- the European "TIN" webservice. See https://ec.europa.eu/taxation_customs/tin/ .

## License

Expand All @@ -30,6 +32,7 @@ compile 'ch.digitalfondue.vatchecker:vatchecker:1.4.3'

## Use

### VIES
As a static method:

```java
Expand Down Expand Up @@ -57,3 +60,17 @@ You can use your own data fetcher if customization is needed, see:

- https://github.com/digitalfondue/vatchecker/blob/master/src/main/java/ch/digitalfondue/vatchecker/EUVatChecker.java#L183
- https://github.com/digitalfondue/vatchecker/blob/master/src/main/java/ch/digitalfondue/vatchecker/EUVatChecker.java#L67

### TIN

As a static method:

```java
EUTinCheckResponse resp = EUTinChecker.doCheck("BE", "00012511119");
Assert.assertEquals(true, resp.isValidStructure());
Assert.assertEquals(true, resp.isValidSyntax());
```

Like the VIES counterpart, you can see the tests for all the possibile outputs:

- https://github.com/digitalfondue/vatchecker/blob/master/src/test/java/ch/digitalfondue/vatchecker/EUTinCheckerTest.java
12 changes: 10 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>3.0</version>
<version>4.0</version>
<configuration>
<header>com/mycila/maven/plugin/license/templates/APACHE-2.txt</header>
<header>template/APACHE-2.txt</header>
<includes>
<include>**/*.java</include>
</includes>
Expand All @@ -135,6 +135,14 @@
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin-git</artifactId>
<!-- make sure you use the same version as license-maven-plugin -->
<version>4.0</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.eluder.coveralls</groupId>
Expand Down
44 changes: 44 additions & 0 deletions src/main/java/ch/digitalfondue/vatchecker/BaseFault.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
*
* Copyright © 2018-2021 digitalfondue (info@digitalfondue.ch)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.digitalfondue.vatchecker;

import java.util.function.Function;

abstract class BaseFault<T extends Enum<T>> {

private final String faultCode;
private final String fault;
private final T faultType;

protected BaseFault(String faultCode, String fault, Function<String, T> converter, T defaultValue) {
this.faultCode = faultCode;
this.fault = fault;
this.faultType = Utils.tryParse(fault, converter, defaultValue);
}

public String getFault() {
return fault;
}

public String getFaultCode() {
return faultCode;
}

public T getFaultType() {
return faultType;
}
}
64 changes: 64 additions & 0 deletions src/main/java/ch/digitalfondue/vatchecker/EUTinCheckResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
*
* Copyright © 2018-2021 digitalfondue (info@digitalfondue.ch)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.digitalfondue.vatchecker;

public class EUTinCheckResponse {

private final boolean validSyntax;
private final boolean validStructure;

private final boolean error;
private final Fault fault;

EUTinCheckResponse(boolean validSyntax, boolean validStructure, boolean error, Fault fault) {
this.validSyntax = validSyntax;
this.validStructure = validStructure;
this.error = error;
this.fault = fault;
}

public boolean isValidSyntax() {
return validSyntax;
}

public boolean isValidStructure() {
return validStructure;
}

public boolean isError() {
return error;
}

public Fault getFault() {
return fault;
}

public static class Fault extends BaseFault<FaultType> {
Fault(String faultCode, String fault) {
super(faultCode, fault, FaultType::valueOf, FaultType.OTHER);
}
}


public enum FaultType {
INVALID_INPUT,
NO_INFORMATION,
SERVICE_UNAVAILABLE,
SERVER_BUSY,
OTHER
}
}
133 changes: 133 additions & 0 deletions src/main/java/ch/digitalfondue/vatchecker/EUTinChecker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
*
* Copyright © 2018-2021 digitalfondue (info@digitalfondue.ch)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.digitalfondue.vatchecker;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

import javax.xml.xpath.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Objects;
import java.util.function.BiFunction;

/**
* A small utility for calling the TIN webservice. See https://ec.europa.eu/taxation_customs/tin/ .
*
* The main entry points are {@link #doCheck(String, String)} and if more customization is needed {@link #doCheck(String, String, BiFunction)}.
*/
public class EUTinChecker {

private static final String ENDPOINT = "https://ec.europa.eu/taxation_customs/tin/services/checkTinService";
private final BiFunction<String, String, InputStream> documentFetcher;

private static final Document BASE_DOCUMENT_TEMPLATE;
private static final XPathExpression VALID_ELEMENT_MATCHER;
private static final XPathExpression VALID_STRUCTURE_MATCHER;
private static final XPathExpression VALID_SYNTAX_MATCHER;

static {
String soapCallTemplate = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
"<soapenv:Header/>" +
"<soapenv:Body>" +
"<checkTin xmlns=\"urn:ec.europa.eu:taxud:tin:services:checkTin:types\">" +
"<countryCode></countryCode><tinNumber></tinNumber>" +
"</checkTin>" +
"</soapenv:Body>" +
"</soapenv:Envelope>";

BASE_DOCUMENT_TEMPLATE = Utils.toDocument(new StringReader(soapCallTemplate));
XPath xPath = XPathFactory.newInstance().newXPath();
try {
VALID_ELEMENT_MATCHER = xPath.compile("//*[local-name()='checkTinResponse']");
VALID_STRUCTURE_MATCHER = xPath.compile("//*[local-name()='checkTinResponse']/*[local-name()='validStructure']");
VALID_SYNTAX_MATCHER = xPath.compile("//*[local-name()='checkTinResponse']/*[local-name()='validSyntax']");
} catch (XPathExpressionException e) {
throw new IllegalStateException(e);
}
}

public EUTinChecker() {
this(Utils::doCall);
}

public EUTinChecker(BiFunction<String, String, InputStream> documentFetcher) {
this.documentFetcher = documentFetcher;
}

/**
* See {@link #doCheck(String, String)}.
*
* @param countryCode 2 character ISO country code. Note: Greece is EL, not GR.
* @param tinNr TIN number
* @return the response, see {@link EUTinCheckResponse}
*/
public EUTinCheckResponse check(String countryCode, String tinNr) {
return doCheck(countryCode, tinNr, this.documentFetcher);
}

/**
* Do a call to the EU tin checker web service.
*
* @param countryCode 2 character ISO country code. Note: Greece is EL, not GR.
* @param tinNr the tin number to check
* @return the response, see {@link EUTinCheckResponse}
*/
public static EUTinCheckResponse doCheck(String countryCode, String tinNr) {
return doCheck(countryCode, tinNr, Utils::doCall);
}

/**
* See {@link #doCheck(String, String)}. This method accept a documentFetcher if you need to customize the
* http client.
*
* @param countryCode 2 character ISO country code. Note: Greece is EL, not GR.
* @param tinNumber TIN number
* @param documentFetcher the function that, given the url of the web service and the body to post, return the resulting body as InputStream
* @return the response, see {@link EUTinCheckResponse}
*/
public static EUTinCheckResponse doCheck(String countryCode, String tinNumber, BiFunction<String, String, InputStream> documentFetcher) {
Objects.requireNonNull(countryCode, "countryCode cannot be null");
Objects.requireNonNull(tinNumber, "tinNumber cannot be null");
try {
HashMap<String, String> params = new HashMap<>();
params.put("countryCode", countryCode);
params.put("tinNumber", tinNumber);
String body = Utils.prepareTemplate(BASE_DOCUMENT_TEMPLATE, params);
try (InputStream is = documentFetcher.apply(ENDPOINT, body); Reader isr = new InputStreamReader(is, StandardCharsets.UTF_8)) {
Document result = Utils.toDocument(isr);
Node validNode = (Node) VALID_ELEMENT_MATCHER.evaluate(result, XPathConstants.NODE);
Node faultNode = (Node) Utils.SOAP_FAULT_MATCHER.evaluate(result, XPathConstants.NODE);
if (validNode != null) {
Node validStructure = (Node) VALID_STRUCTURE_MATCHER.evaluate(result, XPathConstants.NODE);
Node validSyntax = (Node) VALID_SYNTAX_MATCHER.evaluate(result, XPathConstants.NODE);
return new EUTinCheckResponse("true".equals(Utils.textNode(validSyntax)), "true".equals(Utils.textNode(validStructure)), false, null);
} else if (faultNode != null) {
Node faultCode = (Node) Utils.SOAP_FAULT_CODE_MATCHER.evaluate(result, XPathConstants.NODE);
Node faultString = (Node) Utils.SOAP_FAULT_STRING_MATCHER.evaluate(result, XPathConstants.NODE);
return new EUTinCheckResponse(false, false, true, new EUTinCheckResponse.Fault(Utils.textNode(faultCode), Utils.textNode(faultString)));
} else {
return new EUTinCheckResponse(false, false, true, null); // should not enter here in theory
}
}
} catch (IOException | XPathExpressionException e) {
throw new IllegalStateException(e);
}
}

}
35 changes: 5 additions & 30 deletions src/main/java/ch/digitalfondue/vatchecker/EUVatCheckResponse.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/**
* Copyright © 2018 digitalfondue (info@digitalfondue.ch)
/*
*
* Copyright © 2018-2021 digitalfondue (info@digitalfondue.ch)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -52,35 +53,9 @@ public Fault getFault() {
return fault;
}

public static class Fault {
private final String faultCode;
private final FaultType faultType;
private final String fault;

public static class Fault extends BaseFault<FaultType> {
Fault(String faultCode, String fault) {
this.faultCode = faultCode;
this.faultType = tryParse(fault);
this.fault = fault;
}

public String getFaultCode() {
return faultCode;
}

public FaultType getFaultType() {
return faultType;
}

public String getFault() {
return fault;
}
}

private static FaultType tryParse(String fault) {
try {
return FaultType.valueOf(fault);
} catch (IllegalArgumentException | NullPointerException e) {
return FaultType.OTHER;
super(faultCode, fault, FaultType::valueOf, FaultType.OTHER);
}
}

Expand Down
Loading

0 comments on commit 9db9e97

Please sign in to comment.