Refund coordinator
Что делает: ЮKassa webhook о refund-request’е → находит сделку в amoCRM по metadata → просит approval у owner’а (если > 10 000 ₽) → после approval’а обновляет сделку + создаёт задачу в 1С для бухгалтера оформить корректировку.
Production-safe: ни один refund не уходит в бухгалтерию без подтверждения человеком.
Prerequisites
Section titled “Prerequisites”Secrets
Section titled “Secrets”yookassa-shop-idyookassa-secret-keyamocrm-token-mainamocrm-subdomainonec-urlonec-usernameonec-passwordWorkflow DAG
Section titled “Workflow DAG”┌────────────────────┐│ yookassa.webhook. │ event: refund.succeeded│ trigger │└────────┬───────────┘ │ {object: {id, amount, metadata.order_id, ...}} ↓┌────────────────────┐│ amocrm.crm.list │ entity=leads,│ │ filter by custom_field (order_id)└────────┬───────────┘ │ {results: [{id: 12345, name: "...", ...}], _total_items: 1} ↓┌────────────────────┐│ control.switch │ amount > 10000?└────┬───────────┬───┘ │ yes │ no ↓ ↓┌────────────┐ ││ approval. │ ││ request │ │└────┬───────┘ │ │ approved │ └─────┬─────┘ ↓┌────────────────────┐│ amocrm.lead.update │ status_id = "refunded"└────────┬───────────┘ ↓┌────────────────────┐│ onec.odata.create │ Document_КорректировкаРеализации└────────────────────┘Node-by-node
Section titled “Node-by-node”1. yookassa.webhook.trigger
Section titled “1. yookassa.webhook.trigger”events: - refund.succeededtoken: "{{secrets.yookassa-webhook-token}}"2. amocrm.crm.list
Section titled “2. amocrm.crm.list”entity: leadsfilter: custom_fields_values: - field_code: "ORDER_ID" values: [{ value: "{{trigger.object.metadata.order_id}}" }]limit: 1Если нет сделки (list.results.length == 0) — fall’aем с alert’ом в Telegram / логом. Здесь для краткости не показываю.
3. control.switch на trigger.object.amount.value > 10000
Section titled “3. control.switch на trigger.object.amount.value > 10000”Две ветки: approve-required (большой refund) и auto-approved
(мелкий).
4a. approval.request (только для > 10 000 ₽)
Section titled “4a. approval.request (только для > 10 000 ₽)”title: "Refund {{trigger.object.amount.value}} ₽ для сделки {{list.results.0.name}}"body: | Заказ: {{trigger.object.metadata.order_id}} Клиент: {{list.results.0.name}} Сумма refund'а: {{trigger.object.amount.value}} ₽ ЮKassa ID: {{trigger.object.id}}
amoCRM: https://{{secrets.amocrm-subdomain}}.amocrm.ru/leads/detail/{{list.results.0.id}}assignedTo: kind: role role: ownerttl: 172800 # 48 часовДве выходные ветки: approved → шаг 5, rejected → terminal с
telegram.message.send уведомлением что refund отклонён.
5. amocrm.lead.update
Section titled “5. amocrm.lead.update”id: "{{list.results.0.id}}"status_id: 23459 # ваш ID status'а «Возврат»custom_fields_values: - field_code: "REFUND_AMOUNT" values: [{ value: "{{trigger.object.amount.value}}" }] - field_code: "REFUND_AT" values: [{ value: "{{run.startedAt}}" }]6. onec.odata.create
Section titled “6. onec.odata.create”entity: Document_КорректировкаРеализацииdata: Дата: "{{run.startedAt}}" Номер: "REF-{{trigger.object.id}}" Основание_Type: "StandardODATA.Document_РеализацияТоваровУслуг" Основание_Key: "{{list.results.0.custom_fields_values.onec_realization_guid}}" СуммаДокумента: "{{trigger.object.amount.value}}" Комментарий: "Возврат по refund ID: {{trigger.object.id}}"Если 1С автоматически проводит документ (зависит от настроек) — бухгалтер получит notification в 1С’овском messenger’е.
Runtime behavior
Section titled “Runtime behavior”Refund < 10 000 ₽ (auto)
Section titled “Refund < 10 000 ₽ (auto)”webhook → list → switch(auto) → update → create ~3 сек. Сделка в
amoCRM закрыта, корректировка в 1С создана, всё без вмешательства
людей.
Refund > 10 000 ₽ (approval-required)
Section titled “Refund > 10 000 ₽ (approval-required)”webhook → list → switch(approval) → approval.request — дальше run
в статусе awaiting_approval на /approvals.
Owner заходит в /approvals, видит карточку, жмёт Approve →
amocrm.lead.update и onec.odata.create отрабатывают.
Если owner 48 часов не заметил — approval auto-expires, run падает
в failed с reason TIMEOUT. Мы не коммитим рефанд в 1С без ручного
подтверждения — safety-by-default.
Variations
Section titled “Variations”Custom-threshold для approval’а
Section titled “Custom-threshold для approval’а”Замените 10000 на expression с конфигурационной таблицей:
control.switch condition: "trigger.object.amount.value > data_table.rows.get('config', 'refund_threshold')"Тогда менять порог можно без редактирования workflow’а — через data-tables UI.
Alert в Telegram в параллель с approval’ом
Section titled “Alert в Telegram в параллель с approval’ом”После approval.request добавьте telegram.message.send в
параллельную ветку:
approval.request (...) ─┬─→ (approved) → amocrm.lead.update ... └─→ telegram.message.send (нотификация что ждёт approval'а, ссылка на /approvals)Разные 1С-entities для разных продуктов
Section titled “Разные 1С-entities для разных продуктов”Используйте control.switch по product-type перед onec.odata.create,
или централизуйте routing в data-table’е (product_id → onec_entity).
Troubleshooting
Section titled “Troubleshooting”list.results.length === 0
Section titled “list.results.length === 0”Сделка в amoCRM не нашлась. Причины:
order_idв ЮKassa metadata не совпадает сORDER_IDcustom_field’ом в amoCRM (проверьте в обеих системах)- Сделка была удалена в amoCRM после оплаты
- Сделка в другой pipeline’е;
amocrm.crm.listне фильтрует по pipeline — уточните filter’ом если нужно
Добавьте control.switch на list.results.length и fallback-ветку,
где шлёте alert в Telegram: «не смогли найти сделку для refund’а
{id}, разберитесь вручную».
Approval не приходит owner’у
Section titled “Approval не приходит owner’у”v1 не шлёт push-нотификаций. Владельцу нужно видеть /approvals
страницу. Workaround — добавить telegram.message.send параллельно
approval.request (см. Variations).
1С падает с 500
Section titled “1С падает с 500”Типичный trigger: required reference-field не заполнен (Организация _Key, Контрагент_Key). В 1С каждая конфигурация имеет свои
required-поля — смотрите $metadata или тело ошибки.
Refund приходит дважды (ЮKassa ретраит)
Section titled “Refund приходит дважды (ЮKassa ретраит)”OPORA дедуп’ит по trigger.object.id — повторный run не создаётся,
получите 200 OK + header X-Opora-Idempotent: true. Никаких ручных
действий не нужно.