Перейти к содержанию

Интеграция ЮKassa и CRM через n8n: оплата, чек и идемпотентность

Обновлено: 2026-05-30

Открыть мой план
Шаблон для внедрения

Импортируйте JSON в n8n, замените CRM endpoint, Postgres credential и правило поиска сделки по order_id.

Проблема: платежные уведомления ЮKassa могут приходить повторно, а неуспешные статусы выглядят почти так же, как успешные. Если workflow просто принимает webhook и сразу меняет сделку в CRM, можно дважды начислить услугу, отправить два письма или закрыть не тот заказ.

Решение: обрабатывать webhook как финансовое событие: проверить event, status и paid, собрать idempotency key по payment_id, при необходимости сверить статус через API ЮKassa и только потом обновить сделку в CRM.

Статья закрывает сценарий “ЮKassa → n8n → CRM”: готовый workflow JSON, тестовый payload, Code Node, curl, таблица архитектуры, production-риски и чек-лист запуска.

Схема интеграции ЮKassa и CRM через n8n с проверкой идемпотентности
Между уведомлением ЮKassa и CRM стоит проверка статуса и idempotency key.

Проблема: почему платежный webhook ЮKassa нельзя обрабатывать как обычную заявку

Форма заявки может терпеть повтор, а платеж — нет. Для оплаты важно не только получить payment.succeeded, но и понять, какой заказ или сделку обновлять, что делать при повторном уведомлении и как доказать, что business action выполнен один раз.

ЮKassa ожидает подтверждение получения webhook, но ответ 200 OK не должен означать “мы уже закрыли сделку”. Он должен означать “мы приняли событие и обработали его по безопасному правилу”. Поэтому workflow лучше строить вокруг идемпотентности и журнала платежей.

Архитектура workflow ЮKassa → n8n → CRM

НодаРольЧто проверить
YooKassa WebhookПринимает notificationHTTPS endpoint, секретный путь, корректный 200 ответ
Normalize paymentПроверяет event/status/paidpayment.succeeded, paid=true, metadata
Check idempotencyИщет payment_id в журналеPostgres unique key или другой durable storage
Verify payment statusСверяет статус с API ЮKassa при спореBasic Auth/OAuth, timeout, 401/429
Update CRM dealМеняет статус сделки и добавляет комментарийСумма, валюта, order_id/deal_id
Respond 200Подтверждает получениеБез stack trace и персональных данных

Контракт входных данных payment.succeeded

Ключевая практика — передавать order_id или deal_id в metadata при создании платежа. Тогда webhook можно связать с CRM без парсинга описания “Заказ №...”.

{
  "type": "notification",
  "event": "payment.succeeded",
  "object": {
    "id": "2f3c6a99-000f-5000-9000-1d2a3b4c5d6e",
    "status": "succeeded",
    "paid": true,
    "amount": { "value": "12900.00", "currency": "RUB" },
    "income_amount": { "value": "12460.00", "currency": "RUB" },
    "description": "Заказ №10492",
    "metadata": {
      "order_id": "10492",
      "deal_id": "crm-5581",
      "customer_phone": "+7 916 123-45-67"
    },
    "created_at": "2026-05-30T10:00:00.000Z",
    "test": false
  }
}

Не используйте только сумму и телефон для поиска сделки: это легко ломается при частичных оплатах, доплатах и одинаковых заказах.

Проверка платежа и idempotency key (Code Node)

Code Node ниже отсекает неуспешные события, проверяет metadata и формирует стабильный ключ для журнала идемпотентности.

const event = $json.event;
const payment = $json.object ?? {};
if (event !== 'payment.succeeded' || payment.status !== 'succeeded' || payment.paid !== true) {
  return [{ json: { action: 'ignore', reason: 'not_successful_payment', event, status: payment.status } }];
}

const orderId = String(payment.metadata?.order_id ?? '').trim();
const dealId = String(payment.metadata?.deal_id ?? '').trim();
if (!orderId && !dealId) throw new Error('No order_id or deal_id in YooKassa metadata');

const amount = payment.amount?.value;
const currency = payment.amount?.currency ?? 'RUB';
const paymentId = payment.id;
const idempotencyKey = `yookassa:${paymentId}:succeeded`;

return [{
  json: {
    action: 'mark_paid',
    idempotency_key: idempotencyKey,
    payment_id: paymentId,
    order_id: orderId,
    deal_id: dealId,
    amount,
    currency,
    crm_comment: `Оплата ЮKassa ${amount} ${currency}, payment_id=${paymentId}`,
    paid_at: payment.created_at,
    test_payment: payment.test === true
  }
}];

Дальше этот ключ должен попасть в таблицу с уникальным индексом. Если запись уже существует, workflow не должен повторно менять CRM и отправлять уведомления.

Готовый workflow JSON: скачать и импортировать

Полный workflow JSON находится в архиве сайта. Перед запуском замените Postgres credential, CRM endpoint и способ проверки платежа под вашу модель авторизации ЮKassa.

{
  "name": "Nodbot - YooKassa payment to CRM with idempotency",
  "nodes": [
    { "name": "YooKassa Webhook", "type": "n8n-nodes-base.webhook", "purpose": "Принять notification от ЮKassa" },
    { "name": "Normalize payment", "type": "n8n-nodes-base.code", "purpose": "Проверить event/status/paid и собрать idempotency key" },
    { "name": "Check idempotency", "type": "n8n-nodes-base.postgres", "purpose": "Не обработать payment_id дважды" },
    { "name": "Verify payment status", "type": "n8n-nodes-base.httpRequest", "purpose": "При необходимости сверить статус с API ЮKassa" },
    { "name": "Update CRM deal", "type": "n8n-nodes-base.httpRequest", "purpose": "Поставить статус Оплачено и добавить комментарий" },
    { "name": "Respond 200", "type": "n8n-nodes-base.respondToWebhook", "purpose": "Подтвердить получение уведомления" }
  ],
  "connections": "Webhook → Normalize → Idempotency → Verify → CRM update → Respond 200"
}

Пошаговая настройка webhook ЮKassa, n8n и CRM

  1. В ЮKassa укажите HTTPS URL webhook n8n для события payment.succeeded.
  2. При создании платежа сохраняйте order_id или deal_id в metadata.
  3. В n8n импортируйте workflow JSON и настройте Postgres/CRM credentials.
  4. Создайте таблицу идемпотентности с уникальным ключом idempotency_key.
  5. Настройте обновление CRM: статус “Оплачено”, сумма, payment_id, комментарий и чек/receipt link.
  6. Отправьте один payload дважды и убедитесь, что второе событие не меняет сделку повторно.
Карточка сделки CRM после оплаты ЮKassa через n8n
Идеальный результат: сделка отмечена как оплаченная один раз, payment_id сохранён в отдельном поле.

Тесты перед production и проверка API ЮKassa

Проверяйте четыре типа сценариев: успешный платеж, повтор successful webhook, отменённый платеж и webhook без metadata. Для каждого случая должно быть понятно, обновляет ли workflow CRM или безопасно игнорирует событие.

curl -X POST "https://YOUR-N8N-DOMAIN/webhook/yookassa-payment-to-crm" \
  -H "Content-Type: application/json" \
  --data @yookassa-payment-to-crm-payload.json
  • Повторный payment_id должен получить статус duplicate/skipped.
  • payment.canceled не должен переводить сделку в “Оплачено”.
  • Webhook без deal_id/order_id должен создать alert, а не искать сделку по сумме.
  • Ошибка CRM после записи idempotency должна иметь понятный retry/runbook.

Production-риски платежных интеграций

  • Нет durable idempotency. Memory cache не спасёт после рестарта n8n.
  • CRM обновляется до проверки статуса. Так можно закрыть заказ по неподтверждённому событию.
  • Повтор webhook вызывает повторную отгрузку. Бизнес-действие должно быть отделено от технического ответа.
  • Секреты ЮKassa в execution data. Не логируйте auth headers и полный ответ API.
  • Не учитываются возвраты. Для refund нужен отдельный сценарий, а не удаление payment record.

Полезные ссылки и смежные workflow

Официальные документы: входящие уведомления ЮKassa и справочник API ЮKassa. Внутри Nodbot смотрите Webhook idempotency to Postgres, Retry и DLQ для HTTP Request, Postgres node и HTTP Request node.

Критерии готовности

  1. Каждый payment_id обрабатывается один раз.
  2. CRM обновляется только для payment.succeeded и paid=true.
  3. order_id/deal_id приходит из metadata, а не угадывается по сумме.
  4. Есть журнал платежей, retry-правило и alert на ошибки CRM/API.
  5. Повторный webhook не отправляет второе письмо, чек или доступ.

Нужно подключить оплату без финансовых дублей?

Nodbot настроит ЮKassa → n8n → CRM с idempotency, журналом платежей, проверкой статусов и безопасным обновлением сделок.

Обсудить платежную интеграцию