ChartQuery

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 requests

Sem 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.

Nesta página