RussiaAPI

Технический разбор

Идемпотентная обработка webhook video API без повторной выдачи результата

Идемпотентная обработка webhook video API защищает не от самого HTTP-повтора, а от повторной бизнес-операции: второй выдачи результата, лишнего письма, повторного списания внутреннего лимита или перехода задачи назад. Callback из сети нужно считать непроверенными данными до проверки подписи по актуальному контракту. После этого обработчик фиксирует delivery, сопоставляет внутреннюю операцию и передаёт побочный эффект в outbox. RussiaAPI — независимый API gateway; названия событий, заголовков и наличие webhook необходимо подтверждать в текущей документации для конкретного видео-сценария.

Опубликовано 4 сентября 2026 · 10 минут чтения · Ключевой запрос: идемпотентная обработка webhook video API

Граница сервиса. RussiaAPI — независимый сторонний API gateway, а не официальный сервис OpenAI, Anthropic, Google или производителя модели. Совместимый формат описывает отдельный контракт запроса и не обещает одинаковые модели, лимиты, webhook, хранение или цены. Используйте только собственный RUSSIAAPI_API_KEY на сервере; не передавайте внешние ключи, cookie, пароли, коды подтверждения или лишние персональные данные.

Разделите подлинность и идемпотентность

Проверка подписи отвечает на вопрос, можно ли доверять доставке как источнику. Идемпотентность отвечает на другой вопрос: что делать, если та же самая допустимая доставка придёт ещё раз. Нельзя заменять одно другим. Сначала прочитайте raw body без преобразования, проверьте документированный timestamp, подпись и допустимое окно времени. Только затем извлеките event ID и полезные поля. Не угадывайте HMAC-заголовок и не принимайте самодельную подпись как подтверждение RussiaAPI.

Подробный порядок проверки показан в статье о подписи webhook. Если текущий контракт не предоставляет event ID, создайте устойчивый fingerprint из безопасных документированных полей после проверки подписи, но не используйте prompt, ключ или временный URL как идентификатор. Любое допущение о формате нужно зафиксировать как вашу интеграционную policy и покрыть контрактным тестом.

Заведите журнал доставок

Таблица deliveries хранит уникальный provider event ID или fingerprint, внутренний task ID, время получения, результат валидации и итог обработки. Уникальный индекс — реальная защита от гонки: два одновременных HTTP-процесса могут одновременно увидеть, что записи ещё нет. Один из них должен атомарно вставить delivery, а второй получить безопасный duplicate и вернуть 2xx без повторного изменения задачи.

Не превращайте журнал доставок в архив персональных данных. Достаточны ID, хеш, тип события, время, безопасный status и короткая причина ошибки. Raw payload храните только если он действительно нужен для расследования, с минимальным сроком и контролем доступа; секретные заголовки не сохраняйте вовсе. Для диагностики используйте внутренний request ID без prompt и Authorization, как описано в безопасной трассировке.

Переходы состояния должны быть монотонными

У внутренней video-задачи есть разрешённые переходы: submitted может стать processing, ready или failed; ready не должен вернуться в processing от запоздалого callback. Сверяйте внешний task reference с ожидаемым владельцем и tenant, затем применяйте переход в одной транзакции с записью delivery. Если событие приходит раньше, чем ваша задача успела сохранить provider reference, не принимайте его за чужой результат: пометьте для ограниченной сверки или отложите обработку.

Порядок событий не гарантирован сетью. Поэтому поле event time полезно для аудита, но не заменяет таблицу переходов. Не сравнивайте строковые статусы «по алфавиту» и не доверяйте timestamp от клиента. Ваша state machine должна явно отклонить недопустимый переход и отправить его на безопасную проверку. Это уменьшает риск повторной выдачи и дополняет модель асинхронной video-задачи.

Транзакция плюс outbox

Ниже пример Node.js 18+ показывает основной порядок. Функции базы обозначают одну транзакцию: вставка delivery с уникальным индексом, чтение задачи, разрешённый переход и запись outbox происходят вместе. Внешнее уведомление не отправляется прямо из HTTP-обработчика: отдельный worker читает outbox и сам делает ограниченные, идемпотентные попытки. Так ответ callback не зависит от почтового сервиса, а повтор доставки не создаёт второе сообщение.

export async function processVideoWebhook({ rawBody, headers }) {
  if (!verifyCurrentContractSignature(rawBody, headers)) return { statusCode: 401 };
  const event = JSON.parse(rawBody);
  if (!event.id || !event.task_id) return { statusCode: 400 };
  return await db.transaction(async (tx) => {
    const inserted = await tx.insertDeliveryOnce({ providerEventId: event.id, type: event.type });
    if (!inserted) return { statusCode: 204 }; // already durably handled
    const task = await tx.findTaskByProviderId(event.task_id);
    if (!task || !canTransition(task.status, event.type)) return { statusCode: 202 };
    await tx.applyTransition(task.id, event.type);
    await tx.insertOutbox({ key: `video-ready:${task.id}`, kind: 'notify_owner' });
    return { statusCode: 204 };
  });
}

Возвращайте 2xx только после того, как обработчик действительно записал безопасный итог. При временном сбое базы выберите поведение согласно документированному контракту: возможно, нужно вернуть контролируемый 5xx, чтобы поставщик повторил доставку. Не подтверждайте событие до записи, иначе его можно потерять. Пример не раскрывает ключи и не утверждает, что конкретный endpoint или event type существует в каждом плане RussiaAPI.

Replay — отдельная операторская команда

Безопасный replay не означает «послать старый HTTP-запрос ещё раз». Оператор выбирает delivery по внутреннему ID, проверяет её статус и запускает один контролируемый обработчик, который всё равно встречает уникальную запись и state machine. Если нужно повторить только уведомление, worker читает существующую outbox-запись вместо изменения video-задачи. Это позволяет понять, что именно повторяется: приём callback, сверка статуса или побочный эффект.

Для неразрешимых случаев используйте отдельную очередь ошибочных доставок с причиной, числом попыток и сроком расследования. Не оставляйте её как бесконечное хранилище и не запускайте массовый replay без лимита. В руководстве по DLQ описаны ограниченные повторы и сверка перед возвратом в поток. Replay не предназначен для обхода лимитов, условий поставщика или прав на контент.

Проверьте негативные сценарии

До запуска воспроизведите: корректную доставку два раза одновременно, неверную подпись, event ID с другой задачей, запоздалое processing после ready, недоступную базу и падение notification worker. Во всех случаях убедитесь, что готовый результат выдаётся только владельцу внутренней операции и что временная ссылка не попадает в общий лог. Проверка подписи и идемпотентность не заменяют авторизацию браузерного маршрута.

Периодически сопоставляйте terminal-задачи и deliveries: есть ли ready без записанного события, события без задачи, outbox без завершённого перехода или слишком старые checking. Эти метрики говорят о здоровье вашей интеграции, но не доказывают гарантии внешнего сервиса. Если контракт изменился, остановите рискованный маршрут, обновите интеграционный тест и только потом возобновляйте приём событий. Это честнее, чем подгонять обработчик под неизвестный формат.

Проверьте сценарий в RussiaAPI

Создайте собственный тестовый ключ в консоли, сверьте текущий каталог моделей и начните с обезличенного server-side smoke test. Расширяйте доступ и нагрузку только после измеримой проверки.

Открыть консоль RussiaAPI · Документы · Каталог моделей

FAQ

Достаточно ли HMAC-подписи для защиты от дублей?

Нет. Подпись подтверждает доставку по текущему контракту, но поставщик или сеть могут повторить корректное событие. Нужны уникальный журнал delivery и разрешённые переходы внутренней задачи.

Почему уведомление нужно отправлять через outbox?

Так HTTP-обработчик сначала надёжно записывает факт события и состояние задачи. Отдельный worker повторяет только уведомление, а не весь callback и не выдачу результата.

Можно ли replay сделать из браузера?

Нет. Replay — операторская процедура в защищённом контуре. Она должна проверять полномочия, лимит, внутренний ID и состояние, не принимая произвольный raw payload от клиента.

Читайте также