В таком сценарии отключение повторной отправки оставит другую проблему: после сбоя заказ может вообще не попасть в работу. Нужно определить, как интеграция узнаёт прежнюю операцию и выясняет её результат. Начать стоит с возможностей готового коннектора — модуля обмена между системами. Возможно, необходимая защита уже предусмотрена, но её поведение при сбое ещё не проверяли.
Повтор сообщения или новый заказ
Две одинаковые записи ещё не объясняют, что произошло. Покупатель мог действительно оформить ещё один такой же заказ. Мог повторно прийти сигнал об оплате прежнего заказа. Наконец, сама интеграция могла повторить команду, не дождавшись ответа. Во всех случаях товары и сумма могут совпадать, но допустимое действие будет разным.
Поэтому совпадение телефона, состава корзины или суммы нельзя само по себе считать основанием для запрета. В разборе AWS о безопасных повторах есть похожее различие: два запроса с одинаковыми параметрами могут означать намерение создать два одинаковых ресурса. AWS описывает подход, при котором вызывающая сторона передаёт уникальный идентификатор своего запроса. Так она явно сообщает, какое действие хочет повторить.
Для заказа сначала нужно договориться, что именно считается прежним намерением. В нашем условном примере это передача конкретной согласованной отгрузки в работу. Повтор её доставки должен вести к уже созданному заданию. Другая покупка того же товара остаётся новым заказом. Если бизнес допускает несколько отгрузок по одному заказу, каждой нужно своё основание: запретить всё повторное по номеру заказа будет слишком грубым правилом.
Полезно восстановить один спорный случай по обеим сторонам обмена: какое исходное действие было согласовано, какие сообщения отправлялись и сколько заданий фактически появилось. Пока этой связи нет, удаление второй строки может скрыть следствие, оставив причину повторного действия.
Как связать событие с бизнес-действием

В обмене нужно различать попытку доставки, событие и операцию. Попытка показывает, что сообщение отправили ещё раз. Событие сообщает о произошедшем изменении, например об успешной оплате. Операция — действие, которое после этого должна выполнить компания: создать заказ в учётной системе или передать определённую отгрузку на склад. Эти действия не обязаны иметь один и тот же идентификатор.
Stripe прямо предупреждает, что одно событие может прийти несколько раз, и рекомендует учитывать идентификаторы обработанных событий. Документация отдельно рассматривает случаи, когда созданы два разных объекта Event: для распознавания таких дублей она указывает идентификатор объекта вместе с типом события. Это конкретное правило Stripe, а не универсальный способ сравнить любые сообщения о заказах.
Из этого следует вопрос к исполнителю: какой признак связывает входящие сообщения с одной разрешённой операцией именно в нашей цепочке? Если оплата и изменение статуса заказа оба могут запустить создание задания, учёт только идентификаторов событий не объясняет, почему задание не появится дважды. Нужна общая связь с действием, которое бизнес разрешил выполнить.
Для условной отгрузки полезно сохранять её устойчивый идентификатор вместе с результатом: какое задание создано в принимающей системе и где его можно найти. Повтор после перезапуска должен находить эту же связь. Новый случайный ключ при каждой отправке лишит получателя возможности узнать прежнюю операцию по этому ключу.
У одного идентификатора должен быть определённый смысл. Если под прежним ключом пришёл уже другой состав отгрузки, нельзя молча считать запрос ни безопасным повтором, ни разрешением на ещё одно действие. Нужно остановить конфликт и применить согласованное правило изменения заказа. В своей модели AWS возвращает ошибку при изменении параметров с тем же идентификатором. Для конкретной интеграции такое поведение нужно отдельно подтвердить.
Почему защита уведомлений не заменяет ключ операции
Входящее уведомление и исходящая команда решают разные задачи. Уведомление об оплате говорит, что изменилось у отправителя. Команда принимающей системе просит что-то сделать. Защита от повторного уведомления не переносится автоматически на последующую команду.
Например, ЮKassa считает запрос повторным, если совпадают данные и ключ идемпотентности. При тех же данных, но другом ключе запрос выполняется как новый. Идемпотентностью здесь называется возможность повторить запрос без нежелательного повторения операции в пределах условий API (интерфейса обмена между программами). Это свойство конкретного запроса, которое нельзя приписать всей цепочке от сайта до склада.
Для входящих уведомлений ЮKassa действует другой контракт: получатель подтверждает получение кодом HTTP 200. При других кодах сервис продолжает доставку в указанном документацией окне. Такое подтверждение само по себе ничего не сообщает об отгрузке товара.
Поэтому при проверке обмена стоит разделить приём сообщения и выполнение работы. Требование к коннектору можно сформулировать так: после подтверждения приёма сообщение должно оставаться доступным для обработки и после перезапуска, а завершённое действие — иметь отдельное подтверждение результата. Иначе в журнале отправителя доставка будет успешной, хотя работа могла ещё не начаться.
Подлинность сообщения тоже проверяется отдельно. Защита от дублей не делает поддельную команду безопасной. Следует использовать проверку происхождения и актуальности данных, предусмотренную отправителем: например, документация ЮKassa описывает проверку статуса объекта и IP-адреса. Переносить сюда механизм проверки другого сервиса без основания нельзя.
Что делать при неизвестном результате и частичном успехе
Самый неудобный сбой происходит между выполнением действия и сохранением ответа о нём. В условном примере складская система уже создала задание, а коннектор ещё не успел записать его номер и перезапустился. В собственной истории коннектора результата нет. Это не означает, что задания нет на складе.
Такую неопределённость нельзя безопасно свести к статусу «ошибка, повторить заново». ЮKassa отдельно предупреждает, что HTTP 500 не говорит об успешности или неуспешности операции: прежде чем принимать решения, нужно выяснить результат обработки. Здесь это пример платёжного контракта, а не доказательство поведения складской системы. Для склада нужен свой подтверждённый способ узнать исход.
Исполнителю нужно показать, как интеграция найдёт результат по устойчивому идентификатору в принимающей системе либо повторит тот же запрос в пределах её документированной защиты. Возможность поиска должна быть пригодна именно для восстановления: если поиск ещё не показывает созданную запись, пустой ответ сам по себе не доказывает, что действие не выполнялось. Когда надёжно установить исход нельзя, случай должен остановиться на разборе без автоматического создания нового задания.
Один локальный флаг «обработано» этот разрыв не устраняет. Если поставить его до обращения к складу, сбой может оставить заказ без задания, которое система уже считает выполненным. Если поставить после, задание может появиться, а флаг — не сохраниться. В разборе AWS запись идентификатора и изменения в пределах описанной операции должны выполняться атомарно, то есть целиком либо не выполняться вовсе. Локальная таблица коннектора не включает внешний платёж или складскую операцию в такую общую транзакцию сама по себе.
Есть и другой сценарий: два повтора начинают обрабатываться одновременно. Оба успевают проверить историю, оба не находят результата и оба создают задание. Требуется защита, при которой только один обработчик получает право начать эту операцию; второй видит, что она уже выполняется, либо получает сохранённый результат. Исполнитель выбирает технический механизм, но обязан показать поведение при одновременном запуске и восстановление после остановки первого обработчика.
При частичном успехе нужно знать, какой шаг уже завершён. Допустим, заказ создан в учётной системе, а задание на отгрузку ещё нет. Повтор всей цепочки с самого начала может заново создать заказ. Для такого случая следует определить точку возобновления и сохранить связь между завершёнными шагами. Если требуется отмена уже выполненного действия, у неё должны быть собственные правила и подтверждение; исчезновение локальной записи ничего не отменяет снаружи.
Обещание «ровно один раз во всех системах» без разбора этих границ проверить нельзя. Для руководителя полезнее конкретный ответ: какое действие защищено, как узнаётся его результат и в какой ситуации автоматическое продолжение прекращается.
Как проверить готовый коннектор
При автоматизации рабочих процессов стоит сначала проверить уже используемый модуль обмена и возможности принимающей системы. Если они обеспечивают нужное поведение, задача может решиться настройкой и проверкой. Собственная доработка имеет смысл после того, как найдено конкретное ограничение готового пути и понятны требования к поддержке.
Фразы в описании модуля «защита от дублей» недостаточно. Она может относиться к повторной загрузке карточки заказа, тогда как следующий шаг создаёт новое складское задание при каждом запуске. Нужно попросить исполнителя описать одну выбранную цепочку до конечного действия, указать её ключ и место хранения результата. Отдельный вопрос — сохраняет ли кнопка ручного повтора прежнюю операцию или начинает новую.
Затем нужны проверки в безопасном тестовом контуре, без реальных списаний и отгрузок. Ниже предлагаемые сценарии, а не результаты проведённого испытания:
- Повторно доставить сообщение о той же операции, в том числе одновременно. В принимающей системе должно остаться одно разрешённое задание, а история должна объяснять судьбу каждой попытки.
- Прервать обмен после создания задания, но до сохранения ответа коннектором, затем перезапустить обработку. Ожидаемый результат — найденное прежнее задание или остановка для выяснения исхода, без слепого создания нового.
- Остановить обработчик после приёма сообщения, но до выполнения действия. После восстановления работа должна возобновиться по согласованным правилам, а сообщение не должно исчезнуть из-за уже отправленного подтверждения доставки.
- Отдельно оформить две настоящие покупки с одинаковыми товарами и суммой. Обе должны пройти: защита от дублей не должна запрещать законный второй заказ.
- Передать изменённые данные с прежним ключом, а затем поздний повтор после окончания срока защиты. В обоих случаях нужно увидеть заранее согласованное поведение, без незаметной подмены прежней операции новой.
- Прервать цепочку после одного из завершённых шагов. При возобновлении уже выполненный шаг не должен повториться; если продолжение невозможно, случай должен попасть на разбор с известным промежуточным результатом.
Доказательство здесь — записи и действия в принимающей системе, сопоставленные с исходной операцией. Зелёная отметка об успешной доставке не заменяет такую проверку. Если тестовый контур не позволяет воспроизвести часть сбоев, эти ограничения нужно сохранить в результате проверки, а не считать сценарии пройденными по умолчанию.
Сколько хранить историю и кто разбирает исключения
Защита от повтора может иметь срок. У ЮKassa идемпотентность запроса обеспечивается в течение 24 часов после первого запроса; затем повтор обрабатывается как новый. Stripe для API v1 описывает возможность удаления ключей после того, как им исполнилось не менее 24 часов: использование удалённого ключа создаёт новый запрос. Это разные формулировки контрактов. Документация Stripe отдельно различает поведение API v1 и v2 (разных версий этого интерфейса), поэтому правило v1 нельзя молча распространять на другую версию.
Эти сроки не задают время хранения истории заказа в компании. Для своей цепочки нужно сопоставить допустимые задержки доставки, накопление сообщений при сбое и возможность ручного возобновления. Если сотрудник может вернуться к старой операции после окончания внешней защиты, сохранённый ключ уже не даёт прежних оснований для безопасного повтора. Нужен способ проверить результат или передать случай ответственному. AWS также рассматривает поздние запросы и прямо отмечает, что требования к хранению различаются между сервисами и ресурсами.
Ручной разбор должен быть частью работы интеграции. Нужно заранее назначить ответственную роль и согласовать, где она увидит остановленные случаи. Ответственному нужны исходная операция, история попыток, известные результаты в принимающих системах и причина остановки. Например, «неизвестно, создано ли задание» — другой случай, чем «задание точно создано, следующий шаг не выполнен». Для них нельзя оставлять одну неразличимую кнопку «отправить ещё раз».
До возобновления ответственный выясняет состояние и выбирает разрешённое действие. Решение нужно сохранить вместе с основанием, чтобы следующий сотрудник или автоматический повтор не начал тот же разбор заново. Возможность создать новую операцию должна отличаться от продолжения старой и требовать явного решения там, где это предусмотрено процессом.
Начать разбор собственной интеграции можно с одной проблемной отгрузки. Если по ней видно, какое действие было разрешено, где сохранился его результат и почему повтор не создаст новое задание, появляется предметная основа для приёмки обмена. Если связь теряется, это конкретное требование к настройке или доработке, которое стоит закрыть до следующего возобновления.
Источники
Платёжные API ниже используются как документированные примеры условий обмена. Это не рекомендация платёжной системы и не испытание CRM или склада. Условные ситуации и сценарии проверки в статье не являются клиентским кейсом.
- Stripe: получение событий и обработка повторов.
- Stripe: идемпотентные запросы, различия API v1 и v2.
- ЮKassa: формат взаимодействия, идемпотентность и неизвестный результат.
- ЮKassa: входящие уведомления и подтверждение получения.
- AWS Builders’ Library: Making retries safe with idempotent APIs.
Использованы сохранённые полные тексты источников, полученные 16 октября 2026 года. Возможности конкретного коннектора и принимающей системы нужно проверять отдельно.
kAIros