e-bon
e-bon.ro
Arhitectură

Cum funcționează emiterea bonului fiscal

Înțelege cum trimite e-bon o comandă de tipărire către dispozitiv, când comută pe trezirea prin push, cât durează fiecare etapă și cum primești rezultatul final.

Când îi ceri lui e-bon să tipărească un bon fiscal, cererea pleacă de pe serverul tău, trece prin API-ul e-bon și ajunge la dispozitivul AMEF de pe tejgheaua comerciantului. Această pagină explică drumul din perspectiva ta: ce face API-ul în numele tău, cât durează fiecare etapă și cum afli dacă bonul a fost tipărit.

Același flux rulează pentru fiecare comandă fiscală — print_receipt, print_reversal_receipt, void_receipt, x_report, z_report, cash_in, cash_out. Bonurile sunt doar cazul cu volumul cel mai mare.

Trimite comanda de tipărire

Emite o singură cerere HTTP pentru a lansa o tipărire:

POST /api/v1/devices/{deviceId}/commands
Content-Type: application/json
Idempotency-Key: <cheia-ta>

{ "type": "print_receipt", "payload": { /* corpul bonului */ } }

API-ul răspunde imediat cu 201 Created și status: "pending". Tipărirea efectivă rulează în fundal — clientul tău nu este blocat cât timp lucrează dispozitivul.

Două endpoint-uri ating un bon și fac lucruri diferite:
  • POST /api/v1/devices/{deviceId}/commands cu type: "print_receipt" este declanșatorul. Dispozitivul tipărește ca efect secundar.
  • POST /api/v1/receipts este doar persistare. Stochează un bon care a fost deja tipărit (de obicei trimis înapoi de aplicația de pe dispozitiv). Nu invocă imprimanta.
Dacă vrei să tipărești, folosește endpoint-ul de comenzi. Dacă vrei să înregistrezi un bon existent în afara e-bon, folosește endpoint-ul de bonuri.

Ajunge la dispozitiv

API-ul folosește o strategie pe două niveluri pentru a ajunge la dispozitiv. Nu alegi între ele — sistemul decide automat în funcție de starea curentă a conexiunii dispozitivului.

Încearcă mai întâi conexiunea activă

Dacă aplicația de pe dispozitiv este conectată prin WebSocket, API-ul îi predă comanda direct. Aceasta este calea normală și se finalizează de obicei în mai puțin de o secundă.

Treci pe trezirea prin push

Dacă dispozitivul nu este conectat în acest moment (intermitență de date mobile, aplicație în fundal), API-ul trimite o notificare push de prioritate înaltă prin Firebase Cloud Messaging (FCM). Aplicația se trezește, deschide conexiunea, execută comanda și raportează rezultatul înapoi.

Curăță comenzile blocate cu o plasă de siguranță

Un proces de fundal rulează la fiecare 30 de secunde. Orice comandă rămasă în pending sau sent mai mult de 180 de secunde este forțată în timeout, ca să nu rămână blocată la nesfârșit.

Vezi totul ca pe un singur flux de status pe documentul comenzii — nu trebuie să știi ce cale a fost folosită.

Planifică bugetul de timp

EtapăLimităCe înseamnă pentru tine
Încercarea pe conexiune activă30 sDacă dispozitivul răspunde la timp, primești rezultatul pe același canal.
Trezire prin push + rezultat60 sDacă dispozitivul este offline, API-ul așteaptă până la 60 s un rezultat după ce trimite push-ul.
Plasa de siguranță180 sO comandă care nu ajunge la o stare finală în 180 s este forțată în timeout.

Dacă pui un flux sincron pentru utilizator peste aceste apeluri, proiectează-l pentru până la ~3 minute în cazul cel mai rău, înainte să apară un status final. În practică, calea activă se finalizează în mai puțin de o secundă.

Primește rezultatul

Două moduri de a afla ce s-a întâmplat, și le poți folosi pe amândouă:

Abonează-te la aceste evenimente când îți configurezi endpoint-ul de webhook:

  • command.completed — dispozitivul a executat comanda cu succes.
  • command.failed — comanda a eșuat din orice motiv în timpul încercării pe conexiune activă sau prin push.
  • command.timeout — plasa de siguranță de 180 s a încheiat comanda.
  • receipt.created — emis la scurt timp după command.completed pentru un print_receipt, când aplicația de pe dispozitiv trimite înapoi bonul tipărit la e-bon.

Webhook-urile sunt instrumentul potrivit pentru fluxurile fiscale pentru că împing rezultatul în clipa în care este cunoscut.

Tratează fiecare rezultat

status finalÎnseamnăEveniment webhookCe ai de făcut
completedDispozitivul a tipărit bonul și a raportat un ID fiscal.command.completed, apoi receipt.createdSalvează result.fiscalId și result.fiscalDate la comanda ta.
failed (cale activă)Dispozitivul a răspuns cu o eroare — fără hârtie, memorie fiscală plină, respingere ANAF, eroare de driver etc.command.failedCitește errorCode și errorMessage. Arată cauza operatorului, apoi reîncearcă cu o comandă nouă.
failed (cale push)Push-ul nu a putut fi trimis (fără token FCM înregistrat pentru dispozitiv, eroare FCM).command.failedVerifică dacă dispozitivul este înregistrat și online, apoi reîncearcă.
timeout (cale push)Au trecut 60 s de la trezirea prin push fără rezultat. Bonul s-ar putea să fi fost tipărit, dar dispozitivul nu a confirmat la timp.command.failedVerifică dispozitivul. Tipărirea poate avea nevoie de o nouă încercare — confirmă înainte de a reîncerca, ca să eviți duplicatele.
timeout (plasă de siguranță)Au trecut 180 s fără un status final.command.timeoutIdentic cu rândul de mai sus. Inspectează dispozitivul, apoi decide dacă reîncerci.
Un timeoutnu garantează că bonul nu a fost tipărit. Dispozitivul poate să fi tipărit cu succes și să fi pierdut conexiunea înainte să raporteze rezultatul. Confirmă întotdeauna cu operatorul înainte de a reemite o comandă de tipărire — bonurile fiscale duplicate nu se pot anula, doar storna printr-o comandă separată.

Evită tipăririle duplicate

Trimite întotdeauna antetul Idempotency-Key pe POST /commands. Dacă clientul reîncearcă aceeași cerere (intermitență de rețea, restart), e-bon returnează răspunsul original în loc să emită o a doua tipărire. Vezi Idempotență și reîncercări pentru comportamentul complet, inclusiv durata cheii și regulile de redare.

Distinge evenimentele de bon de cele de comandă

Un print_receipt reușit produce două evenimente webhook, în această ordine:

  1. command.completed — se declanșează în clipa în care dispozitivul confirmă execuția. Include ID-ul fiscal în result.
  2. receipt.created — se declanșează la scurt timp după (de obicei în aceeași secundă) când aplicația de pe dispozitiv trimite înapoi corpul complet al bonului la e-bon, pentru stocare.

Dacă te interesează doar dacă tipărirea a reușit, command.completed este suficient. Dacă ai nevoie de bonul detaliat — articole de linie, defalcare TVA, plăți — abonează-te la receipt.created și citește-l prin GET /api/v1/receipts/{id}.

Continuă integrarea