Перейти к содержимому

Обзор локального API

Кассовая программа отправляет чеки в приложение по HTTP на том же компьютере. Приложение проверяет чек, пробивает его через фискальный модуль и возвращает фискальный признак.

  • Адрес: http://127.0.0.1:5600 (порт меняется в настройках приложения).
  • Ключ: заголовок X-Api-Key. Ключ показан в приложении, раздел «API».
  • API доступен только с этого компьютера. Запросы из браузера со сторонних сайтов отклоняются (BRIDGE_FORBIDDEN_ORIGIN).
  • Полное описание в формате OpenAPI: GET /v1/openapi.json (без ключа).
  • Все суммы — целые тийины (1 сум = 100 тийин): 12 500,50 сум → 1250050.
  • Количество — целые тысячные доли: 1 шт → 1000, 0,25 кг → 250.
  • НДС приложение считает само по ставке vatPercent.

POST /v1/receipts

{
"externalId": "POS-2026-000123",
"type": "sale",
"items": [
{
"name": "Хлеб",
"spic": "10901001001000000",
"packageCode": "1500213",
"quantity": 2000,
"unitPrice": 500000,
"discount": 0,
"vatPercent": 12
}
],
"discount": 0,
"payments": { "cash": 600000, "card": 400000, "cardType": "personal" }
}
Поле Обязательно Что это
externalId да Номер чека в вашей программе, до 64 символов. Уникален: см. «Повторы» ниже
type да sale — продажа, refund — возврат
items[].name да Название позиции
items[].spic да ИКПУ (МХИК) из каталога tasnif.soliq.uz
items[].packageCode да Код упаковки из того же каталога
items[].quantity да Количество в тысячных
items[].unitPrice да Цена за единицу в тийинах
items[].discount нет Скидка на позицию в тийинах
items[].vatPercent да Ставка НДС, %
items[].barcode нет Штрихкод
items[].markingCode нет Код маркировки с упаковки
items[].commissionTin нет ИНН комитента (9 цифр)
discount нет Общая скидка на чек в тийинах, включая скидки позиций; остаток разносится по позициям
payments.cash, payments.card да Оплата наличными и картой в тийинах
payments.cardType нет personal или corporate
refund для возврата Данные исходного чека: terminalId, receiptSeq, fiscalSign, dateTime — как их вернул ответ на продажу

Сумма оплат должна точно равняться сумме позиций за вычетом скидок, иначе — BRIDGE_PAYMENT_MISMATCH. Позиции с нулевой стоимостью в ФМ не отправляются.

Ответ 200:

{
"externalId": "POS-2026-000123",
"duplicate": false,
"result": {
"terminalId": "UZ170703100236",
"receiptSeq": "1532",
"fiscalSign": "287265419807",
"dateTime": "2026-10-05 14:12:03",
"qrCodeUrl": "https://ofd.soliq.uz/check?…"
}
}

Повторный запрос с тем же externalId и тем же содержимым не пробивает чек второй раз, а возвращает прежний результат с duplicate: true. Поэтому после обрыва связи запрос можно повторить.

Ситуация Ответ
Чек уже пробит 200, duplicate: true
Тот же номер, другое содержимое 409 BRIDGE_EXTERNAL_ID_CONFLICT
Чек пробивается прямо сейчас 409 BRIDGE_RECEIPT_IN_PROGRESS — повторите через несколько секунд
ФМ отказал 422 OFD1_* / OFD2_* — чек не пробит; тот же чек можно повторить после устранения причины
ФМ не ответил вовремя 504 BRIDGE_FISCAL_DRIVE_TIMEOUT, повтор вернёт 409 BRIDGE_RECEIPT_STATE_UNKNOWN

Если ФМ не ответил, чек мог пробиться. Приложение не даст пробить его ещё раз, пока кассир не проверит чек и не нажмёт «Не пробит» в разделе «Чеки». Узнать состояние: GET /v1/receipts/{externalId}.

Любая ошибка приходит в одном формате; title и action — на языке из Accept-Language (ru, uz-latn, uz-cyrl) или на языке приложения:

{
"error": {
"code": "BRIDGE_PAYMENT_MISMATCH",
"title": "Сумма оплаты не совпадает с суммой чека",
"action": "…",
"message": "Оплата 999999 тийин не равна сумме к оплате по позициям 1000000 тийин"
}
}

Покажите кассиру title и action, а code запишите в лог. Все коды — на странице «Коды ошибок».

HTTP Когда
400 Неверный чек
401 Нет или неверный X-Api-Key
402 Касса отключена: см. «Подписка» ниже (LICENSE_*)
403 Запрос из браузера со стороннего сайта
404 Чека нет в журнале
409 Номер чека занят, чек пробивается или исход неизвестен
422 Фискальный модуль отказал
502 FiscalDriveAPI или сервис маркировки ответил непонятно
503 Приложение не настроено, FiscalDriveAPI не запущен или нет связи с сервером (CLOUD_*)
504 ФМ не ответил вовремя
Запрос Что делает
POST /v1/shift/open Открыть смену
POST /v1/shift/close Закрыть смену
POST /v1/queue/send Отправить накопленные в ФМ чеки в ОФД; ответ — сколько осталось (remaining, у v2 — null)
GET /v1/status Версия приложения, настройка, состояние ФМ и подписки (license)

Операции с ФМ выполняются по одной: если кассовая программа шлёт несколько запросов сразу, они встают в очередь.

POST /v1/marking/check — проверить код маркировки с упаковки перед продажей. Приложение спрашивает сервер, а тот — tasnif.soliq.uz, поэтому кассе нужен интернет.

{
"markingCode": "0104780069000130215Ab…",
"spic": "10901001001000000",
"packageCode": "1500213",
"commissionTin": "123456789"
}

commissionTin — только для товара комитента. Ответ 200: { "valid": true, "message": null }; если код не годится, valid: false, а в message — причина от tasnif.

Если касса не вошла на сервер или сервер недоступен — 503 с кодом CLOUD_*; если tasnif не ответил — 502 CLOUD_MARKING_UNAVAILABLE. Пробивать чек можно и без проверки маркировки: это решает кассовая программа.

Пробитие чека не требует связи с сервером: приложение пишет чек в журнал на кассе и отправляет журнал на сервер каждые 30 секунд и сразу после каждого чека. Пока связи нет, чеки ждут на кассе и уходят, как только она появится. Время смены приложение берёт с сервера, а не с часов компьютера.

Касса работает по ежемесячной подписке. Если касса отключена, на продажу и возврат приложение отвечает 402, а сам чек в журнал не попадает: после оплаты его можно пробить с тем же externalId.

Код Когда
LICENSE_EXPIRED Подписка не оплачена и льготные дни кончились
LICENSE_BLOCKED Кассу отключил администратор
LICENSE_OFFLINE Касса больше суток без связи с сервером
LICENSE_MISSING Касса не вошла на сервер или ещё не получила лицензию

Что работает у отключённой кассы:

  • повтор уже пробитого чека — 200 с duplicate: true;
  • закрытие смены (POST /v1/shift/close) и отправка чеков в ОФД (POST /v1/queue/send);
  • GET /v1/status и GET /v1/receipts/{externalId}.

Состояние подписки — в GET /v1/status, поле license: state (active, expiring — осталось 5 дней и меньше, grace — льготные дни, blocked), code, paidThrough (последний оплаченный день) и disableAt (когда касса отключится). Кассовая программа может заранее предупредить кассира, пока state — expiring или grace.