Documentație API

    FiscalLink expune un API REST local pentru controlul imprimantei fiscale, tipărirea bonurilor și integrarea cu Shopify sau cu aplicații POS proprii.

    URL-uri de bază

    API-ul este servit local de aplicația FiscalLink, iar toate endpoint-urile sunt prefixate cu /api. Porturile implicite: 65401 (HTTPS), 65400 (HTTP).

    MediuAdresăUtilizare
    Securizat (HTTPS)https://localhost.fiscallink.ro:65401/apiRecomandat pentru integrare normală
    Local (HTTP)http://{ip-local}:65400/apiUtil în rețeaua locală, fără TLS

    Format răspuns (API standard)

    Majoritatea endpoint-urilor returnează un format standard:

    Succes

    {
      "StatusCode": 200,
      "Data": "<payload>"
    }

    Eroare

    {
      "StatusCode": 400,
      "Message": "Descrierea erorii"
    }

    Excepție: Controller-ul Legacy (POST /Receipt) folosește un format diferit — vezi secțiunea dedicată.

    Coduri HTTP frecvente

    • 200Cererea a fost procesată cu succes.
    • 400Parametri invalizi, date lipsă sau validare respinsă de dispozitiv.
    • 404Resursa nu a fost găsită (ex: comandă Shopify inexistentă).
    • 424Dispozitivul nu răspunde sau nu este conectat.
    • 500Eroare internă neașteptată.

    Compatibilitate FiscalNet și Fisco

    FiscalLink este compatibil cu API-urile FiscalNet și Fisco. Dacă aplicația ta folosește deja una dintre aceste integrări, poți migra fără schimbări majore de cod.

    Comportamentul dispozitivului diferă între DATECS, Daisy și Tremol. Vezi diferențele dintre tipurile de case de marcat.

    Network Controller

    Returnează adresele API la runtime. Util când integrarea trebuie să descopere unde ascultă FiscalLink.

    GET
    /get-secure-api-address

    Obține adresa API securizată

    Returnează adresa HTTPS completă a API-ului FiscalLink (inclusiv /api).

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "https://localhost.fiscallink.ro:65401/api"
    }

    Eroare

    {
      "StatusCode": 500,
      "Message": "Eroare internă"
    }
    GET
    /get-local-api-address

    Obține adresa API locală

    Returnează adresa HTTP completă a API-ului din rețeaua locală (inclusiv /api).

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "http://192.168.1.100:65400/api"
    }

    Eroare

    {
      "StatusCode": 500,
      "Message": "Eroare internă"
    }

    Device Communication — Bon nefiscal

    Flux: deschide bon → tipărește text/elemente → închide sau anulează.

    POST
    /open-non-fiscal-receipt

    Deschide un bon nefiscal

    Deschide un bon nefiscal. După deschidere folosește print-non-fiscal-text pentru conținut, apoi close-non-fiscal-receipt sau cancel-non-fiscal-receipt.

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "<răspuns dispozitiv>"
    }

    Eroare

    {
      "StatusCode": 424,
      "Message": "Dispozitivul nu răspunde"
    }

    Observații

    • Nu necesită corp de cerere pe DATECS/Daisy.
    • Pe Tremol sunt obligatorii OperatorCode și OperatorPassword (max 4 caractere, padded/truncate).
    • Trebuie să existe un bon nefiscal deschis pentru print/close/cancel.
    POST
    /close-non-fiscal-receipt

    Închide bonul nefiscal

    Închide bonul nefiscal deschis și finalizează tipărirea.

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "<răspuns dispozitiv>"
    }

    Observații

    • Nu necesită corp de cerere.
    POST
    /cancel-non-fiscal-receipt

    Anulează bonul nefiscal

    Anulează bonul nefiscal deschis fără a-l finaliza.

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "<răspuns dispozitiv>"
    }

    Observații

    • Nu necesită corp de cerere.

    Device Communication — Bon fiscal

    Flux: deschide bon → înregistrează vânzări → subtotal → plată → închide. Vezi diferențele DATECS vs Daisy vs Tremol.

    POST
    /open-fiscal-receipt

    Deschide un bon fiscal

    Deschide un bon fiscal sau factură. Pe DATECS și Tremol, invoice=true deschide factură (pe Tremol câmpul este folosit ca FiscalRcpPrintType); pe Daisy câmpul invoice este ignorat. Pe Tremol, OperatorPassword este truncat/padded la exact 4 caractere.

    Corp cerere

    CâmpTipObligatoriuDescriere
    OperatorCodeIntDa1–30
    OperatorPasswordStringDaParola operatorului
    PointOfSaleNumberIntDa1–99999
    InvoiceBoolDatrue=factură (DATECS și Tremol; ignorat pe Daisy)

    Bon fiscal (implicit)

    {
      "OperatorCode": 1,
      "OperatorPassword": "0001",
      "PointOfSaleNumber": 1,
      "Invoice": false
    }

    Factură fiscală (DATECS / Tremol)

    {
      "OperatorCode": 1,
      "OperatorPassword": "0001",
      "PointOfSaleNumber": 1,
      "Invoice": true
    }

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "<răspuns dispozitiv>"
    }
    POST
    /register-free-sale

    Înregistrează o vânzare liberă

    Adaugă un articol pe bon fără PLU predefinit. Suportat pe DATECS și Tremol; Daisy returnează 400 — folosește register-plu-sale.

    Corp cerere

    CâmpTipObligatoriuDescriere
    ProductNameStringDaMax 72 caractere
    TaxCodeIntDa1–8 (VatA–VatH)
    PriceDecimalDaPreț unitar (0–9.999.999,99)
    DepartmentIntDa0–99
    UnitStringDaMax 6 caractere (ex: buc, kg)
    VoidBoolNutrue = storno ultimă vânzare
    QuantityDecimal?NuImplicit 1; 0,01–999.999,999
    DiscountTypeInt?Nu0=None, 1=Majorare Procentuală, 2=Reducere Procentuală, 3=Majorare Fixă, 4=Reducere Fixă
    DiscountValueDecimal?NuObligatoriu cu DiscountType ≠ 0

    Vânzare simplă

    {
      "ProductName": "Produs",
      "TaxCode": 1,
      "Price": 19.99,
      "Department": 1,
      "Unit": "buc"
    }

    Cu cantitate și reducere

    {
      "ProductName": "Cafea",
      "TaxCode": 1,
      "Price": 8.50,
      "Quantity": 2,
      "Department": 1,
      "Unit": "buc",
      "DiscountType": 2,
      "DiscountValue": 10
    }

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "<răspuns dispozitiv>"
    }

    Eroare

    {
      "StatusCode": 400,
      "Message": "Daisy nu suportă vânzări libere; folosește register-plu-sale"
    }
    POST
    /register-plu-sale

    Înregistrează o vânzare prin PLU

    Adaugă pe bon un articol existent în memoria dispozitivului (PLU). Funcționează pe ambele tipuri de case.

    Corp cerere

    CâmpTipObligatoriuDescriere
    PluNumberIntDaPLU trebuie să existe în dispozitiv
    VoidBoolNutrue = storno
    QuantityDecimal?NuImplicit 1
    DiscountTypeInt?Nu0=None, 1=Majorare Procentuală, 2=Reducere Procentuală, 3=Majorare Fixă, 4=Reducere Fixă
    DiscountValueDecimal?NuCu DiscountType ≠ 0

    Minimal

    {
      "PluNumber": 1,
      "Quantity": 2
    }

    Cu reducere

    {
      "PluNumber": 5,
      "Quantity": 1,
      "Void": false,
      "DiscountType": 2,
      "DiscountValue": 15
    }

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "<răspuns dispozitiv>"
    }

    Observații

    • PLU trebuie programat în dispozitiv înainte.
    POST
    /subtotal

    Trimite subtotalul

    Calculează și trimite subtotalul pe dispozitiv. Opțional: tipărire, afișaj, discount/majorare.

    Corp cerere

    CâmpTipObligatoriuDescriere
    PrintBool?NuTipărește subtotalul
    DisplayBool?NuAfișează pe ecran client
    DiscountTypeInt?Nu0=None, 1=Majorare Procentuală, 2=Reducere Procentuală, 3=Majorare Fixă, 4=Reducere Fixă
    DiscountValueDecimal?NuCu DiscountType ≠ 0

    Minimal (fără corp sau {} )

    {}

    Cu opțiuni

    {
      "Print": true,
      "Display": true,
      "DiscountType": 2,
      "DiscountValue": 5
    }

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "<răspuns dispozitiv>"
    }
    POST
    /payment

    Înregistrează plata

    Înregistrează o plată pe bon. Pentru plăți non-cash suma trebuie să acopere exact totalul; restul este permis doar pentru cash.

    Corp cerere

    CâmpTipObligatoriuDescriere
    PaymentModeIntDaMod plată (definit de dispozitiv)
    AmountDecimalDaSuma

    Plată cash

    {
      "PaymentMode": 0,
      "Amount": 100.00
    }

    Plată card (suma exactă)

    {
      "PaymentMode": 1,
      "Amount": 47.50
    }

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "<răspuns dispozitiv>"
    }
    POST
    /close-fiscal-receipt

    Închide bonul fiscal

    Finalizează bonul fiscal după înregistrarea articolelor și plăților.

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "<răspuns dispozitiv>"
    }

    Observații

    • Nu necesită corp de cerere.
    POST
    /cancel-fiscal-receipt

    Anulează bonul fiscal

    Anulează bonul fiscal deschis fără a-l finaliza.

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "<răspuns dispozitiv>"
    }

    Observații

    • Nu necesită corp de cerere.
    POST
    /status-of-the-fiscal-transaction

    Statusul tranzacției fiscale

    Returnează starea curentă a tranzacției fiscale de pe dispozitiv (bon deschis, sumă acumulată etc.).

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": { "<câmpuri răspuns dispozitiv>": "..." }
    }

    Observații

    • Nu necesită corp de cerere.
    • Data este un obiect structurat, nu string.
    POST
    /copy-of-last-fiscal-receipt

    Copia ultimului bon fiscal

    Tipărește o copie a ultimului bon fiscal emis.

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": { "<câmpuri răspuns dispozitiv>": "..." }
    }

    Observații

    • Nu necesită corp de cerere.
    • Pe Tremol funcționează doar cu parametrul FD activat în firmware (service autorizat).
    POST
    /report-z

    Tipărește raportul Z

    Generează raportul fiscal Z, folosit pentru închiderea zilnică. Resetare date.

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "<răspuns dispozitiv>"
    }

    Observații

    • Nu necesită corp de cerere.
    POST
    /report-x

    Tipărește raportul X

    Generează raportul X, fără închidere fiscală.

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "<răspuns dispozitiv>"
    }

    Observații

    • Nu necesită corp de cerere.
    GET
    /ask-for-z-report-structured-information/{number}

    Informații structurate raport Z

    Returnează datele unui raport Z arhivat, în format structurat, după numărul raportului.

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": { "<câmpuri raport Z>": "..." }
    }

    Observații

    • {number} = numărul raportului Z cerut.

    Device Communication — Coduri de bare, linii și afișaj client

    Comenzi pentru elemente individuale de tipărire și pentru afișajul de client al casei de marcat.

    POST
    /display-line-one

    Afișează text pe linia 1 a afișajului client

    Trimite text pe prima linie a afișajului de client al casei de marcat.

    Corp cerere

    CâmpTipObligatoriuDescriere
    TextStringDaTextul afișat (pe Tremol exact 20 caractere — padded/truncat)

    Exemplu

    {
      "Text": "Bun venit!"
    }

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "<răspuns dispozitiv>"
    }
    POST
    /display-line-two

    Afișează text pe linia 2 a afișajului client

    Trimite text pe a doua linie a afișajului de client.

    Corp cerere

    CâmpTipObligatoriuDescriere
    TextStringDaTextul afișat (pe Tremol exact 20 caractere — padded/truncat)

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "<răspuns dispozitiv>"
    }
    POST
    /clear-display

    Golește afișajul client

    Șterge conținutul afișajului de client.

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": "<răspuns dispozitiv>"
    }

    Observații

    • Nu necesită corp de cerere.

    Printare bon complet (o singură cerere)

    Trimite întregul bon într-un singur apel. FiscalLink execută intern: deschidere, articole, subtotal, plăți, închidere.

    Shopify Controller

    Endpoint-urile Shopify sunt apelate de componentele FiscalLink din Shopify POS, care trimit automat un token de sesiune Shopify în antetul Authorization: Bearer. Nu este necesară nicio configurare de credențiale. Vezi ghidul Shopify.

    GET
    /get-shopify-order/{orderId}

    Preia o comandă Shopify

    Returnează detaliile complete ale unei comenzi Shopify (line items, discounturi, transport, tranzacții). orderId este numeric (ex: 123456789), fără prefixe gid://.

    Răspuns

    Succes

    {
      "StatusCode": 200,
      "Data": {
        "id": "gid://shopify/Order/123456789",
        "legacyResourceId": "123456789",
        "name": "#1001",
        "totalPriceSet": { "shopMoney": { "amount": "99.99", "currencyCode": "RON" } },
        "totalDiscountsSet": { "shopMoney": { "amount": "0.00", "currencyCode": "RON" } },
        "note": null,
        "lineItems": {
          "edges": [
            {
              "node": {
                "id": "gid://shopify/LineItem/123",
                "title": "Produs exemplu",
                "quantity": 2,
                "originalUnitPriceSet": { "shopMoney": { "amount": "25.00", "currencyCode": "RON" } },
                "discountedUnitPriceSet": { "shopMoney": { "amount": "22.50", "currencyCode": "RON" } },
                "taxLines": [],
                "discountAllocations": []
              }
            }
          ]
        },
        "transactions": [
          { "gateway": "manual", "amount": "45.00", "kind": "SALE" }
        ],
        "discountApplications": { "edges": [] },
        "shippingLines": {
          "edges": [
            {
              "node": {
                "id": "gid://shopify/ShippingLine/1",
                "title": "Standard",
                "originalPriceSet": { "shopMoney": { "amount": "15.00", "currencyCode": "RON" } },
                "discountedPriceSet": { "shopMoney": { "amount": "0.00", "currencyCode": "RON" } },
                "discountAllocations": []
              }
            }
          ]
        }
      }
    }

    Eroare 401 — Token de sesiune lipsă

    {
      "StatusCode": 401,
      "Message": "Token-ul de sesiune Shopify lipsește. Actualizați aplicația FiscalLink în Shopify POS și încercați din nou."
    }

    Eroare 401/403 — Cerere respinsă de Shopify

    {
      "StatusCode": 401,
      "Message": "Shopify a respins cererea. Asigurați-vă că aplicația FiscalLink este instalată pe magazinul dvs."
    }

    Eroare 404 — Comandă inexistentă

    {
      "StatusCode": 404,
      "Message": "Order not found"
    }

    Legacy Controller (POST /Receipt)

    Endpoint compatibil FiscalNet/Fisco. Acceptă array de stringuri în format FiscalNet (^) sau INP (;,). Formatul este detectat automat. Returnează un format diferit de restul API-ului.

    POST
    /Receipt

    Tipărește bon în format FiscalNet sau INP

    Primește un array de comenzi în format legacy. FiscalNet folosește ^ ca delimitator; INP folosește , și ;. Vezi documentația FiscalNet/Fisco pentru comenzi.

    Corp cerere

    FiscalNet (exemplu)

    [
      "CF^RO12345678",
      "S^Produs^1000^1000^pcs^1^1",
      "ST^",
      "P^1^1000"
    ]

    INP (exemplu)

    [
      "S,1,______,_,__;Produs;10.00;1.000;1;1;2;0;0;pcs",
      "T,1,______,_,__;0;10.00;;;;"
    ]

    Răspuns

    Succes

    {
      "ReceiptStatus": "true",
      "ReceiptNumber": "12345",
      "ErrorCode": "",
      "ErrorInfo": ""
    }

    Eroare

    {
      "ReceiptStatus": "false",
      "ReceiptNumber": "",
      "ErrorCode": "123",
      "ErrorInfo": "Descrierea erorii"
    }

    Observații

    • Răspuns diferit de API standard: receiptStatus, receiptNumber, errorCode, errorInfo.
    • HTTP 200 chiar și la eroare; succes/eroare se citesc din receiptStatus.
    • Comenzi comune: CF (CIF), S (Sale), ST (Subtotal), P (Payment), X^ (Report X), Z^ (Report Z).

    Fluxuri uzuale

    Bon nefiscal

    1. POST /open-non-fiscal-receipt
    2. POST /print-non-fiscal-text (repetat)
    3. POST /close-non-fiscal-receipt (sau /cancel-non-fiscal-receipt pentru anulare)

    Bon fiscal

    1. POST /open-fiscal-receipt
    2. POST /register-free-sale sau /register-plu-sale
    3. POST /subtotal
    4. POST /payment
    5. POST /close-fiscal-receipt (sau /cancel-fiscal-receipt pentru anulare)

    Securitate

    • Folosește endpoint-ul HTTPS pentru integrarea standard, atunci când este posibil.
    • API-ul local HTTP este accesibil doar în rețeaua locală și nu folosește TLS.
    • Datele de intrare sunt validate înainte de comunicarea cu dispozitivul.

    Pentru asistență: [email protected]