Vai al contenuto

Fattura elettronica in Go

Questo tutorial crea tre semplici applicazioni Go 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:

Tip

Per un'esperienza Go ottimale, considera l'uso di Go modules per la gestione delle dipendenze.

Lo sapevi?

L'SDK Go è perfetto per microservizi, applicazioni cloud e sistemi ad alte prestazioni grazie alla sua natura compilata e alla concorrenza nativa.

Receive

Creare l'applicazione

Il primo passo è creare la directory dell'applicazione e inizializzare un modulo Go:

mkdir receive && cd receive
go mod init invoicetronic-receive-example

Installare l'SDK

Installa l'SDK Go:

go get github.com/invoicetronic/go-sdk

Configurare l'SDK

Crea il file main.go:

Configurare l'SDK
package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "os"

    invoicetronicsdk "github.com/invoicetronic/go-sdk"
)

func main() {
    // Configura l'SDK
    config := invoicetronicsdk.NewConfiguration()
    config.Servers = invoicetronicsdk.ServerConfigurations{
        {
            URL: "https://api.invoicetronic.com/v1",
        },
    }

    apiKey := "LA TUA CHIAVE API DI TEST (inizia con ik_test_)"
    auth := apiKey + ":"
    authHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
    config.AddDefaultHeader("Authorization", authHeader)

    client := invoicetronicsdk.NewAPIClient(config)
}

Come puoi vedere, configuriamo l'SDK impostando l'URL del server e l'autenticazione HTTP Basic con la tua chiave API di test (non quella live). Nota come codifichiamo in Base64 la chiave API seguita da ":" per l'header Authorization.

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 nella funzione main:

Scaricare le fatture non lette
    // Scarica le fatture non lette
    ctx := context.Background()

    unread := true
    includePayload := true

    inboundInvoices, _, err := client.ReceiveAPI.ReceiveGet(ctx).
        Unread(unread).
        IncludePayload(includePayload).
        Execute()

    if err != nil {
        fmt.Fprintf(os.Stderr, "Errore: %v\\n", err)
        return
    }

    fmt.Printf("Ricevute %d fatture\\n", len(inboundInvoices))

    for _, invoice := range inboundInvoices {
        if invoice.Encoding != nil {
            if *invoice.Encoding == "Xml" {
                err = os.WriteFile(*invoice.FileName, []byte(*invoice.Payload), 0644)
            } else if *invoice.Encoding == "Base64" {
                decoded, _ := base64.StdEncoding.DecodeString(*invoice.Payload)
                err = os.WriteFile(*invoice.FileName, decoded, 0644)
            }

            if err != nil {
                fmt.Fprintf(os.Stderr, "Errore nel salvataggio del file: %v\\n", err)
                continue
            }

            fmt.Printf("Scaricato %s da un fornitore con Partita IVA %s\\n",
                *invoice.FileName, invoice.Prestatore.Get())
        }
    }

Inclusione del Payload

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

Compila ed esegui l'applicazione:

go run main.go

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 creando una Configuration, impostando l'URL del server e aggiungendo l'header Authorization con HTTP Basic (API key codificata in Base64 seguita da ":").

  2. Dobbiamo creare un client API con NewAPIClient(config) e utilizzare le API specifiche come ReceiveAPI per scaricare le fatture in arrivo.

  3. Le chiamate API usano un pattern fluent con metodi come ReceiveGet(ctx).Unread(true).IncludePayload(true).Execute(). Poiché vogliamo solo fatture nuove e non lette, passiamo Unread(true). Passiamo anche IncludePayload(true) per recuperare il contenuto effettivo della fattura.

  4. Gli oggetti fattura usano puntatori per i campi opzionali. Il campo Encoding può avere i valori "Xml" o "Base64", e Payload contiene il contenuto della fattura.

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 e inizializzare un modulo Go:

mkdir send && cd send
go mod init invoicetronic-send-example

Installare l'SDK

Installa l'SDK Go:

go get github.com/invoicetronic/go-sdk

Configurare l'SDK

Crea il file main.go:

Configurare l'SDK
package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "os"
    "path/filepath"

    invoicetronicsdk "github.com/invoicetronic/go-sdk"
)

func main() {
    // Configura l'SDK
    config := invoicetronicsdk.NewConfiguration()
    config.Servers = invoicetronicsdk.ServerConfigurations{
        {
            URL: "https://api.invoicetronic.com/v1",
        },
    }

    apiKey := "LA TUA CHIAVE API DI TEST (inizia con ik_test_)"
    auth := apiKey + ":"
    authHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
    config.AddDefaultHeader("Authorization", authHeader)

    client := invoicetronicsdk.NewAPIClient(config)
}

Come puoi vedere, configuriamo l'SDK impostando l'URL del server 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 nella funzione main:

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

    metaData := map[string]string{
        "internal_id":             "123",
        "created_with":            "myapp",
        "some_other_custom_data": "value",
    }

    ctx := context.Background()

    payload, err := os.ReadFile(filePath)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Errore nella lettura del file: %v\\n", err)
        return
    }

    fileName := filepath.Base(filePath)
    payloadStr := string(payload)

    sendData := *invoicetronicsdk.NewSend(payloadStr)
    sendData.SetFileName(fileName)
    sendData.SetMetaData(metaData)

    sentInvoice, _, err := client.SendAPI.SendPost(ctx).Send(sendData).Execute()

    if err != nil {
        fmt.Fprintf(os.Stderr, "Errore: %v\\n", err)
        return
    }

    fmt.Printf("La fattura è stata inviata con successo, ora ha l'Id univoco %s.\\n",
        *sentInvoice.Id)

Compila ed esegui l'applicazione:

go run main.go

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 campo LatestState 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
    fresh, _, err := client.SendAPI.SendIdGet(ctx, *sentInvoice.Id).Execute()
    if err != nil {
        fmt.Fprintf(os.Stderr, "Errore: %v\\n", err)
        return
    }

    state := fresh.GetLatestState()
    if state == "" {
        state = "In elaborazione"
    }
    fmt.Printf("Stato corrente: %s\\n", state)

Subito dopo l'invio, GetLatestState() può restituire stringa vuota (HasLatestState() ritorna false): 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 LatestState 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 creando una Configuration, impostando l'URL del server e aggiungendo l'header Authorization con HTTP Basic.

  2. Dobbiamo creare un client API e utilizzare SendAPI per inviare fatture. Le chiamate API usano il pattern SendPost(ctx).Send(sendData).Execute().

  3. Il modello Send viene creato con NewSend() e configurato con setter: SetFileName(), SetPayload() e SetMetaData(). Il payload contiene il contenuto della fattura, mentre MetaData è opzionale e lega dati personalizzati al documento.

  4. Il modello Send espone anche LatestState con lo stato SDI corrente, leggibile via client.SendAPI.SendIdGet(ctx, id).Execute() con il getter GetLatestState(). 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 LatestState 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
go mod init invoicetronic-update-example

Installare l'SDK

go get github.com/invoicetronic/go-sdk

Recuperare la cronologia delle notifiche

Crea il file main.go:

Cronologia delle notifiche per una fattura
package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "os"

    invoicetronicsdk "github.com/invoicetronic/go-sdk"
)

func main() {
    // Configura l'SDK
    config := invoicetronicsdk.NewConfiguration()
    config.Servers = invoicetronicsdk.ServerConfigurations{
        {
            URL: "https://api.invoicetronic.com/v1",
        },
    }

    apiKey := "LA TUA CHIAVE API DI TEST (inizia con ik_test_)"
    auth := apiKey + ":"
    authHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
    config.AddDefaultHeader("Authorization", authHeader)

    client := invoicetronicsdk.NewAPIClient(config)

    // Id della fattura inviata di cui vogliamo ricostruire la cronologia
    sendId := int32(225)

    ctx := context.Background()

    updates, _, err := client.UpdateAPI.UpdateGet(ctx).
        SendId(sendId).
        Sort("last_update").
        Execute()

    if err != nil {
        fmt.Fprintf(os.Stderr, "Errore: %v\n", err)
        return
    }

    fmt.Printf("Trovate %d notifiche per la fattura %d\n", len(updates), sendId)

    for _, update := range updates {
        description := "OK"
        if update.Description.IsSet() && update.Description.Get() != nil {
            description = *update.Description.Get()
        }
        fmt.Printf("  [%s] state=%v - %s\n", update.LastUpdate.Format("2006-01-02T15:04:05Z"), *update.State, description)
    }
}

Compila ed esegui l'applicazione:

go run main.go

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 UpdateAPI invece di SendAPI o ReceiveAPI.

  2. Le chiamate API usano il pattern fluent UpdateGet(ctx).SendId(...).Sort(...).Execute(). Sono disponibili filtri come SendId, State, LastUpdateFrom/LastUpdateTo 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.