Откуцај
Корисничко упутство АПИ документација EN Пријава

Откуцај API v1

REST АПИ за издавање фискалних рачуна, рефундације и копије, слање рачуна е-поштом, извештаје о промету и рад са артиклима. Све што ради каса у прегледачу може и ваш софтвер: веб продавница, ERP, наплатни систем.

ДЕМО РЕЖИМ JSON · UTF-8 240 захтева/мин

За АИ агенте и језичке моделе

Машински читљиви описи овог АПИ-ја постоје и одржавају се уз ову страницу.

Увод и базна адреса

Базна адреса свих позива:

https://otkucaj.com/api/v1

Сви захтеви и одговори су JSON у UTF-8 кодирању, осим журнала који се враћа као чист текст (text/plain). Комуникација иде искључиво преко HTTPS-а. Износи у JSON-у користе тачку као децимални знак (2400.00), јер је то JSON стандард; на самом рачуну и у журналу износи су у српском формату (2.400,00).

Версионисање. Верзија АПИ-ја је део путање: /api/v1. Постојећа поља и понашање се унутар v1 не мењају уназад некомпатибилно; нове могућности се додају као нова, опциона поља. Ако икада буде потребан прекид уговора, он ће живети на путањи /api/v2, а v1 ће наставити да ради.

Завршна коса црта се игнорише: /api/v1/items/ и /api/v1/items су иста путања. Непозната путања враћа 404 са кодом not_found.

Откуцај је у поступку припреме пријаве за одобрење код Пореске управе. Док траје демо режим, сваки рачун носи јасну ознаку ДЕМО, бројеве из демо бројача и линк за проверу на овом сајту, а не на suf.purs.gov.rs. Интеграцију можете развити и тестирати одмах: преласком на Л-ПФР или В-ПФР у уговору АПИ-ја се не мења ништа, само рачуни постају прави.

Брзи почетак

Од кључа до првог рачуна у три корака.

  1. Проверите везу и стање ПФР-а.
    curl -H "Authorization: Bearer kasir_vas_kljuc" \
      https://otkucaj.com/api/v1/status
  2. Издајте први рачун.
    curl -X POST https://otkucaj.com/api/v1/invoices \
      -H "Authorization: Bearer kasir_vas_kljuc" \
      -H "Content-Type: application/json" \
      -d '{
        "invoiceType": "Normal",
        "transactionType": "Sale",
        "cashier": "Веб продавница",
        "payments": [{ "paymentType": "Card", "amount": 2400.00 }],
        "items": [
          { "name": "Мајица", "quantity": 2, "unitPrice": 1200.00, "label": "Ђ" }
        ]
      }'

    Одговор је 201 са целим рачуном: запамтите uuid и pfr.invoiceNumber. Пун облик одговора је код POST /invoices.

  3. Преузмите журнал (текст рачуна спреман за штампу на 40 колона):
    curl -H "Authorization: Bearer kasir_vas_kljuc" \
      https://otkucaj.com/api/v1/invoices/9b2f6c1e-4d3a-4f0b-9a77-2c8e5d1f6a30/journal
GET

/api/v1/status

Стање апликације и везе са ПФР-ом. Употребите за проверу конфигурације и за надзор.

Аутентификација
Authorization: Bearer <api-kljuc>
Параметри
нема
Одговор
200, JSON

Захтев

curl -H "Authorization: Bearer kasir_vas_kljuc" \
  https://otkucaj.com/api/v1/status

Одговор

{
  "app": "Otkucaj",
  "version": "1.2.0",
  "esirNumber": "у поступку одобравања/1.0",
  "pfr": {
    "ok": true,
    "mode": "demo",
    "uid": "DMX7K2QP",
    "message": "Демо ПФР симулатор: активан"
  },
  "time": "2026-08-19T14:00:00.000+02:00"
}

pfr.mode је demo, lpfr или vpfr, у зависности од подешавања инсталације. Када је режим Л-ПФР или В-ПФР, објекат pfr преноси и податке које врати сам процесор фискалних рачуна; ok: false са поруком значи да ПФР тренутно није доступан. esirNumber се склапа од броја и верзије ЕСИР-а из Подешавања.

  • 401 unauthorized
  • 429 rate_limited

Аутентификација

Сваки захтев мора да носи заглавље:

Authorization: Bearer <api-kljuc>

Кључеве генерише администратор у апликацији, у одељку Подешавања · АПИ кључеви. Кључ се приказује само једном, у тренутку генерисања: у бази се чува искључиво његов SHA-256 отисак, па изгубљен кључ није могуће поново прочитати, већ се опозива и генерише нов. Опозив је тренутан: сваки следећи захтев са опозваним кључем добија 401.

Кључеви нису појединачно ограничени по правима: сваки активан кључ може да позове сваку путању. Ако желите да раздвојите системе, генеришите засебан кључ по систему и опозовите га појединачно кад затреба; то је ниво изолације који апликација заиста нуди.

Чувајте кључ само на серверу. Кључ никада не сме да се нађе у JavaScript коду који се извршава у прегледачу купца, у мобилној апликацији, у јавном репозиторијуму нити у URL параметрима. Свако ко види кључ може да издаје рачуне у ваше име. Позиве ка Откуцају увек шаљите са свог сервера.
GET

/api/v1/me

Подаци о кључу којим је захтев потписан: назив, сачувани префикс, датуми, ограничење броја захтева које се стварно примењује и шта кључ сме. Корисно за проверу да интеграција користи онај кључ који мислите да користи.

Аутентификација
Authorization: Bearer <api-kljuc>
Параметри
нема
Одговор
200, JSON

Захтев

curl -H "Authorization: Bearer kasir_vas_kljuc" \
  https://otkucaj.com/api/v1/me

Одговор

{
  "key": {
    "id": 3,
    "label": "Веб продавница",
    "prefix": "kasir_7f3a",
    "active": true,
    "createdAt": "2026-08-01 09:12:44",
    "lastUsedAt": "2026-08-19 13:58:02"
  },
  "rateLimit": { "requests": 240, "windowSeconds": 60 },
  "scope": "full",
  "permissions": [
    "invoices:read", "invoices:write", "items:read", "items:write",
    "reports:read", "taxLabels:read", "categories:read"
  ],
  "note": "API keys are not scoped individually: every active key may call every route."
}
Три поштене напомене о овом одговору.
prefix је онај кратки низ који видите и у Подешавањима; сам кључ се нигде не чува у читљивом облику и никада се не враћа.
scope је увек "full", а permissions набраја шта сваки активан кључ стварно може. То није по кључу подесиво: систем права по кључу не постоји, па га овај одговор ни не глуми.
lastUsedAt је претходни позив, не овај: време текућег позива се уписује тек пошто је ред кључа прочитан.
  • 401 unauthorized
  • 429 rate_limited

Ограничења и понашање

ОграничењеВредностШта се дешава при прекорачењу
Број захтева по кључу240 у 60 секунди429 rate_limited
Слање рачуна е-поштом по кључу30 порука на сат429 rate_limited, посебна порука
Ставки по рачуну1 до 500422 validation
Рачуна по страни (GET /invoices)100следећа страна кроз page
Артикала по позиву (GET /items)1.000вишак се не враћа, страничења нема
Дужина назива ставке и артикла200 знакова422 validation
Дужина buyerId64 знака422 validation
Дужина adText500 знакова422 validation

Прозор ограничења је фиксан, не клизни: први захтев отвара прозор од 60 секунди и броји до 240; кад прозор истекне, бројач креће испочетка. Прекорачење враћа HTTP 429:

{ "error": { "code": "rate_limited", "message": "Rate limit exceeded (240 requests/minute)." } }

Када добијете 429, сачекајте до истека текућег прозора па наставите. За уобичајен рад продавнице (један рачун по поруџбини) ово ограничење се у пракси не досеже. Ограничење које се примењује и оно које пријављује GET /me долазе из истог места у коду, па не могу да се разиђу.

Рачуни

Седам путања око рачуна: издавање, листа, детаљ, журнал, рефундација, копија и слање е-поштом.

POST

/api/v1/invoices

Издавање рачуна. Захтев се валидира, прослеђује ПФР-у, а рачун се уписује у базу тек када га ПФР потпише. Одговор 201 враћа цео рачун, укључујући журнал и линк за проверу.

Аутентификација
Authorization: Bearer <api-kljuc>
Тело
JSON, Content-Type: application/json
Одговор
201, цео рачун
ПољеТипОбавезноОпис
invoiceTypestringне (подразумевано Normal)Normal (промет), Advance (аванс), ProForma (предрачун), Training (обука), Copy (копија)
transactionTypestringне (подразумевано Sale)Sale (продаја) или Refund (рефундација)
items[]arrayда1 до 500 ставки, збир мора бити већи од 0
items[].namestringдаНазив ставке, до 200 знакова
items[].unitstringнеЈединица мере, подразумевано ком
items[].quantitynumberдаВећа од 0 и највише 999.999; заокружује се на 3 децимале (нпр. 0.375)
items[].unitPricenumberдаЈединична цена са ПДВ, 0 или више, заокружује се на 2 децимале
items[].totalAmountnumberнеИзнос реда. Сервер га сам израчунава као количина × цена, послата вредност се не користи
items[].labelstringдаПореска ознака из шифарника у важности, види GET /tax-labels. Стопе одређује искључиво ПФР
items[].gtinstringнеГТИН или бар-код, 8 до 14 цифара
items[].itemIdnumberнеИД артикла из шифарника артикала. Само се памти уз ред рачуна као веза ка артиклу; цена и назив се увек узимају из самог захтева
payments[]arrayнеНачини плаћања. Ако изостане или је празан, цео износ иде као Cash
payments[].paymentTypestringдаCash (готовина), Card (платна картица), Check (чек), WireTransfer (пренос на рачун), Voucher (ваучер), MobileMoney (инстант плаћање), Other (друго безготовинско)
payments[].amountnumberдаИзнос. Ставке са износом 0 или мањим се одбацују. Ако после тога остане тачно једно плаћање, његов износ се сам поставља на укупан износ рачуна; ако их је више, збир мора да одговара збиру ставки (толеранција 0,01)
cashierstringнеИме касира на рачуну; за АПИ позиве подразумевано API
buyerIdstringусловноИД купца, до 64 знака, облик шифра:вредност. Шифарник: 10 ПИБ, 11 ЈМБГ, 12 ПИБ:ЈБКЈС, 20 лична карта, 23 пасош, 30 страни пасош, 40 страни ТИН. Обавезан за сваку рефундацију
buyerCostCenterIdstringнеОпционо поље купца (место трошка)
referentDocumentNumberstringусловноПФР број оригиналног рачуна. Обавезан за Refund и за Copy
referentDocumentDTstringнеВреме оригиналног рачуна, YYYY-MM-DD HH:MM:SS
dateAndTimeOfIssuestringнеЕСИР време, YYYY-MM-DD HH:MM:SS. Користи се за аванс када је уплата стигла раније него што се рачун издаје (нпр. пренос на рачун прокњижен сутрадан); штампа се на рачуну као „ЕСИР време“
adTextstringнеРекламни текст, до 500 знакова, штампа се испод краја рачуна. При затварању аванса овде иде број и датум последњег авансног рачуна

Захтев

curl -X POST https://otkucaj.com/api/v1/invoices \
  -H "Authorization: Bearer kasir_vas_kljuc" \
  -H "Content-Type: application/json" \
  -d '{
    "invoiceType": "Normal",
    "transactionType": "Sale",
    "cashier": "Веб продавница",
    "payments": [{ "paymentType": "Card", "amount": 2400.00 }],
    "items": [
      { "name": "Мајица", "unit": "ком", "quantity": 2, "unitPrice": 1200.00,
        "label": "Ђ", "gtin": "8600000000000" }
    ]
  }'

Одговор 201

{
  "uuid": "9b2f6c1e-4d3a-4f0b-9a77-2c8e5d1f6a30",
  "invoiceType": "Normal",
  "transactionType": "Sale",
  "cashier": "Веб продавница",
  "total": 2400.0,
  "payments": [
    { "paymentType": "Card", "amount": 2400.0 }
  ],
  "items": [
    {
      "name": "Мајица",
      "unit": "ком",
      "quantity": 2.0,
      "unitPrice": 1200.0,
      "totalAmount": 2400.0,
      "label": "Ђ",
      "gtin": "8600000000000"
    }
  ],
  "taxItems": [
    { "label": "Ђ", "categoryName": "О-ПДВ", "rate": 20.0, "amount": 400.0, "total": 2400.0 }
  ],
  "buyerId": null,
  "referentDocumentNumber": null,
  "referentDocumentDT": null,
  "pfr": {
    "mode": "demo",
    "demo": true,
    "requestedBy": "DMX7K2QP",
    "signedBy": "DMX7K2QP",
    "invoiceNumber": "DMX7K2QP-DMX7K2QP-302",
    "invoiceCounter": "143/302ПП",
    "sdcDateTime": "2026-08-19 14:02:33",
    "verificationUrl": "https://otkucaj.com/verify?id=9b2f6c1e-4d3a-4f0b-9a77-2c8e5d1f6a30"
  },
  "journal": "======== ФИСКАЛНИ РАЧУН =======\n...",
  "createdAt": "2026-08-19 14:02:33"
}

Овај облик враћају и GET /invoices/{uuid}, сваки елемент листе рачуна, и путање за рефундацију и копију. Значење ПФР поља: invoiceNumber је ПФР број рачуна (облик JID-JID-N), invoiceCounter је бројач (нпр. 143/302ПП: 143. рачун врсте Промет-Продаја од укупно 302), sdcDateTime је ПФР време, verificationUrl је линк ка страници за проверу рачуна, demo каже да ли је рачун издат у демо режиму. taxItems је пореска рекапитулација по ознакама, са стопом, износом пореза и основицом; стопе долазе искључиво од ПФР-а, никада из вашег захтева.

  • 400 bad_request
  • 401 unauthorized
  • 422 validation
  • 429 rate_limited
  • 502 pfr_error

На 502 рачун није издат и ништа није уписано у базу.

GET

/api/v1/invoices

Листа рачуна, најновији први, до 100 по страни. Сваки елемент је цео рачун, у истом облику као одговор на POST /invoices, заједно са журналом.

Аутентификација
Authorization: Bearer <api-kljuc>
Параметри
у упитном делу адресе (query string)
Одговор
200, JSON
ПараметарТипОпис
fromstringДатум од, YYYY-MM-DD (укључује цео дан од 00:00:00)
tostringДатум до, YYYY-MM-DD (укључује цео дан до 23:59:59)
typestringВрста рачуна: Normal, Advance, ProForma, Training, Copy
pagenumberСтрана, од 1 (подразумевано 1), по 100 рачуна
Како се параметри понашају. За разлику од извештаја, from и to се овде не проверавају форматом: неисправан датум неће вратити 422, него празну или неочекивану листу. Непозната вредност у type се тихо игнорише и филтер се не примењује. Шаљите датуме у облику YYYY-MM-DD и врсту из горњег списка.

Захтев

curl -H "Authorization: Bearer kasir_vas_kljuc" \
  "https://otkucaj.com/api/v1/invoices?from=2026-08-01&to=2026-08-19&type=Normal&page=1"

Одговор

{
  "page": 1,
  "perPage": 100,
  "invoices": [
    { "uuid": "9b2f6c1e-4d3a-4f0b-9a77-2c8e5d1f6a30", "invoiceType": "Normal", "...": "..." }
  ]
}

Празна листа значи да за задате филтере нема рачуна. Укупан број рачуна се не враћа: страничите док не добијете мање од 100 елемената.

  • 401 unauthorized
  • 429 rate_limited
GET

/api/v1/invoices/{uuid}

Један рачун по UUID-у, у пуном облику описаном код POST /invoices.

Аутентификација
Authorization: Bearer <api-kljuc>
Путања
uuid: 36 знакова, мала слова и цифре са цртицама, онако како га АПИ и врати
Одговор
200, цео рачун

Захтев

curl -H "Authorization: Bearer kasir_vas_kljuc" \
  https://otkucaj.com/api/v1/invoices/9b2f6c1e-4d3a-4f0b-9a77-2c8e5d1f6a30

Одговор

{
  "uuid": "9b2f6c1e-4d3a-4f0b-9a77-2c8e5d1f6a30",
  "invoiceType": "Normal",
  "transactionType": "Sale",
  "total": 2400.0,
  "pfr": { "invoiceNumber": "DMX7K2QP-DMX7K2QP-302", "...": "..." },
  "journal": "======== ФИСКАЛНИ РАЧУН =======\n...",
  "createdAt": "2026-08-19 14:02:33"
}

UUID који не постоји враћа 404 са кодом not_found. UUID исписан великим словима не одговара облику путање и завршава као непозната путања, такође 404: шаљите га тачно онако како вам га је АПИ вратио.

  • 401 unauthorized
  • 404 not_found
  • 429 rate_limited
GET

/api/v1/invoices/{uuid}/journal

Журнал рачуна као чист текст (text/plain; charset=utf-8), 40 колона, ћирилицом, спреман за термални штампач од 58 или 80 mm. Идентичан журналу који апликација сама штампа. Ово је једини одговор који није JSON.

Аутентификација
Authorization: Bearer <api-kljuc>
Параметри
нема
Одговор
200, text/plain; charset=utf-8

Захтев

curl -H "Authorization: Bearer kasir_vas_kljuc" \
  https://otkucaj.com/api/v1/invoices/9b2f6c1e-4d3a-4f0b-9a77-2c8e5d1f6a30/journal

Одговор, скраћено

======== ФИСКАЛНИ РАЧУН =======
              106884584
           Пример д.о.о.
           Продавница 1
       Булевар ослобођења 1
              Београд
Касир:           Веб продавница
ЕСИР број: у поступку одобравања/1.0
------ ПРОМЕТ - ПРОДАЈА -------
Артикли
========================================
Назив   Цена         Кол.         Укупно
Мајица /ком (Ђ)
        1.200,00     2          2.400,00
----------------------------------------
За уплату:                      2.400,00
Платна картица:                 2.400,00
========================================
Ознака  Име      Стопа          Порез
Ђ       О-ПДВ    20,00%         400,00
----------------------------------------
Укупан износ пореза:              400,00
========================================
ПФР време:         19.08.2026. 14:02:33
ПФР број рачуна:   DMX7K2QP-DMX7K2QP-302
Бројач рачуна:               143/302ПП
======== КРАЈ ФИСКАЛНОГ РАЧУНА ========

Нефискалне врсте (Копија, Предрачун, Обука) уместо заглавља и краја фискалног рачуна носе линију „ОВО НИЈЕ ФИСКАЛНИ РАЧУН“, коју апликација при штампи увећава на двоструку величину фонта. Копија-Рефундација додатно штампа простор „Потпис купца“ за потпис при враћању новца.

  • 401 unauthorized
  • 404 not_found
  • 429 rate_limited
POST

/api/v1/invoices/{uuid}/refund

Рефундација целог постојећег рачуна, једним позивом. Ставке се преузимају са оригинала, референтни број и време показују на оригинал, а нов рачун се издаје кроз исти пут као и сваки други. Ово је исто што ради дугме Рефундација у апликацији.

Аутентификација
Authorization: Bearer <api-kljuc>
Тело
опционо, JSON
Одговор
201, нов рачун рефундације
Услови. Рефундира се само рачун који је Normal или Advance, врсте трансакције Sale и у статусу издат. Копија, предрачун, обука и већ издата рефундација се одбијају са 422. Ово су исти услови под којима се дугме приказује у апликацији.
Поље телаТипПодразумеваноОпис
buyerIdstringкупац са оригиналаИдентификација купца. Обавезна је за рефундацију: ако је нема ни у телу ни на оригиналу, одговор је 422 са поруком која објашњава да за поништавање сопственог рачуна треба унети свој ПИБ
buyerCostCenterIdstringса оригиналаМесто трошка купца
paymentsarrayплаћања са оригиналаНачини враћања новца. Збир мора да одговара укупном износу рачуна
cashierstringAPIИме касира на рачуну рефундације
adTextstringпразноРекламни текст, до 500 знакова

Ставке се увек узимају са оригиналног рачуна, у целости, и не могу се задати телом захтева. За делимичну рефундацију (враћа се само део ставки или мања количина) користите POST /invoices са transactionType: "Refund", где сами наводите шта се враћа.

Захтев

curl -X POST \
  https://otkucaj.com/api/v1/invoices/9b2f6c1e-4d3a-4f0b-9a77-2c8e5d1f6a30/refund \
  -H "Authorization: Bearer kasir_vas_kljuc" \
  -H "Content-Type: application/json" \
  -d '{ "buyerId": "10:123456789", "cashier": "Веб продавница" }'

Одговор 201

{
  "uuid": "1c77a0d5-9b12-4f8e-b3aa-0d5e7c9f4411",
  "invoiceType": "Normal",
  "transactionType": "Refund",
  "cashier": "Веб продавница",
  "total": 2400.0,
  "buyerId": "10:123456789",
  "referentDocumentNumber": "DMX7K2QP-DMX7K2QP-302",
  "referentDocumentDT": "2026-08-19 14:02:33",
  "pfr": {
    "invoiceNumber": "DMX7K2QP-DMX7K2QP-318",
    "invoiceCounter": "12/318РП",
    "...": "..."
  },
  "journal": "======== ФИСКАЛНИ РАЧУН =======\n...",
  "createdAt": "2026-08-19 16:41:02"
}
  • 400 bad_request
  • 401 unauthorized
  • 404 not_found
  • 422 validation
  • 429 rate_limited
  • 502 pfr_error
POST

/api/v1/invoices/{uuid}/copy

Издавање копије постојећег рачуна, кроз исти пут који користи дугме Копија у апликацији. Копија носи све ставке и плаћања оригинала, референтни број и време оригинала, и његову идентификацију купца.

Аутентификација
Authorization: Bearer <api-kljuc>
Тело
нема, шаље се празан POST
Одговор
201, нова копија
Услови. Копира се само рачун у статусу издат који и сам није копија. Копија копије се одбија са 422, исто као у апликацији.

Захтев

curl -X POST \
  https://otkucaj.com/api/v1/invoices/9b2f6c1e-4d3a-4f0b-9a77-2c8e5d1f6a30/copy \
  -H "Authorization: Bearer kasir_vas_kljuc"

Одговор 201

{
  "uuid": "6d4b1f92-2a70-4c31-8f5e-7b1c0a93de84",
  "invoiceType": "Copy",
  "transactionType": "Sale",
  "total": 2400.0,
  "referentDocumentNumber": "DMX7K2QP-DMX7K2QP-302",
  "referentDocumentDT": "2026-08-19 14:02:33",
  "pfr": { "invoiceNumber": "DMX7K2QP-DMX7K2QP-319", "...": "..." },
  "journal": "ОВО НИЈЕ ФИСКАЛНИ РАЧУН\n...",
  "createdAt": "2026-08-19 16:43:20"
}

Копија није фискални рачун: у журналу нема заглавља фискалног рачуна, него линија „ОВО НИЈЕ ФИСКАЛНИ РАЧУН“. Копија рефундације додатно носи простор за потпис купца.

  • 401 unauthorized
  • 404 not_found
  • 422 validation
  • 429 rate_limited
  • 502 pfr_error
POST

/api/v1/invoices/{uuid}/email

Слање рачуна купцу е-поштом. Порука садржи журнал и линк за проверу рачуна, и потпуно је иста као она коју шаље дугме у апликацији, јер обе иду кроз исти код. Слање се уписује у ревизиони траг као invoice.email.

Аутентификација
Authorization: Bearer <api-kljuc>
Тело
JSON: {"to": "[email protected]"}
Одговор
200, потврда
Поље телаТипОбавезноОпис
tostringдаАдреса примаоца, исправна е-адреса, највише 190 знакова

Захтев

curl -X POST \
  https://otkucaj.com/api/v1/invoices/9b2f6c1e-4d3a-4f0b-9a77-2c8e5d1f6a30/email \
  -H "Authorization: Bearer kasir_vas_kljuc" \
  -H "Content-Type: application/json" \
  -d '{ "to": "[email protected]" }'

Одговор

{
  "ok": true,
  "uuid": "9b2f6c1e-4d3a-4f0b-9a77-2c8e5d1f6a30",
  "to": "[email protected]"
}

Ограничење слања је 30 порука на сат по кључу, независно од општег ограничења од 240 захтева у минуту. Прекорачење враћа 429 са поруком о ограничењу поште. Ако сервер поште одбије поруку, одговор је 502 са кодом mail_error: то није ни грешка ПФР-а ни грешка у захтеву, него неуспело слање, и захтев се сме поновити касније.

  • 400 bad_request
  • 401 unauthorized
  • 404 not_found
  • 422 validation
  • 429 rate_limited
  • 502 mail_error

Токови: продаја, рефундација, копија, предрачун

Сваки законом прописани ток, са пуним телом захтева. Одговор је увек исти облик рачуна као код POST /invoices.

Промет-продаја, са подељеним плаћањем

Најчешћи рачун. На једном рачуну сме више начина плаћања; збир мора одговарати збиру ставки.

{
  "invoiceType": "Normal",
  "transactionType": "Sale",
  "cashier": "Веб продавница",
  "payments": [
    { "paymentType": "Cash", "amount": 1000.00 },
    { "paymentType": "Card", "amount": 1400.00 }
  ],
  "items": [
    { "name": "Мајица", "quantity": 2, "unitPrice": 1200.00, "label": "Ђ" }
  ]
}

Рефундација

Ако враћате цео рачун, најједноставније је позвати POST /invoices/{uuid}/refund. Ручна рефундација кроз POST /invoices мора да носи referentDocumentNumber (ПФР број оригиналног рачуна) и buyerId (идентификацију купца). Без било ког од та два поља захтев се одбија са 422. Ставке и износи су они који се враћају, дакле могу бити и део оригиналног рачуна.

{
  "invoiceType": "Normal",
  "transactionType": "Refund",
  "cashier": "Веб продавница",
  "buyerId": "10:123456789",
  "referentDocumentNumber": "DMX7K2QP-DMX7K2QP-302",
  "referentDocumentDT": "2026-08-19 14:02:33",
  "payments": [{ "paymentType": "Card", "amount": 1200.00 }],
  "items": [
    { "name": "Мајица", "quantity": 1, "unitPrice": 1200.00, "label": "Ђ" }
  ]
}

Рефундације умањују промет у извештајима. Купцу који прима готовину издаје се и Копија-Рефундација са простором за потпис.

Поништавање сопственог рачуна

Погрешно издат рачун се поништава рефундацијом целог износа, при чему продавац као идентификацију купца уноси свој ПИБ, у облику 10:СВОЈПИБ.

{
  "invoiceType": "Normal",
  "transactionType": "Refund",
  "buyerId": "10:106884584",
  "referentDocumentNumber": "DMX7K2QP-DMX7K2QP-302",
  "referentDocumentDT": "2026-08-19 14:02:33",
  "payments": [{ "paymentType": "Card", "amount": 2400.00 }],
  "items": [{ "name": "Мајица", "quantity": 2, "unitPrice": 1200.00, "label": "Ђ" }]
}

Копија

Копија постојећег рачуна се најлакше издаје кроз POST /invoices/{uuid}/copy. Ручно, кроз POST /invoices, копија мора да носи referentDocumentNumber, иначе 422.

{
  "invoiceType": "Copy",
  "transactionType": "Sale",
  "referentDocumentNumber": "DMX7K2QP-DMX7K2QP-302",
  "referentDocumentDT": "2026-08-19 14:02:33",
  "payments": [{ "paymentType": "Card", "amount": 2400.00 }],
  "items": [{ "name": "Мајица", "quantity": 2, "unitPrice": 1200.00, "label": "Ђ" }]
}

Предрачун, па фискални рачун

Предрачун (ProForma) није фискални рачун: служи као понуда. Када купац плати, издаје се прави Промет-Продаја са референцом на предрачун.

Корак 1, предрачун:
{
  "invoiceType": "ProForma",
  "transactionType": "Sale",
  "payments": [{ "paymentType": "WireTransfer", "amount": 12000.00 }],
  "items": [{ "name": "Услуга одржавања", "unit": "мес", "quantity": 1, "unitPrice": 12000.00, "label": "Ђ" }]
}

Корак 2, по уплати, фискални рачун са референцом на предрачун:
{
  "invoiceType": "Normal",
  "transactionType": "Sale",
  "referentDocumentNumber": "DMX7K2QP-DMX7K2QP-310",
  "referentDocumentDT": "2026-08-19 09:15:00",
  "payments": [{ "paymentType": "WireTransfer", "amount": 12000.00 }],
  "items": [{ "name": "Услуга одржавања", "unit": "мес", "quantity": 1, "unitPrice": 12000.00, "label": "Ђ" }]
}

У самој апликацији дугме „Издај фискални рачун“ на предрачуну ради тачно ово: препуни касу и сама постави референцу.

Авансни ток, од прве уплате до коначног рачуна

Авансна ставка носи прописан назив по пореској ознаци: 10: Аванс (Ђ), 11: Аванс (Е), 12: Аванс (Г) или 13: Аванс (А). Количина је увек 1, а цена је износ примљеног аванса. Ток има три врсте докумената: авансни рачун (АП) за сваку уплату, аванс-рефундација (АР) која затвара збир свих аванса, и коначни Промет-Продаја.

  1. Први авансни рачун (АП). Уплата од 5.000,00 стигла преносом на рачун 18.08, а књижи се 19.08: зато dateAndTimeOfIssue носи стварно време уплате (штампа се као „ЕСИР време“).
    {
      "invoiceType": "Advance",
      "transactionType": "Sale",
      "dateAndTimeOfIssue": "2026-08-18 11:30:00",
      "payments": [{ "paymentType": "WireTransfer", "amount": 5000.00 }],
      "items": [{ "name": "10: Аванс (Ђ)", "quantity": 1, "unitPrice": 5000.00, "label": "Ђ" }]
    }

    ПФР врати нпр. invoiceNumber: "DMX7K2QP-DMX7K2QP-311".

  2. Додатни авансни рачуни за сваку следећу уплату, сваки са референцом на претходни АП:
    {
      "invoiceType": "Advance",
      "transactionType": "Sale",
      "referentDocumentNumber": "DMX7K2QP-DMX7K2QP-311",
      "referentDocumentDT": "2026-08-18 11:30:00",
      "payments": [{ "paymentType": "WireTransfer", "amount": 3000.00 }],
      "items": [{ "name": "10: Аванс (Ђ)", "quantity": 1, "unitPrice": 3000.00, "label": "Ђ" }]
    }

    ПФР врати нпр. invoiceNumber: "DMX7K2QP-DMX7K2QP-312".

  3. Затварање: аванс-рефундација (АР) на цео примљени износ, са референцом на последњи АП и обавезним buyerId, јер је реч о рефундацији:
    {
      "invoiceType": "Advance",
      "transactionType": "Refund",
      "buyerId": "10:123456789",
      "referentDocumentNumber": "DMX7K2QP-DMX7K2QP-312",
      "referentDocumentDT": "2026-08-19 10:00:00",
      "payments": [{ "paymentType": "WireTransfer", "amount": 8000.00 }],
      "items": [{ "name": "10: Аванс (Ђ)", "quantity": 1, "unitPrice": 8000.00, "label": "Ђ" }]
    }

    ПФР врати нпр. invoiceNumber: "DMX7K2QP-DMX7K2QP-313".

  4. Коначни Промет-Продаја са стварним ставкама испоруке, референцом на АР и рекламним текстом који носи број и датум последњег авансног рачуна:
    {
      "invoiceType": "Normal",
      "transactionType": "Sale",
      "referentDocumentNumber": "DMX7K2QP-DMX7K2QP-313",
      "referentDocumentDT": "2026-08-19 10:05:00",
      "adText": "Аванс: DMX7K2QP-DMX7K2QP-312 од 19.08.2026.",
      "payments": [{ "paymentType": "WireTransfer", "amount": 8000.00 }],
      "items": [{ "name": "Ормар по мери", "quantity": 1, "unitPrice": 8000.00, "label": "Ђ" }]
    }

У апликацији дугме „Затвори аванс“ на авансном рачуну само одради кораке 3 и 4: издаје АР на цео износ и препуни касу за коначни рачун са постављеном референцом и рекламним текстом.

Режим по чл. 6 ст. 2 Правилника

Инсталација може да ради у режиму у ком су начини плаћања Card, Check и MobileMoney искључени, а такве наплате се евидентирају као готовина. Једна инсталација ради у тачно једном режиму, који бира администратор у Подешавањима. Када је режим укључен, захтев са неким од искључених начина враћа:

422 { "error": { "code": "validation",
  "message": "Начин плаћања „Card“ је искључен подешавањем (режим по чл. 6 ст. 2 Правилника). Унесите као готовину." } }

Интеграција тада шаље {"paymentType": "Cash"} за те наплате.

Артикли

Шифарник артикала који каса нуди на додир. Артикли нису услов за издавање рачуна: POST /invoices прима ставке слободно, са називом и ценом из вашег система.

GET

/api/v1/items

Листа артикала, сортирано по називу, до 1.000 комада. Поље active разликује активне од деактивираних; деактивирани су такође у одговору.

Аутентификација
Authorization: Bearer <api-kljuc>
Параметри
нема, ни страничења ни филтера
Одговор
200, JSON

Захтев

curl -H "Authorization: Bearer kasir_vas_kljuc" \
  https://otkucaj.com/api/v1/items

Одговор

{
  "items": [
    {
      "id": 12,
      "plu": "1001",
      "name": "Мајица",
      "unit": "ком",
      "price": 1200.0,
      "label": "Ђ",
      "gtin": "8600000000000",
      "active": true
    }
  ]
}
  • 401 unauthorized
  • 429 rate_limited
POST

/api/v1/items

Додавање артикла у шифарник.

Аутентификација
Authorization: Bearer <api-kljuc>
Тело
JSON
Одговор
201, ИД новог артикла
ПољеТипОбавезноОпис
namestringдаНазив, до 200 знакова
labelstringдаПореска ознака из шифарника у важности (GET /tax-labels). Непозната ознака враћа 422 са списком дозвољених
pricenumberне (подразумевано 0)Цена са ПДВ, 0 или више, заокружује се на 2 децимале
plustringнеШифра артикла за брзо куцање
unitstringнеЈединица мере, подразумевано ком
gtinstringнеГТИН или бар-код, 8 до 14 цифара

Нов артикал се увек уписује као активан. Поље category се овим позивом не поставља.

Захтев

curl -X POST https://otkucaj.com/api/v1/items \
  -H "Authorization: Bearer kasir_vas_kljuc" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Мајица", "price": 1200.00, "label": "Ђ",
        "plu": "1001", "gtin": "8600000000000", "unit": "ком" }'

Одговор 201

{ "id": 12, "ok": true }
  • 400 bad_request
  • 401 unauthorized
  • 422 validation
  • 429 rate_limited
PUTPATCH

/api/v1/items/{id}

Делимична измена артикла: шаљете само поља која мењате, остала остају нетакнута. Оба метода, PUT и PATCH, раде исто.

Аутентификација
Authorization: Bearer <api-kljuc>
Путања
id: целобројни ИД артикла
Тело
JSON, било која од поља plu, name, unit, price, label, gtin, active
Одговор
200, потврда
Пажљиво са ознаком при измени. Приликом измене се вредност поља label уписује онаква каква је послата, без провере да ли таква пореска ознака постоји, за разлику од додавања артикла где се проверава. Артикал са ознаком које нема у шифарнику ће при издавању рачуна произвести 422. Шаљите само ознаке које враћа GET /tax-labels.

Захтев

curl -X PUT https://otkucaj.com/api/v1/items/12 \
  -H "Authorization: Bearer kasir_vas_kljuc" \
  -H "Content-Type: application/json" \
  -d '{ "price": 1350.00, "active": true }'

Одговор

{ "ok": true }
  • 400 bad_request
  • 401 unauthorized
  • 404 not_found
  • 429 rate_limited
DELETE

/api/v1/items/{id}

Мека деактивација: артикал се не брише из базе, јер га постојећи рачуни и даље референцирају. Добија active: false и нестаје са касе. Поновно активирање је PUT са {"active": true}.

Аутентификација
Authorization: Bearer <api-kljuc>
Путања
id: целобројни ИД артикла
Одговор
200, потврда

Захтев

curl -X DELETE https://otkucaj.com/api/v1/items/12 \
  -H "Authorization: Bearer kasir_vas_kljuc"

Одговор

{ "ok": true }
Овај позив не јавља да артикла нема. Деактивација непостојећег ИД-а такође враћа {"ok": true}, јер се ништа не мења и ништа не пуца. Ако вам је важно да ли артикал постоји, проверите га претходно кроз GET /items.
  • 401 unauthorized
  • 429 rate_limited

Извештаји

Оба извештаја рачунају промет по истом правилу и истим упитима као страница Извештаји у апликацији, па се бројке не могу разићи.

Правило обрачуна. У промет улазе само издати фискални рачуни, дакле Normal и Advance у статусу издат. Рефундације се одузимају. Предрачун, копија и обука не улазе ни у један збир. Исто правило враћа и сам одговор, у пољу rule.
GET

/api/v1/reports/summary

Промет за период, са разрадом по пореској ознаци, начину плаћања и касиру.

Аутентификација
Authorization: Bearer <api-kljuc>
Параметри
from, to
Одговор
200, JSON
ПараметарТипПодразумеваноОпис
fromstringданасДатум од, строго YYYY-MM-DD, укључује цео дан
tostringданасДатум до, строго YYYY-MM-DD, укључује цео дан. Не сме бити ранији од from

Неисправан облик датума или период у ком је from касније од to враћају 422, за разлику од листе рачуна где се датуми не проверавају.

Захтев

curl -H "Authorization: Bearer kasir_vas_kljuc" \
  "https://otkucaj.com/api/v1/reports/summary?from=2026-08-01&to=2026-08-19"

Одговор

{
  "from": "2026-08-01",
  "to": "2026-08-19",
  "totals": {
    "invoices": 142,
    "turnover": 386400.0,
    "refunds": { "count": 3, "amount": 7200.0 }
  },
  "byTaxLabel": [
    { "label": "Ђ", "name": "О-ПДВ", "rate": 20.0, "turnover": 351200.0 },
    { "label": "Е", "name": "П-ПДВ", "rate": 10.0, "turnover": 35200.0 }
  ],
  "byPaymentType": [
    { "paymentType": "Card", "amount": 244800.0 },
    { "paymentType": "Cash", "amount": 141600.0 }
  ],
  "byCashier": [
    { "cashier": "Веб продавница", "invoices": 118, "turnover": 302400.0 },
    { "cashier": "Марија", "invoices": 24, "turnover": 84000.0 }
  ],
  "rule": "Turnover counts issued Normal and Advance receipts only; refunds subtract; ProForma, Copy and Training are excluded."
}

Значење поља: totals.invoices је број рачуна у периоду, укључујући и рефундације; totals.turnover је промет умањен за рефундације; totals.refunds посебно приказује број и укупан износ рефундација, као позитиван број. У byTaxLabel су name и rate вредности null ако ознака више не постоји у шифарнику, а рачуни са њом су остали у бази. Начини плаћања се сабирају из самог рачуна, па за подељена плаћања сваки део улази у свој ред.

  • 401 unauthorized
  • 422 validation
  • 429 rate_limited
GET

/api/v1/reports/daily

Промет и број рачуна по данима, за исти период и по истом правилу као сабирни извештај.

Аутентификација
Authorization: Bearer <api-kljuc>
Параметри
from, to, оба подразумевано данас, строго YYYY-MM-DD
Одговор
200, JSON

Захтев

curl -H "Authorization: Bearer kasir_vas_kljuc" \
  "https://otkucaj.com/api/v1/reports/daily?from=2026-08-01&to=2026-08-05"

Одговор

{
  "from": "2026-08-01",
  "to": "2026-08-05",
  "days": [
    { "date": "2026-08-01", "invoices": 12, "turnover": 28800.0 },
    { "date": "2026-08-02", "invoices": 9, "turnover": 19400.0 },
    { "date": "2026-08-05", "invoices": 15, "turnover": 41250.0 }
  ],
  "rule": "Turnover counts issued Normal and Advance receipts only; refunds subtract; ProForma, Copy and Training are excluded."
}

Дани без промета се изостављају, тачно као у табели у апликацији. Ако вам треба непрекидан низ датума, попуните празнине на својој страни.

  • 401 unauthorized
  • 422 validation
  • 429 rate_limited

Шифарници

Две листе које интеграцији требају да би слала исправне вредности, обе из истог извора који апликација и сама користи.

GET

/api/v1/tax-labels

Пореске ознаке и стопе које су тренутно на снази, из истог извора који користи и валидатор рачуна (само активни редови). Ознака коју врати овај позив је ознака коју ће POST /invoices прихватити.

Аутентификација
Authorization: Bearer <api-kljuc>
Параметри
нема
Одговор
200, JSON

Захтев

curl -H "Authorization: Bearer kasir_vas_kljuc" \
  https://otkucaj.com/api/v1/tax-labels

Одговор

{
  "taxLabels": [
    { "label": "Ђ", "name": "О-ПДВ", "rate": 20.0 },
    { "label": "Е", "name": "П-ПДВ", "rate": 10.0 },
    { "label": "Г", "name": "Без ПДВ", "rate": 0.0 }
  ]
}
Списак није фиксан. Подразумевана инсталација носи ознаке Ђ, Е и Г активне, а ознаку А (Није у ПДВ) неактивну, што значи да је POST /invoices подразумевано не прихвата. Ознаке и стопе се мењају у Подешавањима, а у режиму Л-ПФР или В-ПФР меродаван је списак који даје сам ПФР. Зато читајте овај позив, а не преписујте ознаке у свој код.
  • 401 unauthorized
  • 429 rate_limited
GET

/api/v1/categories

Категорије активних артикала, без празних вредности, сортиране азбучно. Исти упит који каса користи за дугмад категорија.

Аутентификација
Authorization: Bearer <api-kljuc>
Параметри
нема
Одговор
200, JSON

Захтев

curl -H "Authorization: Bearer kasir_vas_kljuc" \
  https://otkucaj.com/api/v1/categories

Одговор

{ "categories": ["Пиће", "Слаткиши", "Храна"] }

Категорије се уносе уз артикал у апликацији. Артикли без категорије не праве празан унос у овој листи.

  • 401 unauthorized
  • 429 rate_limited

Грешке

Свака грешка има исти JSON облик, без обзира на путању:

{ "error": { "code": "validation", "message": "Рефундација мора имати референтни број оригиналног рачуна (referentDocumentNumber)." } }
HTTPcodeУзрокШта урадити
400bad_requestТело захтева није исправан JSONПроверите серијализацију и Content-Type: application/json
401unauthorizedНема Authorization заглавља, кључ је погрешан или опозванПроверите кључ; по потреби генеришите нов у Подешавањима
404not_foundНепозната путања, непостојећи UUID рачуна или ИД артикла при измениПроверите путању и идентификатор
422validationЗахтев не пролази валидацију; порука прецизно каже које поље и заштоИсправите захтев. Не понављајте исти захтев непромењен: резултат ће увек бити исти
429rate_limitedВише од 240 захтева у минуту по кључу, или више од 30 послатих порука на сатСачекајте до истека прозора, па наставите смањеним темпом
502pfr_errorПФР је одбио захтев или није доступанРачун НИЈЕ издат, ништа није уписано. Проверите GET /status, па поновите исти захтев
502mail_errorПорука са рачуном није могла да буде послатаРачун постоји и није дирнут. Слање се сме поновити касније
Најважније правило. На 502 рачун није настао и захтев је безбедно поновити; на 422 захтев треба исправити, а понављање непромењеног захтева нема смисла. Ако ваш систем аутоматски понавља захтеве, нека понавља само 429 и 502 и мрежне прекиде, никада 422.

WooCommerce и интеграције

Готов додатак повезује WooCommerce продавницу без иједне линије кода: otkucaj-fiskalizacija.zip.

  1. У WordPress администрацији отворите Додаци · Додај нови · Отпреми додатак, изаберите преузети zip и активирајте додатак.
  2. У Откуцају (Подешавања · АПИ кључеви) генеришите кључ и одмах га ископирајте: приказује се само једном.
  3. Отворите Подешавања · Откуцај фискализација у WordPress-у и унесите: адресу (https://otkucaj.com), АПИ кључ, статус поруџбине који окида издавање рачуна (нпр. „Завршено“ или „У обради“) и мапирање WooCommerce начина плаћања на фискалне (нпр. картице на Card, поузеће на Cash, вирман на WireTransfer).
  4. Готово. Када поруџбина пређе у изабрани статус, додатак издаје рачун, тачно једном по поруџбини, и у саму поруџбину уписује ПФР број рачуна и линк за проверу, видљиве и вама и купцу.

Пореска ознака по производу: додатку се за појединачни производ може задати мета поље otkucaj_label (вредност из GET /tax-labels); производи без њега користе подразумевану ознаку из подешавања додатка.

Телеграм обавештења. У апликацији (Подешавања · Телеграм обавештења) власник може да укључи Телеграм поруку за сваки рачун издат преко АПИ-ја: врста рачуна, износ, начини плаћања, ПФР број, бројач и линк за проверу стижу у изабрани разговор или групу чим се поруџбина фискализује.

Пример: чист PHP

function otkucaj_izdaj_racun(string $apiKey, array $body): array
{
    $ch = curl_init('https://otkucaj.com/api/v1/invoices');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $apiKey,
            'Content-Type: application/json; charset=utf-8',
        ],
        CURLOPT_POSTFIELDS => json_encode($body, JSON_UNESCAPED_UNICODE),
    ]);
    $raw  = curl_exec($ch);
    $err  = curl_error($ch);
    $http = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);

    if ($raw === false) {
        throw new RuntimeException("Мрежна грешка: $err");   // рачун можда јесте издат, проверити пре понављања
    }
    $json = json_decode($raw, true);
    if ($http === 201) {
        return $json;                                        // цео рачун: uuid, pfr, journal...
    }
    $code = $json['error']['code'] ?? 'unknown';
    $msg  = $json['error']['message'] ?? $raw;
    if ($http === 422) {
        throw new InvalidArgumentException("Неисправан захтев ($code): $msg"); // не понављати
    }
    throw new RuntimeException("Откуцај HTTP $http ($code): $msg");            // 429/502: поновити касније
}

$racun = otkucaj_izdaj_racun($apiKey, [
    'invoiceType' => 'Normal',
    'transactionType' => 'Sale',
    'cashier' => 'Веб продавница',
    'payments' => [['paymentType' => 'Card', 'amount' => 2400.00]],
    'items' => [['name' => 'Мајица', 'quantity' => 2, 'unitPrice' => 1200.00, 'label' => 'Ђ']],
]);
// сачувати уз поруџбину:
// $racun['uuid'], $racun['pfr']['invoiceNumber'], $racun['pfr']['verificationUrl']

Пример: Node.js

async function izdajRacun(apiKey, body) {
  const res = await fetch('https://otkucaj.com/api/v1/invoices', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json; charset=utf-8',
    },
    body: JSON.stringify(body),
    signal: AbortSignal.timeout(30000),
  });
  const json = await res.json();
  if (res.status === 201) return json;
  const { code, message } = json.error ?? {};
  const e = new Error(`Откуцај HTTP ${res.status} (${code}): ${message}`);
  e.retryable = res.status === 429 || res.status === 502; // 422 се никад не понавља
  throw e;
}

const racun = await izdajRacun(process.env.OTKUCAJ_KEY, {
  invoiceType: 'Normal',
  transactionType: 'Sale',
  cashier: 'Веб продавница',
  payments: [{ paymentType: 'Card', amount: 2400.00 }],
  items: [{ name: 'Мајица', quantity: 2, unitPrice: 1200.00, label: 'Ђ' }],
});
console.log(racun.pfr.invoiceNumber, racun.pfr.verificationUrl);

Пример: Python

import requests

def izdaj_racun(api_key: str, body: dict) -> dict:
    r = requests.post(
        'https://otkucaj.com/api/v1/invoices',
        json=body,
        headers={'Authorization': f'Bearer {api_key}'},
        timeout=30,
    )
    if r.status_code == 201:
        return r.json()
    err = r.json().get('error', {})
    if r.status_code == 422:
        raise ValueError(f"Неисправан захтев: {err.get('message')}")  # не понављати
    raise RuntimeError(f"Откуцај HTTP {r.status_code} ({err.get('code')}): {err.get('message')}")

racun = izdaj_racun(API_KEY, {
    'invoiceType': 'Normal',
    'transactionType': 'Sale',
    'cashier': 'Веб продавница',
    'payments': [{'paymentType': 'Card', 'amount': 2400.00}],
    'items': [{'name': 'Мајица', 'quantity': 2, 'unitPrice': 1200.00, 'label': 'Ђ'}],
})
print(racun['pfr']['invoiceNumber'], racun['pfr']['verificationUrl'])

Добра пракса

  • Издајте рачун тек по наплати. Фискални рачун прати новац; поруџбина која није плаћена нема шта да фискализује.
  • Чувајте uuid и pfr.invoiceNumber уз поруџбину. Без њих не можете касније да издате копију, рефундацију, да пошаљете рачун е-поштом нити да преузмете журнал.
  • Читајте шифарнике, не преписујте их. Пореске ознаке узмите са GET /tax-labels при подешавању интеграције, јер се списак разликује од инсталације до инсталације.
  • Поставите временско ограничење од 30 секунди, да позив ка АПИ-ју не блокира вашу апликацију унедоглед.
  • Никада не позивајте АПИ из прегледача купца ни из мобилне апликације: кључ би био јаван. Сав саобраћај иде са вашег сервера.
  • Не шаљите дупле захтеве. Уз сваку поруџбину чувајте свој идемпотентни кључ (нпр. број поруџбине) и пре слања проверите да ли је за њу рачун већ издат. Мрежни прекид после успешног издавања је најчешћи узрок дуплог рачуна: пре понављања проверите GET /invoices за тај дан.
  • Развијте и тестирајте у демо режиму. Прелазак на Л-ПФР или В-ПФР је измена подешавања у Откуцају, без иједне измене у вашем коду.

Промене

ВерзијаНовине
1.2Проширен АПИ: GET /me, GET /tax-labels, GET /categories, POST /invoices/{uuid}/refund, POST /invoices/{uuid}/copy, POST /invoices/{uuid}/email, GET /reports/summary и GET /reports/daily. У самој апликацији: регистрација налога са одобравањем, Cloudflare Turnstile заштита пријаве, Телеграм обавештења о издатим рачунима, нова контролна табла, колапсибилни сајдбар, категорије артикала, попусти по ставци и на цео рачун, конверзија предрачуна у фискални рачун, аутоматско затварање аванса, слање рачуна е-поштом.
1.1Офлајн ред на каси (рачун чека у локалном реду и издаје се по повратку везе), подељена плаћања на једном рачуну, WooCommerce додатак.
1.0Почетна верзија: издавање рачуна, листа и детаљ рачуна, журнал, артикли, АПИ кључеви.

Уговор АПИ-ја v1 је стабилан кроз све наведене верзије апликације: додаване су нове могућности, постојећа поља се нису мењала.

Питања и помоћ при интеграцији: [email protected]. Упутство за рад у самој апликацији: Упутство (PDF).