Vai al contenuto

Fattura elettronica in Java

Questo tutorial crea tre semplici applicazioni Java da zero:

  1. Receive: si connette e autentica con l'API Invoicetronic e scarica le nuove fatture passive in arrivo.
  2. Send: si connette e autentica con l'API Invoicetronic e invia una fattura al SDI.
  3. Update: si connette e autentica con l'API Invoicetronic e consulta la cronologia delle notifiche restituite dallo SDI.

Prima di continuare, assicurati che tutti i prerequisiti siano soddisfatti.

Prerequisiti

Assumiamo che questi prerequisiti siano soddisfatti:

Usiamo Maven per la gestione delle dipendenze, che è lo standard de facto per progetti Java moderni.

Tip

Per un'esperienza Java ottimale, considera l'uso di IntelliJ IDEA o Eclipse come IDE.

Lo sapevi?

L'SDK Java è compatibile con Spring Boot, Jakarta EE e tutte le moderne applicazioni enterprise Java.

Receive

Creare l'applicazione

Il primo passo è creare la directory dell'applicazione:

mkdir receive && cd receive

Configurare Maven

Crea un file pom.xml con la seguente configurazione:

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.invoicetronic.example</groupId>
    <artifactId>receive-example</artifactId>
    <version>1.0.0</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.invoicetronic</groupId>
            <artifactId>java-sdk</artifactId>
            <version>1.8.0</version>
        </dependency>
    </dependencies>
</project>

Configurare l'SDK

Crea la struttura delle directory e il file principale:

mkdir -p src/main/java/com/invoicetronic/example

Crea il file src/main/java/com/invoicetronic/example/Main.java:

Configurare l'SDK
package com.invoicetronic.example;

import com.invoicetronic.sdk.ApiClient;
import com.invoicetronic.sdk.ApiException;
import com.invoicetronic.sdk.Configuration;
import com.invoicetronic.sdk.auth.HttpBasicAuth;
import com.invoicetronic.sdk.api.ReceiveApi;
import com.invoicetronic.sdk.model.Receive;

import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Configura l'SDK
        ApiClient defaultClient = Configuration.getDefaultApiClient();
        defaultClient.setBasePath("https://api.invoicetronic.com/v1");

        HttpBasicAuth basicAuth = (HttpBasicAuth) defaultClient.getAuthentication("Basic");
        basicAuth.setUsername("LA TUA CHIAVE API DI TEST (inizia con ik_test_)");
        basicAuth.setPassword("");
    }
}

Come puoi vedere, configuriamo l'SDK impostando il base path e l'autenticazione HTTP Basic con la tua chiave API di test (non quella live). Nota come usiamo setUsername() per la chiave API e setPassword("") vuoto.

Le chiavi API vengono fornite in coppia

Quando crei il tuo account, ottieni una coppia di chiavi API. Una è la chiave di test per l'API Sandbox, e l'altra è quella live. Puoi distinguerle perché la prima inizia con ik_test_, mentre la seconda inizia con ik_live_. In questo tutorial, usa sempre la chiave di test.

Scaricare le fatture

Siamo pronti per effettuare una richiesta. Vogliamo scaricare le nuove fatture passive che potrebbero essere disponibili dall'SDI. Aggiungi questo codice nel metodo main:

Scaricare le fatture non lette
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

// Scarica le fatture non lette
ReceiveApi receiveApi = new ReceiveApi(defaultClient);

try {
    List<Receive> inboundInvoices = receiveApi.receiveGet(
        null,  // companyId
        null,  // identifier
        true,  // unread
        null,  // committente
        null,  // prestatore
        null,  // fileName
        null,  // lastUpdateFrom
        null,  // lastUpdateTo
        null,  // dateSentFrom
        null,  // dateSentTo
        null,  // documentDateFrom
        null,  // documentDateTo
        null,  // documentNumber
        true,  // includePayload
        null,  // page
        null,  // pageSize
        null   // sort
    );

    System.out.println("Ricevute " + inboundInvoices.size() + " fatture");

    for (Receive invoice : inboundInvoices) {
        if (invoice.getEncoding() == Receive.EncodingEnum.XML) {
            try (FileOutputStream fos = new FileOutputStream(invoice.getFileName())) {
                fos.write(invoice.getPayload().getBytes(StandardCharsets.UTF_8));
            }
        } else if (invoice.getEncoding() == Receive.EncodingEnum.BASE64) {
            try (FileOutputStream fos = new FileOutputStream(invoice.getFileName())) {
                fos.write(Base64.getDecoder().decode(invoice.getPayload()));
            }
        }

        System.out.println("Scaricato " + invoice.getFileName() +
            " da un fornitore con Partita IVA " + invoice.getPrestatore());
    }
} catch (ApiException | IOException e) {
    System.err.println("Errore: " + e.getMessage());
    e.printStackTrace();
}

Inclusione del Payload

Impostiamo includePayload a true per recuperare il contenuto effettivo della fattura nella proprietà payload. Senza questo parametro, il campo payload sarebbe null di default, il che migliora le prestazioni e riduce la dimensione della risposta quando hai bisogno solo dei metadati.

Compila ed esegui l'applicazione:

mvn clean compile exec:java -Dexec.mainClass="com.invoicetronic.example.Main"

Dovresti ottenere un output simile a questo:

Ricevute 3 fatture
Scaricato file1.xml da un fornitore con Partita IVA IT06157670966
Scaricato file2.xml.p7m da un fornitore con Partita IVA IT01280270057
Scaricato file3.xml.p7m da un fornitore con Partita IVA IT01280270057

I file sono nella directory corrente, pronti per essere ispezionati.

Non ricevi fatture nell'ambiente live?

Assicurati di esserti registrato con l'Agenzia delle Entrate, che è un requisito per l'ambiente live.

Cosa abbiamo imparato

In questo esempio, abbiamo imparato diverse cose.

  1. Dobbiamo configurare l'SDK ottenendo il client di default con Configuration.getDefaultApiClient(), impostando il base path e configurando l'autenticazione HTTP Basic con username (chiave API) e password vuota.

  2. Dobbiamo istanziare una classe che rappresenta l'endpoint con cui vogliamo lavorare. In questo caso, utilizziamo ReceiveApi per scaricare le fatture in arrivo, passando il client configurato.

  3. Le classi endpoint come ReceiveApi offrono metodi per interagire con la loro entità target. Chiamiamo receiveGet() per recuperare le fatture. Poiché vogliamo solo fatture nuove e non lette, passiamo true per il parametro unread. Passiamo anche true per includePayload per recuperare il contenuto effettivo della fattura.

  4. Gli oggetti fattura espongono metodi come getEncoding(), getFileName() e getPayload(). L'ultimo contiene il contenuto della fattura, come testo semplice o codificato in Base64, come descritto da getEncoding() che restituisce un enum Receive.EncodingEnum.

Codice sorgente su GitHub

Il codice sorgente per questo Quickstart è disponibile anche su GitHub.

Send

Creare l'applicazione

Il primo passo è creare la directory dell'applicazione:

mkdir send && cd send

Configurare Maven

Crea un file pom.xml con la seguente configurazione:

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.invoicetronic.example</groupId>
    <artifactId>send-example</artifactId>
    <version>1.0.0</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.invoicetronic</groupId>
            <artifactId>java-sdk</artifactId>
            <version>1.8.0</version>
        </dependency>
    </dependencies>
</project>

Configurare l'SDK

Crea la struttura delle directory e il file principale:

mkdir -p src/main/java/com/invoicetronic/example

Crea il file src/main/java/com/invoicetronic/example/Main.java:

Configurare l'SDK
package com.invoicetronic.example;

import com.invoicetronic.sdk.ApiClient;
import com.invoicetronic.sdk.ApiException;
import com.invoicetronic.sdk.Configuration;
import com.invoicetronic.sdk.auth.HttpBasicAuth;
import com.invoicetronic.sdk.api.SendApi;
import com.invoicetronic.sdk.model.Send;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        // Configura l'SDK
        ApiClient defaultClient = Configuration.getDefaultApiClient();
        defaultClient.setBasePath("https://api.invoicetronic.com/v1");

        HttpBasicAuth basicAuth = (HttpBasicAuth) defaultClient.getAuthentication("Basic");
        basicAuth.setUsername("LA TUA CHIAVE API DI TEST (inizia con ik_test_)");
        basicAuth.setPassword("");
    }
}

Come puoi vedere, configuriamo l'SDK impostando il base path e l'autenticazione HTTP Basic con la tua chiave API di test (non quella live).

Le chiavi API vengono fornite in coppia

Quando crei il tuo account, ottieni una coppia di chiavi API. Una è la chiave di test per l'API Sandbox, e l'altra è quella live. Puoi distinguerle perché la prima inizia con ik_test_, mentre la seconda inizia con ik_live_. In questo tutorial, usa sempre la chiave di test.

Inviare una fattura

Siamo pronti per effettuare una richiesta. Vogliamo inviare una fattura all'SDI. Aggiungi questo codice nel metodo main:

Inviare una fattura
// Invia una fattura
String filePath = "/qualche/percorso/file/nomefile.xml";

Map<String, String> metaData = new HashMap<>();
metaData.put("internal_id", "123");
metaData.put("created_with", "myapp");
metaData.put("some_other_custom_data", "value");

SendApi sendApi = new SendApi(defaultClient);

try {
    String payload = new String(Files.readAllBytes(Paths.get(filePath)));

    Send sendData = new Send();
    sendData.setFileName(Paths.get(filePath).getFileName().toString());
    sendData.setPayload(payload);
    sendData.setMetaData(metaData);

    Send sentInvoice = sendApi.sendPost(sendData, null, null);

    System.out.println("La fattura è stata inviata con successo, ora ha l'Id univoco " +
        sentInvoice.getId() + ".");

} catch (ApiException | IOException e) {
    System.err.println("Errore: " + e.getMessage());
    e.printStackTrace();
}

Compila ed esegui l'applicazione:

mvn clean compile exec:java -Dexec.mainClass="com.invoicetronic.example.Main"

Dovresti ottenere un output simile a questo:

La fattura è stata inviata con successo, ora ha l'Id univoco 123.

Verificare lo stato della fattura

Quando inoltri una fattura allo SDI, la consegna non è istantanea: lo SDI esegue dei controlli e restituisce una sequenza di notifiche che descrivono lo stato del processo (Inviato, Consegnato, Scartato, ecc.). Il modello Send espone il metodo getLatestState() con lo stato corrente, evitando una chiamata separata a /update quando ti serve sapere solo come è andata.

Leggere lo stato corrente
// Recupera lo stato più recente di una fattura già inviata
Send fresh = sendApi.sendIdGet(sentInvoice.getId(), null);
String state = fresh.getLatestState() != null ? fresh.getLatestState().getValue() : "In elaborazione";
System.out.println("Stato corrente: " + state);

Subito dopo l'invio, getLatestState() può restituire null: lo SDI non ha ancora processato il documento. Ricontrolla dopo qualche secondo o, meglio, configura un webhook per ricevere una notifica push ad ogni cambio di stato.

Risparmia richieste API

Usa getLatestState() su Send ogni volta che ti serve solo lo stato corrente: una sola chiamata invece di una a /send più una a /update. Ricorri a UpdateApi solo quando ti serve la cronologia completa delle transizioni.

Cosa abbiamo imparato

In questo esempio, abbiamo imparato diverse cose.

  1. Dobbiamo configurare l'SDK ottenendo il client di default con Configuration.getDefaultApiClient(), impostando il base path e configurando l'autenticazione HTTP Basic.

  2. Dobbiamo istanziare una classe che rappresenta l'endpoint con cui vogliamo lavorare. In questo caso, utilizziamo SendApi per inviare fatture. Le classi endpoint come SendApi offrono metodi per interagire con la loro entità target. Chiamiamo sendPost() per inviare una fattura.

  3. Il modello Send espone metodi come setFileName(), setMetaData() e setPayload(). L'ultimo contiene il contenuto della fattura, mentre setMetaData() è opzionale e lega dati personalizzati al documento.

  4. Il modello Send espone anche getLatestState() con lo stato SDI corrente, leggibile via sendIdGet(id, null). Evita una chiamata a /update quando serve solo conoscere lo stato.

Codice sorgente su GitHub

Il codice sorgente per questo Quickstart è disponibile anche su GitHub.

Update

Per lo stato corrente di una fattura inviata, è sufficiente leggere getLatestState() dal modello Send (vedi Verificare lo stato della fattura). Se invece vuoi la cronologia completa delle transizioni — per esempio per capire perché una fattura è stata scartata, mostrare in UI tutti i passaggi di stato con timestamp, o tracciare le notifiche restituite da un ente della Pubblica Amministrazione — usa UpdateApi.

Le query su /update sono gratuite

Le richieste a /update non vengono conteggiate sul tuo piano: puoi consultare la cronologia delle notifiche con la frequenza che preferisci.

Creare l'applicazione

mkdir update && cd update

Configurare Maven

Crea un file pom.xml analogo a quelli precedenti, sostituendo l'artifactId con update-example.

Recuperare la cronologia delle notifiche

Crea il file src/main/java/com/invoicetronic/example/Main.java:

Cronologia delle notifiche per una fattura
package com.invoicetronic.example;

import com.invoicetronic.sdk.ApiClient;
import com.invoicetronic.sdk.ApiException;
import com.invoicetronic.sdk.Configuration;
import com.invoicetronic.sdk.auth.HttpBasicAuth;
import com.invoicetronic.sdk.api.UpdateApi;
import com.invoicetronic.sdk.model.Update;

import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Configura l'SDK
        ApiClient defaultClient = Configuration.getDefaultApiClient();
        defaultClient.setBasePath("https://api.invoicetronic.com/v1");

        HttpBasicAuth basicAuth = (HttpBasicAuth) defaultClient.getAuthentication("Basic");
        basicAuth.setUsername("LA TUA CHIAVE API DI TEST (inizia con ik_test_)");
        basicAuth.setPassword("");

        // Id della fattura inviata di cui vogliamo ricostruire la cronologia
        Integer sendId = 225;

        UpdateApi updateApi = new UpdateApi(defaultClient);

        try {
            List<Update> updates = updateApi.updateGet(
                null,           // companyId
                null,           // identifier
                null,           // prestatore
                null,           // unread
                sendId,         // sendId
                null,           // state
                null,           // lastUpdateFrom
                null,           // lastUpdateTo
                null,           // dateSentFrom
                null,           // dateSentTo
                null,           // page
                null,           // pageSize
                "last_update"   // sort
            );

            System.out.println("Trovate " + updates.size() +
                " notifiche per la fattura " + sendId);

            for (Update update : updates) {
                String description = update.getDescription() != null
                    ? update.getDescription() : "OK";
                System.out.println("  [" + update.getLastUpdate() + "] state=" +
                    update.getState() + " - " + description);
            }
        } catch (ApiException e) {
            System.err.println("Errore: " + e.getMessage());
        }
    }
}

Compila ed esegui l'applicazione:

mvn clean compile exec:java -Dexec.mainClass="com.invoicetronic.example.Main"

Dovresti ottenere un output simile a questo:

Trovate 2 notifiche per la fattura 225
  [2025-01-23T16:56:14Z] state=INVIATO - OK
  [2025-01-23T17:12:03Z] state=CONSEGNATO - OK

Il campo state è la proprietà più importante. I valori più comuni sono:

Valore Nome Descrizione
2 Inviato Inviata allo SDI.
5 Consegnato Consegnata al destinatario.
7 Scartato Rifiutata dallo SDI. In description trovi il motivo.

L'elenco completo dei valori è disponibile nella API Reference.

Monitora sempre lo stato delle fatture inviate

Lo stato Inviato significa solo che il documento è stato preso in carico dallo SDI, non che sia stato consegnato. Uno stato Scartato indica che la fattura non è stata accettata e potrebbe richiedere una correzione e un nuovo invio.

Cosa abbiamo imparato

  1. Per consultare la cronologia delle notifiche utilizziamo la classe UpdateApi invece di SendApi o ReceiveApi.

  2. Il metodo updateGet() accetta filtri come sendId (per le notifiche di una specifica fattura inviata), state (per filtrare per stato), lastUpdateFrom/lastUpdateTo (intervallo temporale) e altri.

  3. Le query su /update sono gratuite e non vengono conteggiate sul tuo piano, quindi puoi richiamarle con la frequenza che ti serve.

Codice sorgente su GitHub

Il codice sorgente per questo Quickstart è disponibile anche su GitHub.