Skip to main content

Referința comenzilor

Fiecare comandă e un JSON trimis cu execute. Răspunsul vine o singură dată, tot ca JSON. Datele comenzilor sunt aceleași ca în API-ul local al terminalului: sale.create primește exact corpul lui POST /api/sales. Toate comenzile, cu cereri în Kotlin și exemple de răspuns, sunt și în referința API, în tabul „Integrare Android”.

Cererea​

{
"v": 1,
"requestId": "7f3c…",
"idempotencyKey": "b2e1…",
"command": "sale.create",
"payload": { }
}
CâmpCe e
vVersiunea contractului. Azi 1.
requestIdIdentificatorul acestei trimiteri. Îl folosești la cancel și îl regăsești în evenimente.
idempotencyKeyCheia comenzii. Obligatorie la comenzile care schimbă ceva (vezi tabelul). SDK-ul generează una dacă nu dai tu.
commandNumele comenzii.
payloadDatele comenzii. La comenzile de citire devin parametrii de căutare.

Răspunsul​

{
"v": 1,
"requestId": "7f3c…",
"command": "sale.create",
"httpStatus": 200,
"replayed": false,
"success": true,
"data": { "saleId": "…", "receiptNumber": 42, "fiscalCode": "…" },
"error": null,
"timestamp": "2026-09-28T13:12:23.076Z"
}
  • success și error sunt mereu în răspuns. La eroare, error are code și message; codurile sunt mai jos.
  • httpStatus, command și replayed lipsesc din erorile date de SDK și de legătura cu POSfix (POSFIX_*, INVALID_RESPONSE, ENGINE_NOT_READY, RESPONSE_TOO_LARGE, INTERNAL_ERROR), deci le citești cu opt….
  • replayed: true înseamnă că răspunsul vine din jurnal: comanda cu aceeași cheie fusese deja executată.

Comenzile​

ComandăCe faceCheie obligatorie
statusStarea terminalului și dacă POSfix e activatnu
sale.createVânzare fiscală: bon la SFS, plata, tipărireada
sale.listVânzările, filtrate după dată, stare, casiernu
sale.getO vânzare cu articolele ei (payload.id)nu
sale.reprintTipărește din nou bonul: originalul, dacă n-a ieșit, altfel un singur DUPLICAT (payload.id)da
cash.in / cash.outIntroducere / extragere de numerar, cu bon de serviciuda
report.x / report.zRaportul X / raportul Z. Z închide și ziua bancarăda
report.periodicRaport periodic pe un intervalda
fiscal.operationsJurnalul operațiunilor fiscalenu
fiscal.dailySummaryTotalurile zilei fiscale curentenu
print.textText nefiscal la imprimantăda
card.voidAnulează o plată cu cardul, înainte de Zda
card.refundRambursează o plată cu cardul, după Zda
catalog.products / catalog.categories / catalog.healthCatalogul terminaluluinu
reference.vatRates / reference.cashiersCotele TVA / casieriinu
loyalty.identifyClientul de fidelitate, după telefon sau cardnu
request.resultRăspunsul unei comenzi trimise înainte, după cheia eida

status​

Răspunde mereu, și pe un terminal neactivat.

{
"apiVersion": 1,
"activated": true,
"headlessMode": true,
"terminal": {
"fiscalStatus": "ready",
"hasOpenShift": true,
"hasCardTerminal": true,
"version": "1.0.34+42"
}
}

terminal și headlessMode lipsesc cât POSfix nu e activat. hasCardTerminal spune dacă se poate plăti cu cardul pe acest aparat.

sale.create​

O vânzare de 90 lei: două articole, plătite 50 lei cash și 40 lei cu cardul.

{
"items": [
{ "name": "Pizza Margherita", "unitPrice": 70.00, "quantity": 1, "vatCode": "B" },
{ "name": "Apă plată 0,5 l", "unitPrice": 10.00, "quantity": 2, "vatCode": "B" }
],
"payments": [
{ "type": "1", "amount": 50.00 },
{ "type": "2", "amount": 40.00 }
],
"amountReceived": 90.00,
"printReceipt": true
}
  • vatCode: litera cotei TVA configurate pe terminal. Lista o dă reference.vatRates.
  • payments[].type: codul ACPS al metodei — 1 numerar, 2 card, 5 tichete de masă, 7 alt instrument. Lista completă e în API-ul local.
  • amountReceived trebuie să fie egal cu suma plăților. Pentru rest, trimite banii primiți în numerar, iar POSfix calculează restul.
  • Plata 2 sau 5 fără rrn pornește terminalul de card. Cu rrn completat, POSfix consideră plata deja făcută în altă parte. Anularea și rambursarea ei le faci tot acolo: card.void și card.refund caută doar printre plățile încasate de POSfix.
  • cartDiscount: { "percent": 10 } sau { "absolute": 5.00 } pe tot bonul.
  • loyalty: { "memberId": "…" } din loyalty.identify. Vânzarea adună punctele clientului.

Răspunsul are, între altele: saleId, receiptNumber, reportNumber, fiscalCode, total, change, vatBreakdown, paymentBreakdown și cardPaymentResult (rrn, authCode, cardMask, chequeNumber, isMiaQr). Păstrează chequeNumber și rrn: primul îți trebuie la card.void, al doilea la card.refund. chequeNumber poate lipsi, fiindcă nu toate băncile îl întorc.

card.void și card.refund​

{ "command": "card.void", "payload": { "chequeNumber": "000123", "amount": 40.00, "reason": "Clientul a renunțat" } }
{ "command": "card.refund", "payload": { "originalRrn": "627110323023", "amount": 40.00 } }

Anularea merge până la raportul Z, rambursarea după el, iar amount poate fi și o parte din plată. Niciuna nu emite bon fiscal. Dacă terminalul nu cunoaște cecul sau RRN-ul, răspunsul e NOT_FOUND.

Comenzile de citire​

La sale.list, fiscal.operations și catalog.*, câmpurile din payload devin parametrii de căutare ai rutei locale:

{ "command": "sale.list", "payload": { "dateFrom": "2026-09-28", "limit": 20 } }

loyalty.identify​

{ "command": "loyalty.identify", "payload": { "phoneOrBarcode": "069123456" } }

Răspunsul are memberId, name, tierName, discountPercent, pointsBalance și bonusAvailable. Reducerea clientului o aplici tu, în cartDiscount, ca să știi totalul exact înainte să ceri plata. La sale.create trimiți apoi loyalty.memberId, iar punctele se adună singure. Căutarea cere internet, iar un client negăsit întoarce LOYALTY_MEMBER_NOT_FOUND.

Cheia de idempotență​

Cheia te apără de bonul dublu. Folosește o cheie nouă pentru fiecare vânzare nouă și aceeași cheie când repeți aceeași vânzare. Cheia rămâne legată de primul ei răspuns, chiar dacă e o eroare: după un card refuzat, încercarea următoare e o vânzare nouă, cu cheie nouă.

  • Aceeași cheie și același payload: primești răspunsul salvat, cu replayed: true. Bonul nu se emite a doua oară.
  • Aceeași cheie și alt payload: IDEMPOTENCY_KEY_REUSE_MISMATCH.
  • Aceeași cheie cât prima comandă încă rulează: REQUEST_IN_PROGRESS. Nimic nu se execută a doua oară.
  • request.result cu aceeași cheie: afli ce s-a întâmplat, fără să execuți nimic. REQUEST_IN_PROGRESS înseamnă că încă rulează; NOT_FOUND, că nu s-a primit nicio comandă cu cheia asta.

Cheile sunt separate pe aplicație: altă aplicație nu vede răspunsurile tale. Cu SDK-ul, posfix.requestResult(key) { … } trimite request.result.

Exemplu. Ai trimis o vânzare, iar aplicația ta a căzut înainte de răspuns. La repornire trimiți request.result cu cheia vânzării:

  • success: true: bonul s-a emis, nu-l mai trimite.
  • REQUEST_IN_PROGRESS: vânzarea încă rulează, de exemplu așteaptă cardul. Întreabă din nou peste câteva secunde.
  • NOT_FOUND: vânzarea n-a ajuns la POSfix. O poți trimite din nou, cu aceeași cheie.

Codurile de eroare​

Codurile specifice integrării:

CodCând apare
TERMINAL_NOT_ACTIVATEDPOSfix nu e activat pe acest terminal. Activează-l o dată, pe ecranul lui.
VALIDATION_ERRORLipsește un câmp obligatoriu, de exemplu idempotencyKey sau payload.id.
UNKNOWN_COMMANDNumele comenzii nu există.
INVALID_JSONCererea nu e un obiect JSON.
SALE_CANCELLEDVânzarea a fost oprită cu cancel înainte de bonul fiscal. Cardurile aprobate au fost anulate.
COMMAND_TIMEOUTComanda rulează de peste 150 s. Continuă în POSfix: rezultatul îl afli cu request.result.
NOT_FOUNDrequest.result: nicio comandă cu această cheie. card.void / card.refund: terminalul nu cunoaște cecul sau RRN-ul.
REQUEST_IN_PROGRESSO comandă cu aceeași cheie încă rulează. Întreabă din nou cu request.result.
LOYALTY_MEMBER_NOT_FOUNDClientul nu există sau serviciul de fidelitate nu răspunde.
ENGINE_NOT_READYPOSfix n-a pornit în 45 s de la trimiterea comenzii.
RESPONSE_TOO_LARGERăspunsul depășește 400 KB. Cere mai puține rânduri, cu limit și offset.
POSFIX_NOT_CONNECTEDSDK: ai trimis o comandă înainte de connect().
POSFIX_DISCONNECTEDSDK: POSfix s-a oprit cât aștepta comanda. Verifică rezultatul cu request.result.
INVALID_RESPONSESDK: răspunsul POSfix nu s-a putut citi.
INTERNAL_ERROREroare neprevăzută în POSfix. Verifică rezultatul cu request.result înainte să retrimiți.

Codurile care vin din API-ul local și apar cel mai des:

CodCând apare
FISCAL_DAY_EXPIREDZiua fiscală a expirat: trimite report.z.
TERMINAL_NOT_FISCALTerminalul nu e încă fiscalizat.
BUSYAltă operațiune fiscală e în curs. Reîncearcă peste câteva secunde, cu aceeași cheie.
CARD_PAYMENT_DECLINEDBanca a refuzat plata sau clientul a renunțat pe terminal.
CARD_PAYMENT_ERRORPlata cu cardul a eșuat din alt motiv decât refuzul băncii.
CARD_TERMINAL_UNAVAILABLEPe acest aparat nu se poate plăti cu cardul.
DUPLICATE_LIMIT_REACHEDDuplicatul bonului a fost deja tipărit.
IDEMPOTENCY_KEY_REUSE_MISMATCHAceeași cheie, trimisă cu alt payload.
MEV_ERROR / MEV_TIMEOUT / MEV_UNAVAILABLESFS a refuzat bonul sau nu a răspuns.

Restul sunt în Referința API.

Fără SDK​

Dacă nu vrei dependența SDK-ului, folosești direct interfața AIDL:

  1. Descarcă fișierele AIDL și dezarhivează-le în src/main/aidl/.
  2. În manifest adaugă <queries><package android:name="md.posfix.posfix" /></queries>. Fără el, pe Android 11+ legarea întoarce false.
  3. Leagă-te cu Intent("md.posfix.integration.BIND").setPackage("md.posfix.posfix") și BIND_AUTO_CREATE. Pe Android 14+ adaugă și BIND_ALLOW_ACTIVITY_STARTS, altfel POSfix nu poate deschide ecranul de plată al băncii.
  4. Apelezi execute(requestJson, callback). E un apel asincron: rezultatul vine pe IPosfixCallback.onResult.

Pe lângă execute, interfața are getApiVersion(), cancel(requestId) și registerListener / unregisterListener pentru evenimente.