Клієнт fiscalgate — оплати у фіскальні чеки Checkbox, рівно один раз
Роль Solo Creator / Architect
Період Sep 2026
Стек
Go Checkbox API ПРРО Фіскалізація Checkbox Monobank LiqPay WayForPay Еквайринг Платіжні шлюзи SQLite Docker
fiscalgate — оплати у фіскальні чеки Checkbox, рівно один раз preview

fiscalgate — оплати у фіскальні чеки Checkbox, рівно один раз

Огляд

Незалежний проєкт: сервіс на Go, що перетворює підтверджені оплати Monobank і LiqPay (та накладений платіж) на фіскальні чеки ПРРО Checkbox, оформлює повернення чеком повернення, а зміну відкриває й закриває (Z-звіт) за розкладом.

Результат: 300 замовлень × 4 одночасні копії кожного вебхука дають рівно 300 чеків; загублена відповідь реєстратора ніколи не подвоює чек; передоплата плюс накладений платіж стає одним чеком з двома формами оплати.


Справжня складність

Видати фіскальний чек — це один HTTP-запит. Зробити це правильно — ні: провайдери повторюють вебхуки, доки не побачать 2xx; дві копії можуть летіти одночасно; відповідь реєстратора може загубитися вже після прийняття чека; реєстратор може бути недоступний; частина замовлень оплачується двома способами. Кожен випадок має закінчитися рівно одним чеком — ні нулем, ні двома.


Архітектура

fiscalgate: вебхук, розрахунок, черга, Checkbox, посилання клієнту

Перевірений вебхук записує платіж під унікальним ключем (провайдер, id платежу); коли замовлення оплачене повністю, одне фіскальне завдання ставиться в чергу в тій самій транзакції. Воркер відкриває зміну на вимогу, надсилає чек із нашим UUID, опитує до статусу DONE й відправляє клієнту посилання на чек.


Ключові інженерні рішення

Рішення Чому
Унікальний ключ платежу + завдання в транзакції, що завершує замовлення Дублікати й одночасні вебхуки — no-op за конструкцією, а не за таймінгом
Власний UUID як id чека; перед повтором — пошук Якщо відповідь загубилась, повтор знаходить чек, а не створює другий
Експоненційний backoff, видимий стан failed, ендпоінт requeue Збій відкладає чек, але не губить; завдання, що здалося, видно з причиною
Чек лише коли оплачено рівно повністю Передоплата карткою + накладений платіж = один чек з двома формами оплати; переплата чекає на людину
Повернення обмежені продажем, спершу картка, потім готівка, ідемпотентні за ключем Чек повернення не перевищить проданого й не поверне двічі
Зміна закривається після щоденної межі, лише коли немає чеків у роботі Z-звіт ніколи не потрапляє посеред продажу

Результати

Перевірка Результат
Паралельність 300 замовлень × 4 одночасні копії вебхука → рівно 300 чеків
Збій реєстратора 3 невдалі спроби → повтор із backoff → один чек
Загублена відповідь після успішного POST один POST, один чек
Тести 26 під детектором гонок, покриття 68–82% по пакетах

Підключення до бойових систем

  • Checkbox: checkbox.base_url, login, password, license_key (${VAR} читає з оточення).
  • Monobank / LiqPay: токен чи приватний ключ і зареєстровані callback-адреси /webhooks/monobank, /webhooks/liqpay.
  • Ваш магазин: POST /api/orders при створенні замовлення, …/cod-paid при отриманні, …/refund для повернень.
  • Інший реєстратор чи еквайринг: один тип, що реалізує service.Fiscal, або один парсер, що повертає acquiring.Payment.

Код: github.com/valpere/fiscalgate


Стек

Go · Checkbox API · Monobank · LiqPay · SQLite · Docker