Vai al contenuto

API fattura elettronica in PHP

Invia fatture elettroniche allo SdI e ricevi fatture e notifiche dei fornitori dal tuo codice PHP, con esempi completi da copiare. Ambiente: PHP 7.4+ con curl.

Prima di iniziare

  1. Crea un account e conferma l'e-mail: hai 30 giorni e 20 fatture di prova.
  2. Usa le stesse credenziali del portale per il login alle API (POST https://api.fattura-elettronica.cloud/api/login) e conserva il token.
  3. Per ricevere fatture e notifiche sul tuo server, imposta gli indirizzi dei tuoi endpoint nel profilo del portale e attiva l'inoltro.

Inviare una fattura elettronica in PHP

Basta l'estensione curl, presente in quasi tutte le installazioni. Il file viene inviato con CURLFile in multipart/form-data. La risposta contiene inoltro.id_sdi, l'identificativo con cui arriveranno le notifiche della fattura.

<?php
// PHP 7.4+ con l'estensione curl
const API = 'https://api.fattura-elettronica.cloud/api';

function chiamaApi(string $percorso, array $campi = [], ?string $token = null): array
{
    $ch = curl_init(API . $percorso);
    $header = ['Accept: application/json'];
    if ($token) {
        $header[] = 'Authorization: Bearer ' . $token;
    }
    curl_setopt_array($ch, [
        CURLOPT_HTTPHEADER => $header,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 90,
    ]);
    if ($campi) {
        curl_setopt($ch, CURLOPT_POSTFIELDS, $campi); // array = multipart/form-data (POST)
    }
    $risposta = curl_exec($ch);
    $http = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);
    return [$http, json_decode((string) $risposta, true)];
}

// 1. Login: il token vale a lungo, salvalo e rifai il login solo se ricevi 401
[$http, $login] = chiamaApi('/login', ['email' => 'tua@email.it', 'password' => 'LaTuaPassword']);
$token = $login['access_token'] ?? null;
if (!$token) {
    exit('Login non riuscito: ' . ($login['message'] ?? "HTTP $http"));
}

// 2. Invio della fattura XML allo SdI
$file = new CURLFile(__DIR__ . '/IT01234567890_00001.xml', 'application/xml');
[$http, $esito] = chiamaApi('/inoltro', ['fileFattura' => $file], $token);

if ($http === 200 && ($esito['esito'] ?? '') === 'ok') {
    echo 'Inviata, identificativo SdI: ' . $esito['inoltro']['id_sdi'] . PHP_EOL;
} else {
    echo "Errore $http: " . ($esito['messaggio'] ?? 'risposta non valida') . PHP_EOL;
}

// 3. Elenco delle fatture inviate (esito: 0 in attesa, 1 consegnata, 2 mancata consegna, 3 scartata, ...)
[$http, $elenco] = chiamaApi('/inoltro', [], $token);
foreach ($elenco['inoltri'] ?? [] as $inoltro) {
    echo $inoltro['id_sdi'] . ' ' . $inoltro['esito'] . PHP_EOL;
}

Codici di risposta: 200 inviata, 400 file non valido, 401 token scaduto, 403 pacchetto esaurito o e-mail non confermata, 422 rifiutata dallo SdI. Tutti i dettagli nella documentazione completa.

Ricevere le fatture elettroniche in PHP

Quando arriva una fattura per il tuo codice destinatario la mandiamo al tuo endpoint con una POST form-urlencoded: strxml (XML della fattura), xmlF (file originale), xmlM (metadati SdI), tutti in base64, e formato. Gli endpoint sono normali file PHP raggiungibili via web. Leggi il corpo con php://input prima di usarlo: serve così com'è per verificare la firma. Salva le fatture fuori dalla cartella pubblica.

<?php
// ricevi-fatture.php - URL da indicare in "Endpoint ricezione fatture" (es. https://tuosito.it/ricevi-fatture.php)
const SEGRETO = 'IL_TUO_SEGRETO';  // pagina Integrazione del portale; lascia '' se non usi la firma

$corpo = file_get_contents('php://input');

// Verifica della firma: X-FE-Firma = HMAC-SHA256 di "timestamp\ncorpo" con il tuo segreto
if (SEGRETO !== '') {
    $timestamp = $_SERVER['HTTP_X_FE_TIMESTAMP'] ?? '';
    $attesa = hash_hmac('sha256', $timestamp . "\n" . $corpo, SEGRETO);
    if (!ctype_digit($timestamp) || abs(time() - (int) $timestamp) > 300
            || !hash_equals($attesa, $_SERVER['HTTP_X_FE_FIRMA'] ?? '')) {
        http_response_code(403);
        exit('firma non valida');
    }
}

parse_str($corpo, $dati);
$xmlFattura   = base64_decode($dati['strxml'] ?? ''); // XML della fattura, già estratto dal .p7m
$fileOriginale = base64_decode($dati['xmlF'] ?? '');  // file così come è arrivato dallo SdI (.xml o .xml.p7m)
$metadati     = simplexml_load_string(base64_decode($dati['xmlM'] ?? '')); // file metadati dello SdI
$formato      = $dati['formato'] ?? '';               // FPR12 tra privati, FPA12 verso la PA

$idSdi = $metadati ? (string) $metadati->IdentificativoSdI : '';
if ($idSdi === '' || $xmlFattura === '') {
    http_response_code(400);
    exit('dati mancanti');
}

// Salva la fattura fuori dalla cartella pubblica. Stesso IdentificativoSdI = stessa fattura:
// se arriva di nuovo (ad esempio dopo un tuo errore) sovrascrivi, non duplicare.
$cartella = __DIR__ . '/../fatture_ricevute';
if (!is_dir($cartella)) {
    mkdir($cartella, 0750, true);
}
file_put_contents("$cartella/$idSdi.xml", $xmlFattura);
file_put_contents("$cartella/{$idSdi}_" . basename((string) $metadati->NomeFile), $fileOriginale);

// Leggi i dati che ti servono direttamente dall'XML
$fattura = simplexml_load_string($xmlFattura);
$fornitore = (string) $fattura->FatturaElettronicaHeader->CedentePrestatore->DatiAnagrafici->Anagrafica->Denominazione;
$numero = (string) $fattura->FatturaElettronicaBody->DatiGenerali->DatiGeneraliDocumento->Numero;

echo 'OK'; // rispondi esattamente OK: con qualsiasi altra risposta la fattura viene rimandata più tardi

Rispondi OK con HTTP 200 solo dopo aver salvato la fattura. Se il tuo server non risponde riproviamo per circa 4 giorni: la stessa fattura può arrivare più volte, riconoscila da IdentificativoSdI.

Ricevere le notifiche SdI in PHP

Ogni notifica dello SdI (consegna, scarto, mancata consegna, esiti della PA) arriva al tuo endpoint notifiche con i campi tipoNotifica e esitojson. Il significato di ogni notifica è spiegato nella guida alle notifiche SdI.

<?php
// ricevi-notifiche.php - URL da indicare in "Endpoint ricezione notifiche"
const SEGRETO = 'IL_TUO_SEGRETO';  // pagina Integrazione del portale; lascia '' se non usi la firma

$corpo = file_get_contents('php://input');
if (SEGRETO !== '') { // stessa verifica della firma dell'endpoint fatture
    $ts = $_SERVER['HTTP_X_FE_TIMESTAMP'] ?? '';
    $attesa = hash_hmac('sha256', $ts . "\n" . $corpo, SEGRETO);
    if (!ctype_digit($ts) || abs(time() - (int) $ts) > 300 || !hash_equals($attesa, $_SERVER['HTTP_X_FE_FIRMA'] ?? '')) {
        http_response_code(403);
        exit('firma non valida');
    }
}

parse_str($corpo, $dati);
$tipi = [1 => 'RC', 2 => 'MC', 3 => 'NS', 4 => 'NE', 5 => 'DT', 6 => 'AT'];
$tipo = $tipi[(int) ($dati['tipoNotifica'] ?? 0)] ?? null;
$notifica = json_decode($dati['esitojson'] ?? '', true); // la notifica dello SdI convertita in JSON
if (!$tipo || !$notifica) {
    http_response_code(400);
    exit('dati mancanti');
}

$stati = [
    'RC' => 'consegnata',
    'MC' => 'mancata consegna: disponibile nel cassetto fiscale del cliente',
    'NS' => 'scartata',              // i motivi sono in $notifica['ListaErrori']
    'NE' => 'esito del committente', // solo PA: EsitoCommittente.Esito = EC01 accettata, EC02 rifiutata
    'DT' => 'decorrenza termini',    // solo PA: nessuna risposta entro 15 giorni
    'AT' => 'attestazione di avvenuta trasmissione',
];

// IdentificativoSdI è lo stesso restituito dall'API al momento dell'invio (inoltro.id_sdi).
// La stessa notifica può arrivare due volte: l'aggiornamento deve poter essere ripetuto.
$pdo = new PDO('mysql:host=localhost;dbname=gestionale;charset=utf8mb4', 'utente', 'password');
$pdo->prepare('UPDATE fatture SET stato_sdi = ?, notifica_sdi = ? WHERE id_sdi = ?')
    ->execute([$stati[$tipo], $dati['esitojson'], $notifica['IdentificativoSdI']]);

echo 'OK'; // rispondi esattamente OK: con qualsiasi altra risposta la notifica viene rimandata

Prossimi passi

Inizia con PHP

Prova gratis 30 giorni con 20 fatture, poi da 5 € al mese.