Integrare ANAF SPV — Cum sa conectezi aplicatia de facturare la ANAF
Ghid tehnic pentru integrarea cu ANAF prin SPV: autorizare OAuth2, trimitere facturi si verificare status.
Arhitectura API-ului e-Factura ANAF
ANAF expune un API REST pentru sistemul e-Factura, documentat oficial pe pagina de developer a institutiei. Autentificarea se face prin protocolul OAuth 2.0, cu fluxul de autorizare prin redirectare (Authorization Code Flow) — similar cu „Conectare cu Google/Facebook" din aplicatii web.
Exista doua medii disponibile:
- Mediul de test (sandbox):
https://api.anaf.ro/test/— pentru dezvoltare si testare, fara efecte fiscale reale - Mediul de productie:
https://api.anaf.ro/prod/— pentru tranzactii reale, cu efecte legale
Principalele endpoint-uri utilizate in fluxul e-Factura sunt:
POST /upload— incarcarea unui fisier XML (factura sau nota de creditare)GET /stareMesaj?id_incarcare={id}— verificarea statusului unui uploadGET /descarcare?id={id}— descarcarea raspunsului semnat de ANAFGET /listaMesajeFactura— lista mesajelor (facturi primite si trimise) din inbox-ul SPV
Pasul 1: Inregistrarea aplicatiei in SPV
Pentru a obtine acces la API-ul ANAF, trebuie mai intai sa inregistrezi aplicatia de facturare in Spatiul Privat Virtual (SPV) al firmei tale. Iata cum:
- Autentifica-te in SPV la adresa
spv.anaf.rocu semnatura digitala sau prin intermediul serviciului de autentificare cu date ANAF - Navigheaza la sectiunea „Servicii disponibile" > „e-Factura" > „Obtine token acces"
- Sistemul iti va prezenta o interfata de autorizare OAuth2: copiaza Client ID si, daca aplicatia ta necesita, genereaza un Client Secret
- Completeaza Redirect URI — aceasta este adresa la care ANAF va trimite codul de autorizare dupa ce utilizatorul aproba accesul (de ex:
https://app.storno.ro/callback/anaf)
Retine ca token-ul OAuth2 este asociat cu CUI-ul firmei, nu cu contul de utilizator al persoanei care l-a generat. Daca firma are mai multi utilizatori SPV, oricare dintre ei poate autoriza aplicatia.
Pasul 2: Fluxul de autorizare OAuth2
Odata inregistrata aplicatia, fluxul de autorizare functioneaza astfel:
- Redirectare la ANAF: Aplicatia redirecxtioneaza utilizatorul catre endpoint-ul de autorizare ANAF:
GET https://logincert.anaf.ro/anaf-oauth2/v1/authorize ?response_type=code &client_id={CLIENT_ID} &redirect_uri={REDIRECT_URI} &scope=efactura &state={RANDOM_STATE} - Autentificarea utilizatorului: Utilizatorul se autentifica in SPV (cu semnatura digitala sau token hardware)
- Codul de autorizare: ANAF redirecxtioneaza inapoi la
redirect_uricu un parametrucodein URL - Schimbul codului pentru token: Aplicatia face o cerere POST catre endpoint-ul de token:
POST https://logincert.anaf.ro/anaf-oauth2/v1/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code &code={CODE} &client_id={CLIENT_ID} &client_secret={CLIENT_SECRET} &redirect_uri={REDIRECT_URI}
Raspunsul contineaccess_token(valabil 1 ora) sirefresh_token(valabil 30 zile) - Reimprospatarea token-ului: Inainte de expirarea
access_token-ului, aplicatia il poate reinnoi automat folosindrefresh_token, fara a solicita reautorizarea utilizatorului
Pasul 3: Incarcarea unei facturi XML
Cu token-ul de acces obtinut, trimiterea unei facturi se face printr-un POST multipart:
POST /prod/FCTEL/rest/upload?standard=UBL&cif={CIF_EMITENT}
Authorization: Bearer {ACCESS_TOKEN}
Content-Type: multipart/form-data
[fisier XML al facturii]
Parametrii importanti:
standard:UBLpentru formatul UBL 2.1 sauCIIpentru Cross Industry Invoicecif: CUI-ul firmei emitente (fara prefix RO pentru firmele romane)
Raspunsul de la ANAF este un JSON care contine index_incarcare — acesta este ID-ul unic al upload-ului, necesar pentru verificarea statusului:
{
"ExecutionStatus": 0,
"index_incarcare": 12345678
}
Un ExecutionStatus de 0 inseamna ca fisierul a fost primit cu succes. Atentie: acesta nu inseamna ca factura a fost acceptata — doar ca a fost incarcata in coada de procesare.
Pasul 4: Verificarea statusului procesarii
Procesarea de catre ANAF dureaza de la cateva secunde la cateva minute. Poti verifica statusul astfel:
GET /prod/FCTEL/rest/stareMesaj?id_incarcare={INDEX_INCARCARE}
Authorization: Bearer {ACCESS_TOKEN}
Raspunsul posibil:
{
"stare": "ok",
"id_descarcare": 87654321
}
Stari posibile:
"ok"— factura a fost acceptata;id_descarcarecontine ID-ul pentru descarcarea raspunsului semnat"nok"— factura a fost respinsa; apeleaza endpoint-ul de descarcare pentru a obtine lista de erori"in prelucrare"— inca in procesare; reincearca peste cateva secunde"eroare prelucrare"— eroare tehnica pe serverele ANAF; retrimite dupa un interval
Pasul 5: Descarcarea dovezii de transmitere
Odata ce statusul este ok sau nok, descarca fisierul de raspuns:
GET /prod/FCTEL/rest/descarcare?id={ID_DESCARCARE}
Authorization: Bearer {ACCESS_TOKEN}
Raspunsul este un fisier ZIP care contine:
{index_incarcare}.xml— fisierul XML original pe care l-ai trimis{index_incarcare}_semnatura.xml— semnatura digitala aplicata de ANAF (XMLDSig)- In caz de eroare:
{index_incarcare}_Errors.xml— lista detaliata a erorilor de validare
Arhiveaza acest fisier ZIP pentru fiecare factura — reprezinta dovada legala ca factura a fost inregistrata in sistemul e-Factura.
Erorile frecvente si cum le rezolvi
CIF invalid (Cif-ul beneficiarului nu este valid): Verifica ca CUI-ul clientului este activ in baza de date ANAF. Poti verifica prin API-ul public ANAF verificare.anaf.ro sau prin SPV.
Schema XML invalida: Fisierul XML nu respecta schema UBL 2.1. Valideaza fisierul local cu un validator XML schema inainte de a-l trimite (schema oficiala este publicata de ANAF).
Token expirat: access_token-ul a expirat. Implementeaza logica de reimprospatare automata folosind refresh_token inainte de fiecare cerere API.
Duplicate: ANAF poate respinge un fisier daca detecteaza ca o factura cu acelasi numar si CUI emitent a mai fost trimisa. Verifica in baza ta de date daca factura a mai fost trimisa inainte de a retrimite.
Sfaturi de implementare pentru robustete
Implementeaza retry logic cu backoff exponential. ANAF poate fi indisponibil sau lent intermitent. O strategie de reincercare cu intervale crescatoare (1s, 5s, 30s, 5min) previne supraincarcarea serverelor si asigura ca facturile ajung in final la destinatie.
Stocheaza intotdeauna index_incarcare in baza de date imediat dupa upload, inainte de a verifica statusul. Daca aplicatia ta cade sau serverul reporneste intre upload si verificarea statusului, vei putea relua verificarea din index_incarcare stocat.
Monitorizeaza expirarea refresh_token-ului. Tokenul de refresh expira dupa 30 de zile de neutilizare. Daca nu ai trimis facturi o luna, tokenul poate expira si va trebui reautorizat manual prin SPV. Trimite o alerta utilizatorului cu 7 zile inainte de expirare.