Когда локальный бизнес выходит на международный уровень, приходится учитывать много разных тонкостей новых стран, чтобы ничего не нарушить, особенно местное налоговое законодательство.
У Додо Пиццы уже больше 890 точек в 17 странах, везде используется платформа Dodo IS. В большинстве случаев нужно передавать данные о каждом чеке в налоговую с первого дня работы, чтобы сразу же не закрыться от штрафов. Поэтому задача интеграции нашего ПО с местным налоговым ПО для нас одна из самых важных при запуске новой страны.
Меня зовут Дима Карпов, я продакт‑оунер в команде, которая занимается кассами, чеками и всем, что связано с оплатами в ресторанах. В статье расскажу, как мы решаем описанную выше задачу так, чтобы ни один бизнес и разработчик не пострадал.
Отправляем данные о чеках и спим спокойно
Передавать данные о продажах в налоговую требует государство, чтобы увеличить сборы НДС и снизить теневую экономику. В 10 из 17 стран, где мы работаем, есть особые требования к передаче чеков и их хранению, в остальных можно просто напечатать чек и передать гостю.
Требования к формату передачи и хранению данных о продажах в каждой стране свои. Например, в России это регулируется 54-ФЗ. По этому закону необходимо передавать чеки онлайн сразу при продаже, через контрольно‑кассовую технику (ККТ). Далее ККТ сохраняет чек у себя в фискальной памяти, присваивает ему идентификатор и передаёт через оператора фискальных данных в налоговую. На чеке печатается QR‑код для проверки через специальный сервис налоговой.
В некоторых странах есть другие варианты передачи чеков, например, когда нужно передавать чеки напрямую на сервер налоговой через HTTP, в ответ получить номер фискальной операции, распечатать его на чеке через принтер и отдать гостю. Отдельная история — передача чека в электронном виде, не печатая его на бумаге, потому что не во всех странах можно так делать.
По большому счёту каждая страна может придумать свою систему передачи и требования к чекам, а нам нужно всё это соблюдать, если хотим работать в этой стране.
В итоге работать с чеками и налогами очень сложно, потому что нужно:
перед открытием изучить и учесть все требования закона в новой стране, выбрать проверенное налоговой решение;
написать новую интеграцию с нашей информационной системой — Dodo IS;
менять и дорабатывать интеграцию, если меняется законодательство;
обеспечивать стабильную работу интеграции.
Моя любимая аналогия — электрические розетки в разных странах. В Англии один тип розетки, в США второй, в России — третий. Нельзя приехать с российской вилкой в США и просто вставить её в местную розетку. Так и у нас с кассами и налоговыми в странах — нельзя один раз сделать и жить спокойно.
Как работает Dodo IS с чеками
Dodo IS как система управляет всем бизнесом «из коробки», в том числе она должна уметь печатать чеки. Для этого у нас есть два производственных интерфейса: касса ресторана и касса доставки. Это веб‑сайты, каждый со своим бэкендом и фронтендом.
Касса ресторана — интерфейс для приёма заказа кассиром в зале. Обрабатывает заказы из мобильного приложения, когда гость хочет получить свой заказ в зале.
Касса доставки отвечает за обработку заказов на доставку, например, с сайта, колл‑центра, мобильного приложения. Также курьеры могут через неё забрать заказ или посмотреть его состав.
Но сами кассы не занимаются печатью чеков, им нужна помощь третьего приложения — сервиса печати чеков с исторически сложившимся названием CashHardware. Его фронтенд динамически подгружается в окно браузера, умеет общаться с кассами и содержит в себе всю логику печати чеков и передачи их в налоговую.
Другая важная часть печати чеков — это наше приложение для касс на базе Electron (подробнее о работе Electron и нашем переходе на него можно узнать из видео). Для общения с железками из JS‑кода мы используем установленные в системе COM‑объекты. В современных и хорошо защищённых браузерах доступа к COM нет, раньше кассы долго работали на Internet Explorer (IE). Теперь Electron спасает нас от необходимости работать с IE, даёт некоторые другие плюшки и при этом не лишает взаимодействия с COM‑объектами.
Физически это минимум два моноблока на WIndows, которые стоят в ресторане и на них всё это «крутится». В России к каждому из них подключены по USB кабелю ККМ Атол, через которые мы печатаем чеки и отправляем в налоговую, банковский терминал. Также есть вспомогательные устройства типа денежного ящика или детектора купюр, но их оставим за скобками.
Две беды с новыми странами: долго открывать, тяжело поддерживать
Свои правила в каждой стране
Каждый раз, когда Додо Пицца открывается в новой стране, Dodo IS должна уметь работать с чеками этой страны. Для подготовки к открытию у нас есть выделенная IT‑команда. Помимо других технических задач, ей приходилось вместе с бизнесом в погружаться в местное законодательство, чтобы подружить кассы Dodo IS с местным ПО или железками. На это могло уходить 60–70% всего времени, которое выделялось под запуск. Например, в Чехии надо поднимать свой сервис для хранения чеков в случае отсутствия интернета, в Армении нужно передавать специальный код продукта в чеке и т. д.
Вдобавок приходилось разбираться с протоколами взаимодействия железок, а это не зона экспертизы или интереса команды, поэтому настроения в ней были безрадостные.
Только представьте: вот разработчик, он хочет сделать так, чтобы на кассе ресторана появился поиск по ингредиентам и кассиру было удобно. А тут внезапно обнаруживается, что в Литве почему‑то не работает RASO‑провайдер, который работает как оболочка над чековым принтером. Разработчик первый раз в жизни это видит. Хорошо, если есть дока по принципу работы хотя бы на литовском. Тратит время и отвлекается на помощь, пока кассиры продолжают страдать без поиска. Боль.
А если одновременно запускаются 2 или 3 страны? Эта команда становится «бутылочным горлышком» при масштабировании.
Поддерживать много стран тяжело
Чем больше стран, тем больнее становится их поддерживать. И дело не только в технических проблемах с существующими интеграциями, но и в новых требованиях в законе.
Например, в Нигерии при запуске надо было просто печатать чек, теперь нужно передавать чеки онлайн в местную налоговую — FIRS, в ответ получить код и штрихкод для проверки чека, которые надо напечатать на чеке.
Команде открытия стран и команде касс часто приходилось переключаться на срочные проблемы и доработки от действующих стран, что приводило к расфокусу, а партнёры страдали, т.к. могли получить штрафы за невыдачу чеков.
Просто отдать код в аутсорс тоже не было вариантом из‑за того, что бэкенд сервиса CashHardware лежал в монолите, где ещё куча другого кода, в котором надо разобраться. Да и небезопасно отдавать его внешним командам.
Если вернуться к аналогии электрических розеток, то мы даже не делали свои вилки, а просто вставляли голые провода в розетки. Откуда идут эти провода, могли знать только люди, которые их делали — если вспомнили бы, конечно.
Придумываем универсальное решение
Было понятно, что надо сделать такую электрическую вилку, которую мы будем отдавать во все страны, а уже под любую страну можно легко сделать переходник и включать это все в местные розетки. Назвали это плагинной системой касс.
Альтернативные решения
Конечно, мы искали решения — универсальные переходники. Одно из таких Fiscal solution. Обещают сделав один раз интеграцию с ними поддерживать фискализацию в 23 странах, но по факту все равно каждую страну надо допиливать и финансово не очень выгодно выходит, пока мы на этапе переговоров.
Ещё были попытки интегрироваться через POS‑системы, у которых уже есть интеграции в разных странах, например r‑keeper, но схема получалась слишком сложная, а ценник высоким.
У плагинной системы было две основные задачи:
Упростить запуск новых стран, чтобы кассовые интеграции можно делать силами партнёра или сторонних разработчиков, устранить узкое место и легко масштабироваться при необходимости.
Облегчить поддержку касс. Должна появиться чёткая граница разделения ответственности: если ошибка произошла уже в плагине, ею занимается партнёр или аутсорсеры. Если в общем коде до него — наши разработчики. Партнёру проще следить за изменениями требований закона и будет хорошо, если он сам сможет адаптировать интеграцию.
Для этого нам надо было:
дать внешним командам необходимые инструменты для разработки интеграции с кассами;
дать аутсорсерам возможность проверить, что их решения на самом деле работают.
Больше всего болело у команды открытия новых стран. Ребята пришли с инициативой к бизнесу, получили поддержку и целого разработчика в помощь, написали RFC и сделали жизнеспособное решение.
Архитектура решения
Раньше с кассами мы общались из окна браузера, и COM‑объекты были единственным способом повзаимодействовать с чем‑то низкоуровневым. Теперь же у плагинов есть доступ к полноценному NodeJS, что открывает много возможностей, в первую очередь взаимодействие с файловой системой.
Например, в Польше поставщик кассового оборудования написал для нас exe‑программу. Мы сохраняем JSON‑файл с данными о чеке на диск и запускаем эту программу, передавая ей путь к файлу, а она уже общается с ККМ и печатает чек. В старой парадигме мы бы так сделать не смогли, пришлось бы штудировать документацию низкоуровневого протокола, которая далеко не вся переведена с польского на английский.
Каждый плагин находится в своём репозитории на GitHub. В этих репозиториях настроены GitHub Actions, которые собирают проект, прогоняют тесты и загружают получившийся артефакт в Azure Blob Storage. Каждый артефакт имеет вид <version>.zip, где версия это та, что указана в package.json.
Отдельно в папке каждого плагина есть файл version
— в нём указана актуальная версия, которую надо использовать на продакшене. Для обновления актуальной версии есть отдельный экшен в GitHub.
Иногда нужно проверить какие‑то новые фичи или исследовать проблемы на продакшене, не распространяя это на всю сеть. Для этого мы можем переопределять версию плагина на уровне конкретной кассы в Dodo IS.
Как плагины используются Electron-приложением
Каждое приложение на Electron — это по сути один процесс main
и несколько процессов renderer
, в которых уже отрисовываются браузерные окна. Общаются они с помощью шины сообщений IPC. Раньше весь код печати чеков находился внутри renderer‑процесса и при помощи библиотечки Winax взаимодействовал с драйверами устройств. Если же нужно было самим напечатать чек, то мы верстали его в HTML, открывали в отдельном BrowserWindow и отправляли на принтер, который настроен в системе основным.
С одной стороны, схема стала сложнее. С другой, она дала нам массу других возможностей.
Теперь на уровне renderer‑процесса появилась единая точка входа для всех моделей касс, которые используют плагинную систему.
При загрузке страницы происходит шаг инициализации. В main‑процесс приходит команда «инициализировать плагин такого‑то типа». На этом шаге происходит проверка уже установленной версии плагина. Если её нет или она не актуальная, то на её место из CDN скачивается актуальная версия плагина. Потом main‑процесс делает require("index.js") в папке с плагином и сохраняет полученный объект.
Теперь, когда кассе в окне браузера надо распечатать чек, команда через IPC, согласно публичным контрактам, отправляется в плагин, а там уже может происходить всё что угодно: работа с драйвером, HTTP‑запросы к серверу налоговой, вызов.exe‑файла или просто печать на принтер через BrowserWindow.
Проверено — работает!
Первый плагин для Польши мы написали сами в паре с производителем ККМ.
Доработали контракты и убедились, что можно написать плагин вне нашего монолита и система будет работать.
Дальше надо было подтвердить, что плагин можно написать внешним разработчикам.
Одна из целей плагинной системы — упростить поддержку существующих стран. Для того чтобы партнёры сами могли поддерживать интеграцию страны, нужно перевести действующие страны на плагины. Выглядит как подходящая задача для внешних разработчиков, поэтому мы принялись искать для себя аутсорс‑команду.
Поиск аутсорса — отдельная история, но после того, как нашли подходящих ребят, достаточно быстро перевели на плагин белорусские кассы. Партнёр принял плагины к себе на обслуживание и может быстро дорабатывать их под свои требования.
Но не всё идеально
Недостаточно проработанные контракты
Когда запускали плагины, казалось, что мы хорошо проработали контракты, оставили там только самое нужное, а всё новое можно будет безболезненно добавить в будущем. Мы ошибались.
Например, в Словакии идентификатор чека возврата приходил с типом данных String
, до этого они у нас везде хранились как int
. В итоге пришлось мигрировать базу и все контракты переводить на строковые значения.
На проработку контрактов стоило потратить намного больше времени и сил, это бы точно окупилось дальше. В самом начале можно было себе позволить делать ломающие изменения, но сейчас это уже больно и приходится городить костыли.
Партнёрам нужны опытные разработчики
На текущем этапе есть понимание, что для разработки плагина мы рекомендуем искать разработчиков уровня middle+ и выше. Код достаточно важный, если он будет работать плохо, возможны штрафы.
Возможно, дальше мы сможем упростить процесс и снизить порог вхождения, чтобы ничего нельзя было сломать в плагине.
Что в итоге и куда дальше
Сейчас у нас есть работающая плагинная система с пятью плагинами, внешняя команда разработки от наших партнеров - CSSSR, которую мы рекомендуем нашим партнёрам. Ребята могут быстро масштабироваться и открывать необходимое количество стран.
Плагинная система работает успешно, как и задумывалось: облегчает жизнь разработке и партнёрам. Спасает разработчиков от расфокуса и растущего потока саппорта. Партнёрам позволяет не ждать, пока внутренняя команда доберётся до их проблем.
Уже случилось несколько «прилётов» от партнеров из действующих стран, например:
Нигерия, где стало нужно не просто печатать чеки, а передавать их онлайн в налоговую;
Кыргызстан, где были кассы с без передачи чеков онлайн в налоговую, с хранением на физическом накопителе. А теперь надо перейти на онлайн передачу чеков.
Все эти прилёты теперь не так больны для нас и команды не должны менять свои планы. Партнёры могут быстро адаптировать свои интеграции под изменения.
В 2023 году мы планируем открыть минимум 8 стран, где точно проверим устойчивость плагинной системы. И пусть у нас пока нет универсальной «вилки», которая подходит ко всем розеткам, зато мы и партнёры можем быстро делать переходники для своих стран — осталось только значимо увеличить количество этих стран.
Надеюсь, наш опыт поможет вам набить меньше шишек, если планируете выводить свой бизнес зарубеж. А если вы уже решали такую проблему, знаете универсальный переходник или хотите обсудить эту тему, пишите в комментариях или мне в Телеграм.