Диагностика платежей в n8n: webhooks, idempotency и CRM ¶
Обновлено: 2026-05-29
Короткий ответ ¶
Платёжный webhook в n8n нужно проектировать как финансовый журнал, а не как обычный “триггер для CRM”. Главная задача — принять событие один раз, связать его с заказом, сохранить исходный payload, вернуть успешный ответ провайдеру и только потом обновлять CRM, склад, Telegram и отчёты. Если workflow сразу делает всё подряд, любая задержка CRM, лимит API или ошибка маппинга превращает платёж в дубль, пропущенный заказ или ручной разбор.
Чем эта страница отличается от диагностики ЮKassa ¶
Страница про ЮKassa должна решать частный кейс конкретного провайдера. Эта страница — общий чеклист архитектуры для любых платежных webhook: ЮKassa, CloudPayments, Stripe-подобные API, внутренний биллинг, кассовый модуль или кастомный backend. Здесь важны не названия полей, а принципы: статусы, идемпотентность, журнал, retry, алерты и ручная сверка.
Быстрая развилка по симптомам ¶
| Симптом | Что обычно сломано | Что исправлять |
|---|---|---|
| Оплата прошла, заказ не обновился | нет связи payment → order | metadata, order table, CRM field |
| Один заказ обновился дважды | повтор webhook обработан как новый | idempotency storage |
| Статус отмены перезатёр оплату | нет таблицы разрешённых переходов | state machine для заказа |
| Возврат не виден в отчётах | возвраты смешаны с оплатами | отдельный поток refund events |
| Провайдер повторяет доставку | n8n не отвечает 2xx стабильно |
быстрый response и очередь |
| Менеджер не видит ошибку | failed execution остаётся в n8n | алерт в Telegram/почту |
Минимальная схема надёжного платежного workflow ¶
Разделите платежный процесс на пять логических блоков:
- Приём события — Webhook node, базовая валидация, быстрый ответ.
- Журналирование — запись
event_id,payment_id,order_id, статуса, суммы и сырого payload. - Идемпотентность — проверка, обрабатывалось ли такое событие раньше.
- Бизнес-решение — перевод заказа, уведомления, склад, чек-лист менеджера.
- Контроль — алерты, reconciliation, отчёт неизвестных статусов.
Такую схему можно реализовать даже без сложной инфраструктуры. Для MVP подойдёт Google Sheets или база CRM, но для боевых оплат лучше использовать Postgres с уникальными ключами.
Таблица статусов вместо IF-хаоса ¶
Не разносите платежные статусы по десятку IF-нод. Лучше завести единую таблицу переходов. Она может быть в Code node, Postgres или отдельном JSON-конфиге.
{
"payment.succeeded": {
"order_status": "paid",
"crm_stage": "Оплачено",
"notify": ["sales", "ops"]
},
"payment.canceled": {
"order_status": "payment_canceled",
"crm_stage": "Оплата отменена",
"notify": ["sales"]
},
"refund.succeeded": {
"order_status": "refunded",
"crm_stage": "Возврат",
"notify": ["finance"]
}
}
Плюс такого подхода — его легко проверить вручную. Если появляется новый статус, workflow не должен молча выбирать “ветку по умолчанию” и портить заказ. Неизвестное событие сохраняется в журнал и отправляется в алерт.
Идемпотентность: где хранить и что считать дублем ¶
Повторная доставка webhook — нормальная часть платёжных систем. Провайдер может повторить событие при таймауте, временной ошибке или невалидном ответе. Поэтому “дубль” — это не баг провайдера, а сценарий, к которому workflow обязан быть готов.
Практичные варианты хранения:
| Хранилище | Подходит для | Минусы |
|---|---|---|
| Postgres | production, много оплат | нужна база и миграции |
| CRM custom field | малый поток, всё в CRM | сложно хранить сырой payload |
| Google Sheets | быстрый старт | лимиты, гонки, ручные правки |
| Redis | временная защита от повторов | не заменяет финансовый журнал |
Ключ события должен включать стабильный идентификатор объекта и тип события. Если использовать только order_id, можно случайно заблокировать возврат после оплаты. Если использовать только event, все успешные оплаты станут “одинаковыми”.
Как не потерять платёж из-за CRM ¶
CRM — не источник истины для оплаты. CRM может быть недоступна, вернуть 429, принять не все поля или изменить API. Поэтому порядок безопаснее такой:
- Платёжное событие записано в финансовый журнал.
- Заказ найден по
order_idилиpayment_id. - CRM обновлена.
- Результат CRM-обновления записан отдельно.
- При ошибке CRM создаётся retry-задача или алерт.
Если CRM недоступна, платежный журнал всё равно должен показать: деньги пришли, заказ найден, обновление CRM не выполнено. Тогда оператор чинит интеграцию, а не ищет платёж “в воздухе”.
Retry и rate limit ¶
Для исходящих HTTP Request в n8n используйте Retry on Fail и разумные задержки, но не включайте бесконечные повторы. Платёжные действия должны быть предсказуемыми: 2–3 попытки, запись ошибки, алерт, ручной повтор после анализа. Если провайдер или CRM возвращает 429, лучше добавить batching/Wait, а не запускать сотни одновременных запросов.
Отдельно проверьте, какие действия можно безопасно повторять. Уведомление менеджера можно повторить, но создание возврата или повторный запрос на списание — только с ключом идемпотентности и журналом.
Контрольная сверка раз в день ¶
Даже хороший webhook не заменяет сверку. Сделайте отдельный scheduled workflow:
- взять заказы за последние 24 часа;
- получить актуальные платежные статусы из API провайдера;
- сравнить с CRM;
- найти расхождения;
- отправить короткий отчёт финансовому или ответственному менеджеру.
Это особенно важно, если бизнес использует несколько каналов оплат, ручные возвраты или менеджеры могут менять статус заказа руками.
FAQ ¶
Почему нельзя сразу обновлять CRM из платежного webhook?
Можно, но рискованно. Если CRM зависнет, webhook может не получить успешный ответ, а провайдер повторит событие. Лучше сначала принять и записать событие, затем обновлять CRM отдельным шагом.
Какой статус заказа считать финальным?
Финальность зависит от провайдера и бизнес-процесса. Обычно succeeded означает успешную оплату, но возвраты и отмены нужно обрабатывать отдельными событиями, а не перетирать историю.
Что делать с неизвестным платежным событием?
Не менять заказ автоматически. Сохранить payload, отправить алерт и добавить новый статус в таблицу переходов после проверки.
Где хранить сырой payload?
В журнале событий: Postgres, отдельная таблица, защищённое хранилище. Не храните в открытых логах секреты и лишние персональные данные.
Как проверить, что дублей больше не будет?
Отправьте один и тот же payload дважды. Первый запуск должен изменить заказ, второй — попасть в ветку “уже обработано”.