---
title: "ЮKassa и n8n: платежи в CRM без дублей | Nodbot"
source_url: "https://nodbot.ru/workflows/yookassa-payment-to-crm/"
canonical_url: "https://nodbot.ru/workflows/yookassa-payment-to-crm/"
language: "ru"
content_type: "WorkflowTemplate"
section: "workflows"
generated_at: "2026-05-30"
word_count_source: 1127
---

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

## AI summary

Problem/Solution-мануал по обработке платежей ЮKassa через n8n: webhook payment.succeeded, подтверждение 200 OK, проверка статуса платежа, идемпотентность, обновление CRM и защита от повторных событий.

## Best used for

Полноценный Problem/Solution-мануал для внедрения в n8n: импортировать workflow JSON, настроить webhook/API, проверить дубли, выполнить production-тесты и передать решение команде.

## Table of contents

- Проблема: почему платежный webhook ЮKassa нельзя обрабатывать как обычную заявку
- Архитектура workflow ЮKassa → n8n → CRM
- Контракт входных данных payment.succeeded
- Проверка платежа и idempotency key (Code Node)
- Готовый workflow JSON: скачать и импортировать
- Пошаговая настройка webhook ЮKassa, n8n и CRM
- Тесты перед production и проверка API ЮKassa
- Production-риски платежных интеграций
- Полезные ссылки и смежные workflow
- Критерии готовности

## Key topics

- ЮKassa webhook
- payment.succeeded
- n8n CRM integration
- payment idempotency
- Postgres unique key
- CRM paid status
- workflow JSON

## Source outline

Интеграция ЮKassa и CRM через n8n: оплата, чек и идемпотентность ¶ Обновлено: 2026-05-30 Сохранить в мой план Открыть мой план Шаблон для внедрения Скачать workflow JSON Скачать test payload Скопировать curl Импортируйте JSON в n8n, замените CRM endpoint, Postgres credential и правило поиска сделки по order_id . Содержание Проблема: почему платежный webhook ЮKassa нельзя обрабатывать как обычную заявку Архитектура workflow ЮKassa → n8n → CRM Контракт входных данных payment.succeeded Проверка платежа и idempotency key (Code Node) Готовый workflow JSON: скачать и импортировать Пошаговая настройка webhook ЮKassa, n8n и CRM Тесты перед production и проверка API ЮKassa Production-риски платежных интеграций Полезные ссылки и смежные workflow Критерии готовности Проблема: платежные уведомления Ю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 стоит проверка статуса и idempotency key. Проблема: почему платежный webhook ЮKassa нельзя обрабатывать как обычную заявку ¶ Форма заявки может терпеть повтор, а платеж — нет. Для оплаты важно не только получить payment.succeeded , но и понять, какой заказ или сделку обновлять, что делать при повторном уведомлении и как доказать, что business action выполнен один раз. ЮKassa ожидает подтверждение получения webhook, но ответ 200 OK не должен означать “мы уже закрыли сделку”. Он должен означать “мы приняли событие и обработали его по безопасному правилу”. Поэтому workflow лучше строить вокруг идемпотентности и журнала платежей. Архитектура workflow ЮKassa → n8n → CRM ¶ Нода Роль Что проверить YooKassa Webhook Принимает notification HTTPS endpoint, секретный путь, корректный 200 ответ Normalize payment Проверяет event/status/paid payment.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. Скачать готовый workflow JSON Скачать тестовый payload { "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 ¶ В ЮKassa укажите HTTPS URL webhook n8n для события payment.succeeded . При создании платежа сохраняйте order_id или deal_id в metadata . В n8n импортируйте workflow JSON и настройте Postgres/CRM credentials. Создайте таблицу идемпотентности с уникальным ключом idempotency_key . Настройте обновление CRM: статус “Оплачено”, сумма, payment_id, комментарий и чек/receipt link. Отправьте один payload дважды и убедитесь, что второе событие не меняет сделку повторно. Идеальный результат: сделка отмечена как оплаченная один раз, 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 . Критерии готовности ¶ Каждый payment_id обрабатывается один раз. CRM обновляется только для payment.succeeded и paid=true . order_id / deal_id приходит из metadata, а не угадывается по сумме. Есть журнал платежей, retry-правило и alert на ошибки CRM/API. Повторный webhook не отправляет второе письмо, чек или доступ. Нужно подключить оплату без финансовых дублей? Nodbot настроит ЮKassa → n8n → CRM с idempotency, журналом платежей, проверкой статусов и безопасным обновлением сделок. Обсудить платежную интеграцию

## Test payload

```json
{
  "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
  }
}
```

## Key implementation snippet

```javascript
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
  }
}];
```

## Importable workflow structure

```json
{
  "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"
}
```

## Retrieval hints

- Использовать HTML как canonical source.
- Markdown удобен для LLM-ответов, извлечения workflow-контракта, кода и чеклистов.
- Для ссылок пользователю отдавать canonical URL.
