Пять критичных ошибок интеграции API при запуске
Ошибка №1. Нет идемпотентности и «шторм» ретраев
Симптомы: дубли заказов/платежей, расхождение сумм, спорные возвраты, алерты DLQ растут.
Корень: повторная доставка запросов/вебхуков и сетевые флаппи — нормальны. Если операция «создать/списать» не идемпотентна, ретраи умножают ущерб.
Как правильно
Idempotency-Key/`operation_id` на все небезопасные методы (POST/PATCH).
Уникальный индекс в БД по `operation_id`. Повтор — верните прошлый результат.
Вебхуки через Inbox-таблицу (dedupe по `event_id+signature`). Исходящие события — Outbox.
Ретраи: максимум 1–2 раза, экспонента + джиттер, только для безопасных операций.
HTTP-конвенция (пример):http
POST /v1/payments
Idempotency-Key: ik_f35a2
Content-Type: application/json
{"amount": 5000, "currency": "EUR", "source": "card_..."}sql
ALTER TABLE payments ADD CONSTRAINT uniq_op UNIQUE (operation_id);python for i in range(2):
try: return call_api(payload, timeout=0.6)
except Timeout:
sleep(0.05 2i + random.uniform(0, 0.05))
raise UpstreamUnavailable- Вся «денежная/создающая» логика имеет `operation_id` и uniq-индекс.
- Входящие вебхуки только через Inbox с идемпотентным воркером.
- Клиентская SDK автоматически проставляет Idempotency-Key.
Ошибка №2. Тайм-ауты/ретраи против SLO: «перегрев» зависимостей
Симптомы: p95 внезапно уплывает, очереди растут, circuit breaker «челкает».
Корень: общий SLO ответа — 400–600 мс, а тайм-ауты к внешним API — по 1–2 с, да ещё ретраи ×3. Вы делаете дольше, чем можете, и штурмуете зависимость повторами.
Как правильно
Budget-тайминг: если SLO 400 мс, upstream-тайм-аут: 250–300 мс; общий тайм-аут запроса ≤ SLO.
Limits/Backpressure: семафоры/worker-pool на вызовы к каждой зависимости. Переполнено → 429/503 сразу.
Circuit breaker: `open` при тайм-аутах/5xx, `half-open` дозировано.
Admission control: ограничьте параллелизм (на поток, на endpoint/PSP).
Пример (Go):go sem:= make(chan struct{}, 64) // лимит конкуренции к PSP func callPSP(ctx context.Context, req Req) (Res, error) {
select {
case sem <- struct{}{}:
defer func(){ <-sem }()
c, cancel:= context.WithTimeout(ctx, 300time.Millisecond)
defer cancel()
return psp.Do(c, req)
default:
return Res{}, ErrBusy // немедленный отказ вместо очереди без конца
}
}- Тайм-ауты короче SLO; ретраи ≤ 2; есть джиттер.
- Пулы/семафоры на внешние API; circuit breaker с метриками.
- На «busy» маршрутах возвращаем 429/Retry-After, а не держим соединения.
Ошибка №3. Слабая безопасность: подписи вебхуков, секреты, TLS
Симптомы: «чужие» вебхуки проходят, секреты в коде/логе, MITM-риски.
Корень: нет проверки подписи/свежести, секреты живут в env-файлах, старые TLS и слабые заголовки.
Как правильно
Подпись вебхуков HMAC-SHA256 + `X-Timestamp` (окно ≤ 5–10 мин), строгое сравнение подписи.
mTLS для критичных интеграций или IP allow-list.
Ротация секретов через Vault/Cloud KMS; минимум прав; аудит вычитки.
TLS 1.2/1.3 only, HSTS, правильные CORS (узкий список источников).
Проверка подписи (Python):python def verify(sig_hdr, ts_hdr, body, secret):
if abs(time.time() - int(ts_hdr)) > 600: raise Expired()
calc = hmac.new(secret, (ts_hdr + "." + body).encode(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(calc, sig_hdr): raise BadSig()- Все вебхуки подписаны и проверяются; окно свежести ограничено.
- Секреты в KMS/Vault, есть ротация и аудит.
- TLS/HSTS включены; CORS точечный; IP/mTLS где уместно.
Ошибка №4. Контракт-дрифт: схема «жила своей жизнью»
Симптомы: прод падал «только у части клиентов», 500/422 в логах, разные версии SDK и API спорят.
Корень: нет строгого описания контрактов, обратно несовместимые изменения, «тихие» поля, разный смысл у одинаковых названий.
Как правильно
Контракт-первый: OpenAPI/AsyncAPI + генерация серверов/клиентов; для событий — Avro/Protobuf + Schema Registry.
Версионирование: `v1 → v2` (URI/хедер), deprecation-план, grace-период.
Backward-compat: только additive изменения в минорных релизах; запрет удалять/переименовывать без v-bump.
Контрактные тесты: Pact/Buf — провайдер/консумер проверяются в CI.
Примеры:yaml
OpenAPI: чёткий тип суммы в минорных единицах amount_minor:
type: integer minimum: 0 description: Сумма в минимальных единицах валюты (целое)- Контракты хранятся в git, CI валидирует/ломает при несовместимости.
- Регистры схем для событий, совместимость «back/forward».
- Док-страница изменений, даты деприкации, тестовый стенд для партнёров.
Ошибка №5. «Слепой» запуск: нет метрик/логов/трейсов и песочницы
Симптомы: «ничего не видно», поддержку заваливает, дебаг — руками в проде.
Корень: не включили наблюдаемость, нет синтетики, песочницу тестили «на словах».
Как правильно
RED/USE-метрики: rate/error/latency на каждом endpoint, по маршрутам/методам.
Корреляция: `trace_id` во всех логах и ответах; связка запрос↔вебхук.
Синтетика: health-пробы (login/deposit-песок), SLA-мониторинг T+60 для вебхуков.
Песочница/стейдж: полностью изолированные ключи/домены, фиктивные PSP, записи «не попадают в отчёты».
Ответ с trace-идентификатором:http
HTTP/1.1 202 Accepted
Trace-Id: 7f2b3d8e9c1a4
Location: /v1/ops/req_42/status- Метрики RED/USE, дашборды, алерты (симптомы + причины).
- Трейсы end-to-end; логи JSON, без PII, с `trace_id`.
- Синтетика из ключевых регионов; песочница обязательна, ключи разные.
Прелаунч-план (T-7 → T-0)
T-7 дней:- Финальный контракт-скан: нет ли несовместимых изменений; freeze схем.
- Секреты/сертификаты: проверьте ротацию, доступы, KMS-политики.
- Синтетика 24×7, алерты привязаны к on-call.
- Нагрузочный мини-прогон (burst 2–5 мин): p95/пулы/очереди в зелёной зоне.
- DRY-RUN вебхуков (повторы, 5xx, джиттер), проверка DLQ.
- «Книга телефонов» партнёров: L1/L2 контакты, war-room канал.
- Канареящий трафик 5% → 25% → 50% по SLO-гейтам; готов rollback.
- Включены kill-switch/feature-flags на рисковых фичах.
- War-room активен, статус-шаблоны подготовлены.
Роллбэк-план (если что-то пошло не так)
1. Снять трафик на предыдущую стабильную версию/маршрут.
2. Отключить фичефлагом спорные изменения.
3. Стабилизировать очереди/пулы, остановить ретраи при «шторме».
4. Пост-инцидент: собрать таймлайн, корни, задачи (фикс-форвард/фиксы контракта).
Таблица самопроверки на запуск (короткая)
Часто задаваемые «а как быть, если…»
…провайдер не поддерживает Idempotency-Key?
Храните `hash(body)` + `partner_request_id` и вводите свою идемпотентность.
…вебхуки иногда приходят «раньше» ответа?
Сшивайте по `operation_id` и временно держите «unknown → reconcile» статус; периодический reconciler закроет расхождения.
…надо поддержать старых клиентов и новых?
Версионируйте endpoint’ы (`/v1` и `/v2`), маршрутизируйте по заголовку/URI, держите backward-совместимость минимум N месяцев.
Резюме
Провалы интеграций почти всегда про одно и то же: нет идемпотентности, неправильные тайм-ауты и ретраи, слабая подпись вебхуков, дрейф контрактов и отсутствие видимости. Заранее зафиксируйте контракты, включите наблюдаемость, расставьте лимиты/бекпрешер, подпишите все внешние взаимодействия и запустите синтетику. Тогда даже при сбоях у партнёров ваш релиз останется управляемым — без денег, потерянных в ретраях, и без бессонной ночи у всей команды.
