- «Нам нужна простая интеграция: сайт передает заказы в CRM и ERP по API».
- «Какие именно события: создание, резерв, оплата, отмена, возврат, смена адреса?»
- «Ну... заказ».
Обычно именно в этот момент проект и начинает ползти по срокам. Короче, API для интеграции - это не набор POST и GET, а договоренность между системами: кто меняет состояние заказа, где итоговая правда по оплате, что считать сбоем связи, а что бизнес-ошибкой.
Сначала сценарии, потом endpoint'ы
Самая частая ошибка - сразу рисовать Swagger. На практике первым документом должна быть карта сценариев: кто инициирует событие, какая система источник по каждой сущности, какие статусы вообще существуют и кто имеет право их менять.
У заказа почти никогда нет состояния «просто есть». Обычно это «создан», «подтвержден», «оплачен», «в резерве», «собран», «частично отгружен», «отменен», «возвращен». Если это не проговорить заранее, API потом растет рывками, а каждая доработка тянет пересогласование с CRM, ERP, складом и бухгалтерией.
У нас был интернет-магазин на Laravel 11. На старте описали только создание заказа и оплату. После запуска приехали 7 новых статусов и split shipment. В итоге получили 30-40% доработок к уже согласованному контракту. Код был нормальный, проблема оказалась в аналитике.
Я бы перед endpoint'ами закрыл вот что:
- кто инициирует каждое событие;
- список систем-источников по заказу, оплате и остаткам;
- какие переходы статусов допустимы;
- где бизнес-ошибка, а где сбой связи;
- какие операции должны переживать повторы и задержки.
Когда поля «и так понятны», люди начинают чинить руками
В маленьких проектах это любимая ловушка. Все смотрят на поле status и думают, что уже договорились. Потом на сайте оказывается 5 статусов, в CRM - 12, в ERP - 8, и менеджер руками правит заказы, чтобы документы хоть как-то сходились.
Мы это проходили в связке сайт + CRM + 1С. Пока не сделали каноническую модель данных и таблицу маппинга, ручная обработка держалась на уровне 15-20% операций. У бухгалтера своя логика плательщика, у склада - своя логика получателя, а в интерфейсе это выглядело почти одинаково.
Swagger показывает форму данных. Интеграцию держит смысл полей и правила переходов.
Вопрос: Достаточно ли согласовать названия полей в OpenAPI?
Ответ: Нет. Нужны обязательность, справочники, ограничения, правила маппинга и понятный словарь терминов. Иначе через месяц команда будет «временно» править руками то, что стоило договорить на старте.
Хороший контракт проверяется на дублях и таймаутах
По основному сценарию обычно все выглядит аккуратно. Запрос пришел, заказ создался, статус вернулся. Настоящие расходы начинаются позже - когда ERP подвисла, клиент нажал кнопку еще раз, а система создала второй документ.
В одном B2B-проекте на Node.js 20 + NestJS счета дублировались после сетевого таймаута. Причина была простая: мы не договорились про Idempotency-Key. Потом добавили ключ, хранение результата в Redis на 24 часа, и за 2 недели инциденты этого класса почти исчезли.
Тут полезна очень простая матрица:
| Ситуация | Код | Смысл |
|---|---|---|
| Объект создан сразу | 201 Created |
операция завершена |
| Приняли в обработку | 202 Accepted |
результат будет позже |
| Конфликт состояния | 409 Conflict |
объект есть, но статус другой |
| Бизнес-правило нарушено | 422 Unprocessable Entity |
формат верный, смысл нет |
Когда команда валит все в 500, первая линия поддержки потом разбирает бизнес-ошибки как аварии инфраструктуры. Это дорогая привычка.
Иногда лучший ответ API - «принято»
Синхронная схема всем нравится на демо: нажал кнопку и сразу получил результат. Но если в цепочке есть медленная ERP, эта красота быстро заканчивается.
В прошлом году мы переделывали интеграцию оптового кабинета. До этого фронт ждал ERP по 8-12 секунд, пользователи кликали повторно, а в системе росли дубли.
До: фронт ждет финальный ответ от ERP, таймауты, повторы, ручные разборы.
После: API валидирует минимум, отвечает 202 Accepted за 300-500 мс, дальше заказ уходит в RabbitMQ, итоговый статус приходит позже. На пике число ошибок просело больше чем на 40%.
Я бы в таких задачах фиксировал не только REST-контракт, но и события: что ушло в очередь, когда операция считается принятой, где проверять финальный результат. Если интеграция событийная, AsyncAPI тоже полезен, но только после сценариев, а не вместо них.
Плохой знак - когда вторую интеграцию API уже не переживает
Самый неприятный провал у нас был в проекте, где API сделали строго под один сайт. Тогда это казалось экономией. Через полгода подключили мобильное приложение и кабинет дилера, и стало видно, что ответы завязаны на логику конкретных экранов.
Расширить контракт не получилось, пришлось его ломать и пересобирать. Рефакторинг занял 6 недель. Мы тогда сэкономили на старте пару обсуждений и сильно переплатили позже. Это была наша ошибка, вполне приземленная и дорогая.
Если хотите быстро понять, насколько API вообще спроектирован, попросите у команды не список методов, а набор артефактов:
- карта событий и критичных сценариев;
- каноническая модель данных и маппинги;
- правила ошибок, повторов и идемпотентности;
- политика версий;
- тестовые сценарии в Postman или Insomnia.
Перед согласованием API я бы задал один вопрос: если завтра добавится еще один канал продаж, вы будете расширять контракт или переписывать его с нуля? Вот там обычно и лежит реальная цена интеграции.