Платёж и фискальный чек часто обрабатываются разными сервисами. Банк может подтвердить списание мгновенно, а касса ответить позже или временно не отвечать. Если заказ сразу переводится в финальный статус, ошибка фискализации теряется среди успешных операций и обнаруживается только при сверке.
Возьмите один заказ и соберите хронологию: подтверждение оплаты, изменение статуса заказа, постановка задания на чек, ответ кассы, присвоение фискальных реквизитов и отправка клиенту. Не создавайте чек вручную, пока не проверено, не находится ли исходная задача в повторной обработке.
Что проверить в первую очередь
Для проблемы «заказ закрывается раньше завершения фискализации» сначала зафиксируйте один воспроизводимый пример: точное время, идентификатор объекта, пользователя или операции, входные данные, версию приложения и фактический результат. Отдельно запишите ожидаемое поведение: оплата, исполнение заказа и фискализация имеют отдельные статусы, а бизнес-процесс явно определяет, когда заказ можно считать полностью завершённым. Это не формальность. Без исходной точки легко принять временное совпадение за исправление, изменить сразу несколько условий и потерять возможность доказать настоящую причину сбоя.
Проверку начинайте с чтения состояния и журналов. Не запускайте массовый перерасчёт, повторную отправку, удаление записей или изменение прав на рабочей системе, пока не подготовлены резервная копия и способ отката. Секреты, токены, персональные данные и содержимое заказов в диагностические выгрузки не включайте.
- Сверьте payment ID, order ID, fiscal task ID и idempotency key.
- Проверьте, какой статус означает оплату, исполнение и получение фискального документа.
- Посмотрите длину очереди кассы, число попыток и возраст самого старого задания.
- Уточните, не создала ли касса чек при потерянном ответе вашего приложения.
- Проверьте смену, систему налогообложения, предмет расчёта и способ оплаты.
Почему возникает проблема
Видимый симптом обычно находится в конце цепочки. Пользователь видит неверный статус или отказ, хотя первичная ошибка могла произойти в API, фоновой задаче, кеше, очереди, внешнем сервисе либо при проверке доступа. Основной риск этого сценария: клиент получает товар и финальный статус без подтверждённого чека, а ошибка кассы обнаруживается слишком поздно. Поэтому исправление только интерфейса или ручная правка итоговой записи часто скрывает проблему, но не устраняет её.
Разбирайте события по хронологии и ищите первую точку, где фактические данные перестают соответствовать бизнес-правилу. В распределённой системе одинаково важны успешный ответ, повтор запроса, задержка события, параллельное выполнение и восстановление после временного отказа.
- Обработчик платежа закрывает заказ до успешной постановки задания в надёжную очередь.
- Worker подтверждает сообщение до сохранения фискальных реквизитов.
- Таймаут кассы трактуется как окончательный отказ, хотя операция продолжилась у провайдера.
- Повторная задача создаётся с новым ключом и приводит к риску дублирующего чека.
- Менеджеру показывается только статус заказа без отдельного состояния фискализации.
Пошаговая диагностика
Создайте безопасный тестовый сценарий, максимально похожий на проблемный, но не затрагивающий реальные списания, рассылки и клиентские данные. Присвойте операции единый correlation ID и проследите его через входящий запрос, бизнес-логику, базу, очередь и внешние интеграции. Для каждого этапа фиксируйте вход, результат, код ответа, время выполнения и номер версии записи.
Сначала подтвердите сам факт расхождения, затем последовательно исключайте уровни. Сравните рабочий и ошибочный случаи по одним и тем же полям. Проверьте часовой пояс, формат идентификаторов, порядок событий, права сервисной учётной записи и актуальность конфигурации на каждом экземпляре приложения. Если результат зависит от повторного запроса, задержки или конкретного узла, это важная часть причины, а не случайный шум.
- Сопоставьте timestamps всех событий с единым часовым поясом.
- Проверьте broker, retry и dead-letter queue для заданий старше допустимого времени.
- Запросите состояние операции у кассового провайдера по исходному внешнему идентификатору.
- Найдите закрытые заказы без fiscal document number или ссылки на чек.
- Сравните правила для полной оплаты, предоплаты, зачёта аванса, возврата и частичного расчёта.
Как разделить заказ, оплату и фискализацию
Финальный статус заказа не должен заменять статусы связанных процессов. Оплата может быть successful, доставка completed, а фискализация pending или failed. Эти состояния хранятся отдельно, но объединяются в понятное правило готовности заказа.
Надёжная реализация хранит бизнес-состояние явно и не пытается восстановить его только по экрану, случайному логу или последнему webhook. У каждого значимого действия должен быть стабильный идентификатор, понятный владелец, версия правила и проверяемый переход статуса. Повторная доставка одного события не должна создавать второе действие, а запоздавшее событие не должно возвращать объект в невозможное состояние.
- После оплаты в одной транзакции создаётся outbox-событие для фискализации.
- Fiscal task имеет стабильный ключ, номер попытки, next retry и ссылку на заказ.
- Успех фиксируется только после получения и сохранения фискальных реквизитов.
- Неопределённый таймаут сначала проверяется у провайдера, а не повторяется вслепую.
- Просроченная задача попадает в рабочий список с владельцем и безопасным действием.
Как исправить проблему
Исправление разделите на небольшие обратимые изменения. Сначала устраните подтверждённую первопричину, затем восстановите повреждённые данные отдельной контролируемой процедурой. Не смешивайте выпуск нового кода и массовую коррекцию истории в одном непрозрачном запуске: для них нужны разные отчёты, критерии успеха и планы отката.
Если затронуты существующие записи, сначала сформируйте dry-run: список объектов, старое значение, предлагаемое новое значение и основание для изменения. Обновление должно быть идемпотентным, ограниченным точной выборкой и сопровождаться аудитом. Для финансовых данных, прав доступа и персональной информации предпочтительны компенсирующие записи, а не переписывание истории.
- Добавьте отдельные состояния fiscal_pending, fiscal_processing, fiscal_done и fiscal_failed.
- Создавайте задачу через transactional outbox вместе с фиксацией оплаты.
- Сделайте worker идемпотентным и проверяйте операцию у кассы после неопределённого ответа.
- Показывайте менеджеру причину, число попыток и безопасную кнопку повторной проверки.
- Подготовьте отчёт закрытых заказов без чека и исправляйте их после сверки с провайдером.
Безопасный порядок внедрения
- Сохраните конфигурацию, связанные записи и необходимые журналы; отдельно проверьте, что резервную копию действительно можно восстановить.
- Воспроизведите проблему на тестовом объекте и сохраните результат до изменения, чтобы после выпуска сравнить одинаковые сценарии.
- Внесите минимальное изменение под системой контроля версий, опишите причину, ожидаемый эффект, ограничения и точный способ отката.
- Прогоните нормальный сценарий, ошибочный ввод, повтор одного запроса, два параллельных запроса и временную недоступность зависимости.
- Выпустите изменение на ограниченную долю трафика или один процесс, если архитектура это позволяет, и сравните метрики со старой версией.
- Только после стабильного наблюдения выполните контролируемое исправление исторических данных и сохраните итоговый отчёт.
Как проверить результат
Один успешный пример недостаточен. Проверьте основную операцию повторно, крайние значения, одновременные действия, перезапуск процесса и восстановление после краткого сбоя сети. Результат подтверждайте не только экраном пользователя, но и состоянием базы, очереди, внешнего сервиса и журналом аудита. Особое внимание уделите тому, что система делает при повторной доставке уже обработанного события.
Критерии приёмки сформулируйте до выпуска. Каждый пункт должен давать однозначный ответ «выполнено» или «не выполнено», а не субъективную оценку. Если тест невозможно повторить автоматически, оставьте короткий регрессионный чек-лист с тестовыми данными и ожидаемыми статусами.
- При доступной кассе чек создаётся один раз и реквизиты привязываются к заказу.
- При таймауте повтор не формирует второй документ.
- После перезапуска worker незавершённая задача продолжает обработку.
- Ошибка в данных видна менеджеру и не скрывается финальным статусом заказа.
- Сверка находит каждую оплату без успешной фискализации и не включает допустимые исключения.
Типичные ошибки при исправлении
- Повторно пробивать чек без запроса статуса исходной операции.
- Хранить только булево поле чек создан без промежуточных состояний.
- Закрывать dead-letter queue ручным удалением сообщений.
- Подменять время расчёта временем ответа фонового worker без учёта правил учёта.
- Не различать чеки прихода, возврата, предоплаты и зачёта.
Опаснее всего исправление, которое убирает заметный симптом ценой отключения проверки, ослабления прав или потери аудита. Такое изменение может сделать интерфейс «зелёным», но увеличить ущерб при следующем сбое. Если временный обход всё же необходим, ограничьте его срок, пользователей и область действия, добавьте мониторинг и заранее назначьте дату удаления.
Как предотвратить повторение
Профилактика строится вокруг явных контрактов, идемпотентных обработчиков, наблюдаемости и повторяемых релизов. Система должна не только корректно работать сейчас, но и быстро показывать нарушение правила после обновления, роста нагрузки или отказа интеграции. Ошибку, которая уже привела к потере времени, денег, данных или заявок, стоит закрепить автоматическим тестом.
Настройте мониторинг полного пользовательского пути, а не только доступности отдельных серверов. Техническая метрика должна быть связана с бизнес-результатом: заказ завершён, доступ выдан, файл восстановлен, событие обработано один раз, данные изолированы. Порог оповещения задавайте по нормальному профилю нагрузки и проверяйте, что уведомление содержит достаточно контекста для первого решения.
- Возраст и количество fiscal_pending по кассе и типу документа.
- Доля оплат без фискальных реквизитов через заданный интервал.
- Число повторных попыток, неопределённых ответов и сообщений в dead-letter queue.
- Расхождение между реестром кассы и локальными документами.
- Время от подтверждения оплаты до доступности ссылки на чек клиенту.
Что подготовить для технического разбора
- Короткое описание ожидаемого и фактического поведения с точной последовательностью действий.
- Время возникновения, идентификатор тестового объекта и версии всех затронутых компонентов.
- Обезличенные фрагменты журналов до и после ошибки с единым correlation ID.
- Список последних изменений, результаты уже выполненных проверок и условия, при которых симптом исчезает.
- Безопасный доступ к тестовой среде либо минимальный пример, не содержащий паролей, токенов и персональных данных.
Частые вопросы
Можно ли закрывать заказ до ответа кассы?
Это зависит от процесса, но состояние фискализации должно оставаться отдельным, наблюдаемым и гарантированно доводиться до результата.
Что делать после таймаута?
Сначала запросить операцию по стабильному идентификатору у кассы, потому что чек мог быть создан при потерянном ответе.
Как исправлять старые заказы без чеков?
Сформировать сверочный список, исключить уже созданные документы и выполнять корректирующие действия по правилам учёта с полным аудитом.
Когда нужна помощь специалиста
Если заказы закрываются раньше кассы, я могу проверить очередь, статусы, идемпотентность и сверку, затем внедрить безопасные повторные попытки. Для оценки нужны обезличенная хронология одного заказа, ответы кассы и схема текущих статусов.