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

Первый чек за 5 минут

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

  1. Установите приложение и пройдите мастер настройки — установка.
  2. Откройте в приложении раздел «API» и скопируйте адрес и ключ. Адрес по умолчанию — http://127.0.0.1:5600. В примерах ниже замените YOUR_API_KEY на свой ключ.

Проверьте, что приложение отвечает:

Окно терминала
curl http://127.0.0.1:5600/v1/status -H "X-Api-Key: YOUR_API_KEY"

В ответе configured: true — касса настроена, license.state — состояние подписки, fiscalDrive.connected: true — ФМ на месте.

Две лепёшки по 5 000 сум и чай за 8 000 сум, скидка на чек 1 000 сум, оплата наличными 17 000 сум.

Окно терминала
curl -X POST http://127.0.0.1:5600/v1/receipts \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept-Language: ru" \
-d '{
"externalId": "POS-2026-000123",
"type": "sale",
"items": [
{ "name": "Лепёшка", "spic": "10901001001000000", "packageCode": "1500213",
"quantity": 2000, "unitPrice": 500000, "vatPercent": 12 },
{ "name": "Чай зелёный", "spic": "10901001001000000", "packageCode": "1500213",
"quantity": 1000, "unitPrice": 800000, "vatPercent": 12 }
],
"discount": 100000,
"payments": { "cash": 1700000 }
}'

JavaScript (Node.js 18+ или браузерное приложение кассы)

Заголовок раздела «JavaScript (Node.js 18+ или браузерное приложение кассы)»
const API = 'http://127.0.0.1:5600';
const KEY = 'YOUR_API_KEY';
async function registerReceipt(receipt) {
const response = await fetch(`${API}/v1/receipts`, {
method: 'POST',
headers: { 'X-Api-Key': KEY, 'Content-Type': 'application/json', 'Accept-Language': 'ru' },
body: JSON.stringify(receipt),
});
const body = await response.json();
if (!response.ok) {
// title и action — готовый текст для кассира, code — для лога
throw Object.assign(new Error(body.error.title), { status: response.status, ...body.error });
}
return body.result; // { terminalId, receiptSeq, fiscalSign, dateTime, qrCodeUrl }
}
const result = await registerReceipt({
externalId: 'POS-2026-000123',
type: 'sale',
items: [
{
name: 'Лепёшка',
spic: '10901001001000000',
packageCode: '1500213',
quantity: 2000,
unitPrice: 500000,
vatPercent: 12,
},
{
name: 'Чай зелёный',
spic: '10901001001000000',
packageCode: '1500213',
quantity: 1000,
unitPrice: 800000,
vatPercent: 12,
},
],
discount: 100000,
payments: { cash: 1700000 },
});
console.log('Фискальный признак:', result.fiscalSign, 'QR:', result.qrCodeUrl);
using System.Net.Http.Json;
var http = new HttpClient { BaseAddress = new Uri("http://127.0.0.1:5600"), Timeout = TimeSpan.FromSeconds(60) };
http.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
http.DefaultRequestHeaders.Add("Accept-Language", "ru");
var receipt = new
{
externalId = "POS-2026-000123",
type = "sale",
items = new[]
{
new { name = "Лепёшка", spic = "10901001001000000", packageCode = "1500213", quantity = 2000, unitPrice = 500000, vatPercent = 12 },
new { name = "Чай зелёный", spic = "10901001001000000", packageCode = "1500213", quantity = 1000, unitPrice = 800000, vatPercent = 12 },
},
discount = 100000,
payments = new { cash = 1700000 },
};
var response = await http.PostAsJsonAsync("/v1/receipts", receipt);
var body = await response.Content.ReadFromJsonAsync<System.Text.Json.JsonElement>();
if (!response.IsSuccessStatusCode)
{
var error = body.GetProperty("error");
// Показать кассиру title и action, записать в лог code
Console.WriteLine($"{error.GetProperty("title")}: {error.GetProperty("action")} ({error.GetProperty("code")})");
return;
}
Console.WriteLine($"Фискальный признак: {body.GetProperty("result").GetProperty("fiscalSign")}");
import json
import urllib.request
import urllib.error
API = "http://127.0.0.1:5600"
KEY = "YOUR_API_KEY"
def register_receipt(receipt: dict) -> dict:
request = urllib.request.Request(
f"{API}/v1/receipts",
data=json.dumps(receipt).encode(),
headers={"X-Api-Key": KEY, "Content-Type": "application/json", "Accept-Language": "ru"},
method="POST",
)
try:
with urllib.request.urlopen(request, timeout=60) as response:
return json.load(response)["result"]
except urllib.error.HTTPError as failure:
error = json.load(failure)["error"]
# title и action — для кассира, code — для лога
raise RuntimeError(f'{error["title"]}: {error["action"]} ({error["code"]})') from None
result = register_receipt({
"externalId": "POS-2026-000123",
"type": "sale",
"items": [
{"name": "Лепёшка", "spic": "10901001001000000", "packageCode": "1500213",
"quantity": 2000, "unitPrice": 500000, "vatPercent": 12},
{"name": "Чай зелёный", "spic": "10901001001000000", "packageCode": "1500213",
"quantity": 1000, "unitPrice": 800000, "vatPercent": 12},
],
"discount": 100000,
"payments": {"cash": 1700000},
})
print("Фискальный признак:", result["fiscalSign"], "QR:", result["qrCodeUrl"])
{
"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?t=UZ170703100236&r=1532&c=20261005141203&s=287265419807"
}
}

Сохраните result вместе с чеком: фискальный признак и QR печатаются на чеке, а terminalId, receiptSeq, fiscalSign и dateTime понадобятся для возврата.

  • Таймаут — не повод пробивать чек заново. Повторите запрос с тем же externalId: приложение вернёт прежний результат. Почему это безопасно — повторы и externalId.
  • Возврат — тот же запрос с "type": "refund" и полем refund с данными исходного чека.
  • Ошибки — что показывать кассиру.
  • Все запросы и поля — справочник API.

Таймаут запроса — 60 секунд. Обычно чек пробивается за 1–8 секунд, но если в этот момент приложение отправляет накопленные чеки в ОФД, чек ждёт своей очереди. Если ответа нет, повторите тот же запрос: приложение доведёт чек до конца и вернёт результат или ответит 409 BRIDGE_RECEIPT_IN_PROGRESS — тогда подождите несколько секунд и повторите ещё раз.