Документация

Refund coordinator

Что делает: ЮKassa webhook о refund-request'е → находит сделку в amoCRM по metadata → просит approval у owner'а (если > 10 000 ₽) → после approval'а обновляет с

Что делает: ЮKassa webhook о refund-request'е → находит сделку в amoCRM по metadata → просит approval у owner'а (если > 10 000 ₽) → после approval'а обновляет сделку + создаёт задачу в 1С для бухгалтера оформить корректировку.

Production-safe: ни один refund не уходит в бухгалтерию без подтверждения человеком.

Prerequisites

Secrets

yookassa-shop-id
yookassa-secret-key
amocrm-token-main
amocrm-subdomain
onec-url
onec-username
onec-password

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

1. yookassa.webhook.trigger

events:
  - refund.succeeded
token: "{{secrets.yookassa-webhook-token}}"

2. amocrm.crm.list

entity: leads
filter:
  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

Две ветки: approve-required (большой refund) и auto-approved (мелкий).

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: owner
ttl: 172800  # 48 часов

Две выходные ветки: approved → шаг 5, rejected → terminal с telegram.message.send уведомлением что refund отклонён.

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

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

Refund < 10 000 ₽ (auto)

webhook → list → switch(auto) → update → create ~3 сек. Сделка в amoCRM закрыта, корректировка в 1С создана, всё без вмешательства людей.

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

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'ом

После approval.request добавьте telegram.message.send в параллельную ветку:

approval.request (...) ─┬─→ (approved) → amocrm.lead.update ...
                        └─→ telegram.message.send (нотификация что
                             ждёт approval'а, ссылка на /approvals)

Разные 1С-entities для разных продуктов

Используйте control.switch по product-type перед onec.odata.create, или централизуйте routing в data-table'е (product_id → onec_entity).

Troubleshooting

list.results.length === 0

Сделка в amoCRM не нашлась. Причины:

  • order_id в ЮKassa metadata не совпадает с ORDER_ID custom_field'ом в amoCRM (проверьте в обеих системах)
  • Сделка была удалена в amoCRM после оплаты
  • Сделка в другой pipeline'е; amocrm.crm.list не фильтрует по pipeline — уточните filter'ом если нужно

Добавьте control.switch на list.results.length и fallback-ветку, где шлёте alert в Telegram: «не смогли найти сделку для refund'а {id}, разберитесь вручную».

Approval не приходит owner'у

v1 не шлёт push-нотификаций. Владельцу нужно видеть /approvals страницу. Workaround — добавить telegram.message.send параллельно approval.request (см. Variations).

1С падает с 500

Типичный trigger: required reference-field не заполнен (Организация _Key, Контрагент_Key). В 1С каждая конфигурация имеет свои required-поля — смотрите $metadata или тело ошибки.

Refund приходит дважды (ЮKassa ретраит)

SUMMING дедуп'ит по trigger.object.id — повторный run не создаётся, получите 200 OK + header X-Opora-Idempotent: true. Никаких ручных действий не нужно.