Validar Números de IVA Europeus
Verifique números de IVA europeus no checkout para ativar isenções fiscais B2B e prevenir fraude usando a API de validação de IVA Veille.
Visão geral
As empresas da UE estão isentas de IVA ao comprar a outras empresas da UE — mas apenas com um número de IVA válido. Este guia mostra como validar um número de IVA no checkout e recuperar os detalhes da empresa associada (nome e morada) da base de dados VIES.
Pré-requisitos
- Uma chave de API Veille — obtenha uma em app.veille.io
- Instale as dependências para o seu idioma:
pip install requestsSem dependências adicionais — utiliza a API fetch nativa (Node 18+).
Extensão curl ativada (ativa por padrão).
Sem dependências adicionais — utiliza net/http (Go 1.18+).
Sem dependências adicionais — utiliza java.net.http (Java 11+).
Sem dependências adicionais — utiliza System.Net.Http (.NET 6+).
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"Passos
Validar um número de IVA
Chame GET /v1/vat com o parâmetro vat. O número deve incluir o prefixo do país (ex: PT123456789).
import requests
API_KEY = "YOUR_API_KEY"
response = requests.get(
"https://api.veille.io/v1/vat",
headers={"x-api-key": API_KEY},
params={"vat": "PT123456789"},
)
result = response.json()
print(result)const API_KEY = "YOUR_API_KEY";
const params = new URLSearchParams({ vat: "PT123456789" });
const response = await fetch(`https://api.veille.io/v1/vat?${params}`, {
headers: { "x-api-key": API_KEY },
});
const result = await response.json();
console.log(result);<?php
$apiKey = "YOUR_API_KEY";
$params = http_build_query(["vat" => "PT123456789"]);
$ch = curl_init("https://api.veille.io/v1/vat?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($result);package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.veille.io/v1/vat?vat=PT123456789", nil)
req.Header.Set("x-api-key", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]any
json.Unmarshal(body, &result)
fmt.Println(result)
}import java.net.URI;
import java.net.http.*;
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.veille.io/v1/vat?vat=PT123456789"))
.header("x-api-key", "YOUR_API_KEY")
.GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());using System.Net.Http;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");
var body = await client.GetStringAsync("https://api.veille.io/v1/vat?vat=PT123456789");
Console.WriteLine(body);#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let result = reqwest::Client::new()
.get("https://api.veille.io/v1/vat")
.header("x-api-key", "YOUR_API_KEY")
.query(&[("vat", "PT123456789")])
.send().await?.json::<serde_json::Value>().await?;
println!("{:#?}", result);
Ok(())
}Ler os detalhes da empresa
Um número válido devolve is_valid: true, company_name, company_address e country_code.
if result.get("is_valid"):
print(f"✅ Número de IVA válido")
print(f" Empresa : {result.get('company_name')}")
print(f" Morada : {result.get('company_address')}")
print(f" País : {result.get('country_code')}")
else:
print(f"❌ Número de IVA inválido: {result.get('error', 'Não encontrado no VIES')}")if (result.is_valid) {
console.log("✅ Número de IVA válido");
console.log(` Empresa : ${result.company_name}`);
console.log(` Morada : ${result.company_address}`);
console.log(` País : ${result.country_code}`);
} else {
console.log(`❌ Número de IVA inválido: ${result.error ?? "Não encontrado no VIES"}`);
}if ($result["is_valid"] ?? false) {
echo "✅ Número de IVA válido\n";
echo " Empresa : {$result['company_name']}\n";
echo " Morada : {$result['company_address']}\n";
echo " País : {$result['country_code']}\n";
} else {
echo "❌ Número de IVA inválido: " . ($result["error"] ?? "Não encontrado no VIES") . "\n";
}if result["is_valid"].(bool) {
fmt.Printf("✅ Número de IVA válido\n Empresa : %v\n Morada : %v\n País : %v\n",
result["company_name"], result["company_address"], result["country_code"])
} else {
errMsg := "Não encontrado no VIES"
if e, ok := result["error"].(string); ok { errMsg = e }
fmt.Println("❌ Número de IVA inválido:", errMsg)
}import org.json.*;
var r = new JSONObject(response.body());
if (r.getBoolean("is_valid")) {
System.out.printf("✅ Número de IVA válido%n Empresa : %s%n Morada : %s%n País : %s%n",
r.getString("company_name"), r.getString("company_address"), r.getString("country_code"));
} else {
System.out.println("❌ Número de IVA inválido: " + r.optString("error", "Não encontrado no VIES"));
}using System.Text.Json;
var r = JsonDocument.Parse(body).RootElement;
if (r.GetProperty("is_valid").GetBoolean())
{
Console.WriteLine("✅ Número de IVA válido");
Console.WriteLine($" Empresa : {r.GetProperty("company_name")}");
Console.WriteLine($" Morada : {r.GetProperty("company_address")}");
Console.WriteLine($" País : {r.GetProperty("country_code")}");
}
else
{
var error = r.TryGetProperty("error", out var e) ? e.GetString() : "Não encontrado no VIES";
Console.WriteLine($"❌ Número de IVA inválido: {error}");
}if result["is_valid"].as_bool().unwrap_or(false) {
println!("✅ Número de IVA válido");
println!(" Empresa : {}", result["company_name"].as_str().unwrap_or(""));
println!(" Morada : {}", result["company_address"].as_str().unwrap_or(""));
println!(" País : {}", result["country_code"].as_str().unwrap_or(""));
} else {
let err = result["error"].as_str().unwrap_or("Não encontrado no VIES");
println!("❌ Número de IVA inválido: {}", err);
}Construir um handler de checkout B2B com IVA
Retire o IVA do total da encomenda quando é fornecido um número de IVA empresarial europeu verificado.
import requests
API_KEY = "YOUR_API_KEY"
IVA_RATE = 0.23 # 23% IVA (Portugal)
def lookup_vat(number: str) -> dict:
r = requests.get("https://api.veille.io/v1/vat",
headers={"x-api-key": API_KEY}, params={"vat": number})
return r.json()
def calculate_checkout(subtotal: float, vat_number: str | None = None) -> dict:
iva_amount = subtotal * IVA_RATE
vat_info = None
if vat_number:
result = lookup_vat(vat_number)
if result.get("is_valid"):
iva_amount = 0 # Mecanismo de autoliquidação B2B — sem IVA cobrado
vat_info = {"empresa": result["company_name"], "pais": result["country_code"]}
else:
return {"error": "O número de IVA fornecido não é válido."}
total = subtotal + iva_amount
return {"subtotal": subtotal, "iva": iva_amount, "total": total, "empresa": vat_info}
print(calculate_checkout(100.00))
print(calculate_checkout(100.00, vat_number="PT123456789"))
print(calculate_checkout(100.00, vat_number="INVALIDO123"))const API_KEY = "YOUR_API_KEY";
const IVA_RATE = 0.23; // 23% IVA (Portugal)
async function lookupVat(number: string) {
const params = new URLSearchParams({ vat: number });
const res = await fetch(`https://api.veille.io/v1/vat?${params}`, {
headers: { "x-api-key": API_KEY },
});
return res.json();
}
async function calculateCheckout(subtotal: number, vatNumber?: string) {
let ivaAmount = subtotal * IVA_RATE;
let companyInfo: any = null;
if (vatNumber) {
const result = await lookupVat(vatNumber);
if (result.is_valid) {
ivaAmount = 0;
companyInfo = { empresa: result.company_name, pais: result.country_code };
} else {
return { error: "O número de IVA fornecido não é válido." };
}
}
return { subtotal, iva: ivaAmount, total: subtotal + ivaAmount, empresa: companyInfo };
}
console.log(await calculateCheckout(100));
console.log(await calculateCheckout(100, "PT123456789"));
console.log(await calculateCheckout(100, "INVALIDO123"));<?php
const IVA_RATE = 0.23; // 23% IVA (Portugal)
function lookupVat(string $apiKey, string $number): array {
$params = http_build_query(["vat" => $number]);
$ch = curl_init("https://api.veille.io/v1/vat?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
return $result;
}
function calculateCheckout(string $apiKey, float $subtotal, ?string $vatNumber = null): array {
$ivaAmount = $subtotal * IVA_RATE;
$empresa = null;
if ($vatNumber) {
$result = lookupVat($apiKey, $vatNumber);
if ($result["is_valid"] ?? false) {
$ivaAmount = 0;
$empresa = ["empresa" => $result["company_name"], "pais" => $result["country_code"]];
} else {
return ["error" => "Número de IVA inválido."];
}
}
return ["subtotal" => $subtotal, "iva" => $ivaAmount, "total" => $subtotal + $ivaAmount, "empresa" => $empresa];
}
print_r(calculateCheckout("YOUR_API_KEY", 100.0));
print_r(calculateCheckout("YOUR_API_KEY", 100.0, "PT123456789"));package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
const APIKey = "YOUR_API_KEY"
const IVARate = 0.23 // 23% IVA (Portugal)
func lookupVAT(number string) map[string]any {
req, _ := http.NewRequest("GET", "https://api.veille.io/v1/vat?vat="+number, nil)
req.Header.Set("x-api-key", APIKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var r map[string]any
json.Unmarshal(body, &r)
return r
}
func calculateCheckout(subtotal float64, vatNumber string) map[string]any {
ivaAmount := subtotal * IVARate
var empresa map[string]any
if vatNumber != "" {
result := lookupVAT(vatNumber)
if result["is_valid"].(bool) {
ivaAmount = 0
empresa = map[string]any{"empresa": result["company_name"], "pais": result["country_code"]}
} else {
return map[string]any{"error": "Número de IVA inválido."}
}
}
return map[string]any{"subtotal": subtotal, "iva": ivaAmount, "total": subtotal + ivaAmount, "empresa": empresa}
}
func main() {
fmt.Println(calculateCheckout(100, ""))
fmt.Println(calculateCheckout(100, "PT123456789"))
fmt.Println(calculateCheckout(100, "INVALIDO123"))
}import java.net.URI;
import java.net.http.*;
import org.json.*;
public class Main {
static HttpClient client = HttpClient.newHttpClient();
static String API_KEY = "YOUR_API_KEY";
static double IVA_RATE = 0.23; // 23% IVA (Portugal)
static JSONObject lookupVat(String number) throws Exception {
var req = HttpRequest.newBuilder()
.uri(URI.create("https://api.veille.io/v1/vat?vat=" + number))
.header("x-api-key", API_KEY).GET().build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
return new JSONObject(resp.body());
}
static JSONObject calculateCheckout(double subtotal, String vatNumber) throws Exception {
double ivaAmount = subtotal * IVA_RATE;
JSONObject empresa = null;
if (vatNumber != null && !vatNumber.isEmpty()) {
var result = lookupVat(vatNumber);
if (result.getBoolean("is_valid")) {
ivaAmount = 0;
empresa = new JSONObject().put("empresa", result.getString("company_name"))
.put("pais", result.getString("country_code"));
} else {
return new JSONObject().put("error", "Número de IVA inválido.");
}
}
return new JSONObject().put("subtotal", subtotal).put("iva", ivaAmount)
.put("total", subtotal + ivaAmount).put("empresa", empresa);
}
public static void main(String[] args) throws Exception {
System.out.println(calculateCheckout(100, null));
System.out.println(calculateCheckout(100, "PT123456789"));
System.out.println(calculateCheckout(100, "INVALIDO123"));
}
}using System.Net.Http;
using System.Text.Json;
const double IVA_RATE = 0.23; // 23% IVA (Portugal)
var apiKey = "YOUR_API_KEY";
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
async Task<JsonElement> LookupVat(string number)
{
var body = await client.GetStringAsync($"https://api.veille.io/v1/vat?vat={number}");
return JsonDocument.Parse(body).RootElement;
}
async Task<string> CalculateCheckout(double subtotal, string? vatNumber = null)
{
double ivaAmount = subtotal * IVA_RATE;
string? empresa = null;
if (vatNumber != null)
{
var result = await LookupVat(vatNumber);
if (result.GetProperty("is_valid").GetBoolean())
{ ivaAmount = 0; empresa = result.GetProperty("company_name").GetString(); }
else return """{"error":"Número de IVA inválido."}""";
}
return $"""{{ "subtotal": {subtotal}, "iva": {ivaAmount}, "total": {subtotal + ivaAmount}, "empresa": "{empresa}" }}""";
}
Console.WriteLine(await CalculateCheckout(100));
Console.WriteLine(await CalculateCheckout(100, "PT123456789"));
Console.WriteLine(await CalculateCheckout(100, "INVALIDO123"));use reqwest::Client;
use serde_json::{json, Value};
const IVA_RATE: f64 = 0.23; // 23% IVA (Portugal)
const API_KEY: &str = "YOUR_API_KEY";
async fn lookup_vat(client: &Client, number: &str) -> Value {
client.get("https://api.veille.io/v1/vat")
.header("x-api-key", API_KEY)
.query(&[("vat", number)])
.send().await.unwrap().json::<Value>().await.unwrap()
}
async fn calculate_checkout(client: &Client, subtotal: f64, vat_number: Option<&str>) -> Value {
let mut iva_amount = subtotal * IVA_RATE;
let mut empresa = json!(null);
if let Some(number) = vat_number {
let result = lookup_vat(client, number).await;
if result["is_valid"].as_bool().unwrap_or(false) {
iva_amount = 0.0;
empresa = json!({"empresa": result["company_name"], "pais": result["country_code"]});
} else {
return json!({ "error": "Número de IVA inválido." });
}
}
json!({ "subtotal": subtotal, "iva": iva_amount, "total": subtotal + iva_amount, "empresa": empresa })
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
println!("{}", calculate_checkout(&client, 100.0, None).await);
println!("{}", calculate_checkout(&client, 100.0, Some("PT123456789")).await);
println!("{}", calculate_checkout(&client, 100.0, Some("INVALIDO123")).await);
Ok(())
}Guarde sempre o nome e morada da empresa validada junto com o registo da encomenda. As autoridades fiscais da UE exigem prova de que o mecanismo de autoliquidação foi corretamente aplicado.