Kommo + Adyen: enterprise-платежи из воронки продаж с автоматическим учётом

Kommo + Adyen: enterprise-платежи из воронки продаж с автоматическим учётом

Adyen — enterprise payment platform которую используют Uber, Spotify, McDonald’s, Booking.com: единый gateway для онлайн-платежей, in-store (POS), in-app и marketplace payouts. Ключевые преимущества перед Stripe или Recurly: глобальное acquiring (прямые соглашения с платёжными системами без посредников), встроенная сверка через Adyen Data для финансовых отчётов, marketplace split payments. Для B2B компаний с enterprise-клиентами Adyen часто является стандартом — не потому что проще, а потому что контракт требует. Без интеграции с Kommo Won: платёжная ссылка создаётся вручную, менеджер не видит статус оплаты в CRM.

Adyen vs Stripe vs Mollie для enterprise B2B

ПараметрAdyenStripeMollie
Модель acquiringПрямая (own acquiring)Через партнёровЧерез партнёров
Interchange++ pricingДаНетНет
Marketplace paymentsНативноStripe ConnectНет
In-store (POS)Да (Adyen Terminal)Stripe TerminalНет
Минимум оборот~$1M/годНетНет
Сверка/отчётностьAdyen Data (встроенная)Через дашбордЧерез дашборд
EU-ориентацияДа (Amsterdam)Да (Dublin)Да (Netherlands)

Adyen выбирают компании с оборотом от $1M/год где важна прямая межбанковская комиссия (interchange++) и единая платформа для всех каналов продаж.

Архитектура: Kommo + Adyen

Kommo Won  ->  Python handler  ->  Adyen Payment Links API  ->  ссылка в Note
Adyen Webhook  ->  Python handler  ->  Kommo Note / Deal Stage

Adyen не имеет коннектора для Kommo. Интеграция через Adyen API (Management API + Checkout API) и webhooks.

Аутентификация: Adyen API Key

Adyen использует заголовок X-API-Key. API Key создаётся в Adyen Customer Area -> Developers -> API credentials -> Create new credential.

import requests
import hmac, hashlib, base64

ADYEN_API_KEY    = "your_api_key"
ADYEN_MERCHANT   = "YourMerchantAccount"
ADYEN_BASE       = "https://checkout-test.adyen.com/checkout/v71"
# Production: https://checkout-live.adyen.com/checkout/v71
ADYEN_HEADERS    = {
    "X-API-Key":    ADYEN_API_KEY,
    "Content-Type": "application/json",
}
ADYEN_HMAC_KEY   = "your_hmac_key_hex"  # из Adyen Customer Area -> Webhooks

Создание платёжной ссылки при Won

Adyen Payment Links позволяет создать ссылку на hosted checkout без PCI-ответственности на вашей стороне:

from datetime import datetime, timezone, timedelta

def create_adyen_payment_link(amount_cents: int, currency: str,
                               description: str, reference: str,
                               email: str = "") -> dict:
    # reference - уникальный идентификатор, используем Kommo deal ID
    expires_at = (
        datetime.now(timezone.utc) + timedelta(days=7)
    ).strftime("%Y-%m-%dT%H:%M:%S+00:00")

    payload = {
        "merchantAccount": ADYEN_MERCHANT,
        "amount": {
            "currency": currency,
            "value":    amount_cents,
        },
        "reference":   reference,
        "description": description,
        "expiresAt":   expires_at,
        "returnUrl":   "https://yoursite.com/payment-complete",
    }
    if email:
        payload["shopperEmail"] = email

    resp = requests.post(
        f"{ADYEN_BASE}/paymentLinks",
        headers=ADYEN_HEADERS,
        json=payload,
    )
    resp.raise_for_status()
    return resp.json()

def on_kommo_deal_won(lead: dict, contact: dict):
    price       = int((lead.get("price") or 0) * 100)  # cents
    currency    = get_custom_field(lead, CURRENCY_FIELD_ID) or "EUR"
    reference   = f"kommo-{lead['id']}"
    description = lead.get("name", "")
    email       = get_contact_email(contact)

    link_data  = create_adyen_payment_link(
        amount_cents=price,
        currency=currency,
        description=description,
        reference=reference,
        email=email,
    )
    link_url   = link_data.get("url", "")
    link_id    = link_data.get("id", "")

    save_to_kommo_deal(lead["id"], {
        "adyen_payment_link_id": link_id,
        "adyen_reference":       reference,
    })
    create_kommo_note(
        lead["id"],
        f"Adyen: платёжная ссылка создана -> {link_url} (истекает через 7 дней)",
    )

Webhook: Adyen -> Kommo

Adyen webhooks подписываются через HMAC-SHA256. Настройка: Customer Area -> Developers -> Webhooks -> Standard webhook.

def verify_adyen_hmac(payload: dict, hmac_key_hex: str,
                       received_hmac: str) -> bool:
    # Adyen HMAC verification
    fields = [
        "pspReference", "originalReference", "merchantAccountCode",
        "merchantReference", "value", "currency", "eventCode", "success",
    ]
    hmac_key  = bytes.fromhex(hmac_key_hex)
    data      = ":".join(str(payload.get(f, "")) for f in fields)
    computed  = base64.b64encode(
        hmac.new(hmac_key, data.encode("utf-8"), hashlib.sha256).digest()
    ).decode()
    return hmac.compare_digest(computed, received_hmac)

@app.route("/webhooks/adyen", methods=["POST"])
def adyen_webhook():
    body = request.json
    for item in body.get("notificationItems", []):
        notification = item.get("NotificationRequestItem", {})
        received_hmac = notification.get("additionalData", {}).get("hmacSignature", "")

        if not verify_adyen_hmac(notification, ADYEN_HMAC_KEY, received_hmac):
            return "[accepted]", 401

        event_code = notification.get("eventCode", "")
        reference  = notification.get("merchantReference", "")
        success    = notification.get("success") == "true"

        lead_id = find_kommo_deal_by_custom_field("adyen_reference", reference)
        if not lead_id:
            continue

        if event_code == "AUTHORISATION" and success:
            amount    = notification.get("amount", {})
            value_str = f"{amount.get('value', 0) / 100:.2f} {amount.get('currency', '')}"
            create_kommo_note(lead_id,
                f"Adyen: платёж авторизован - {value_str}")

        elif event_code == "CAPTURE" and success:
            amount    = notification.get("amount", {})
            value_str = f"{amount.get('value', 0) / 100:.2f} {amount.get('currency', '')}"
            create_kommo_note(lead_id,
                f"Adyen: платёж захвачен (captured) - {value_str}")
            move_kommo_deal_to_stage(lead_id, PAID_STAGE_ID)

        elif event_code == "REFUND" and success:
            create_kommo_note(lead_id, "Adyen: возврат платежа выполнен")

        elif event_code == "CHARGEBACK":
            create_kommo_note(lead_id, "Adyen: chargeback - требуется реакция")
            create_kommo_task(lead_id, "Adyen: обработать chargeback")

    return "[accepted]", 200

Важно: Adyen требует ответ [accepted] (строка) с кодом 200, иначе повторяет webhook до 10 раз.

Marketplace split payments

Если у вас marketplace-модель (вы продаёте от имени нескольких вендоров), Adyen поддерживает автоматическое разделение платежа через splits объект:

def create_split_payment_link(amount_cents: int, currency: str,
                               reference: str, splits: list) -> dict:
    payload = {
        "merchantAccount": ADYEN_MERCHANT,
        "amount":  {"currency": currency, "value": amount_cents},
        "reference": reference,
        "splits":    splits,
    }
    resp = requests.post(
        f"{ADYEN_BASE}/paymentLinks",
        headers=ADYEN_HEADERS,
        json=payload,
    )
    resp.raise_for_status()
    return resp.json()

# splits пример: 90% вендору, 10% комиссия маркетплейса
SPLITS_EXAMPLE = [
    {"type": "MarketPlace", "amount": {"value": 9000}, "account": "vendor_account_code"},
    {"type": "Commission",  "amount": {"value": 1000}},
]

Реальный кейс

B2B SaaS-маркетплейс (EU, 200+ enterprise клиентов, Kommo + Adyen):

  • До: Won -> финансовый отдел вручную создавал payment request в Adyen Customer Area, отправлял ссылку по email. 1–2 рабочих дня задержки. Менеджер не видел статус оплаты без захода в Adyen.
  • После: Won -> Python webhook -> Adyen Payment Link за 2 секунды -> ссылка в Note. CAPTURE event -> Note «оплачено» + смена этапа на «Paid». Менеджер видит весь цикл в Kommo без входа в Adyen.
  • Дополнительно: CHARGEBACK event -> Note + Task -> менеджер реагирует в день события. До интеграции chargeback-уведомления приходили только на email финансового директора.

Для кого актуально

  • Enterprise B2B с Adyen как корпоративным стандартом (контракт требует конкретный gateway)
  • Marketplace-компании где нужен автоматический split между продавцами и платформой
  • Компании с multi-currency продажами в EU/US/APAC — Adyen поддерживает 150+ валют нативно
  • Команды где менеджеры продаж должны видеть статус оплаты в CRM без доступа к Adyen Customer Area

Часто задаваемые вопросы

Adyen тестовый vs production — как переключать?

Adyen предоставляет тестовую среду (Customer Area Test) и production (Customer Area Live). URL отличаются: checkout-test.adyen.com vs checkout-live.adyen.com. API Keys разные для test и live. В коде: использовать переменную среды ADYEN_ENV = "test" | "live" для переключения base URL и API key.

Payment Link создаётся с expiresAt параметром (ISO 8601). После истечения — ссылка недействительна. Для повторного запроса платежа нужно создать новый Link через POST /paymentLinks. Статус существующего Link: GET /paymentLinks/{id} — поля status: active, expired, completed. При expired -> автоматически создавать новый через webhook или cron.

Как Adyen обрабатывает EU Strong Customer Authentication (SCA)?

Adyen нативно поддерживает 3DS2 для SCA. Payment Links включают 3DS challenge автоматически если банк клиента его требует. Дополнительной настройки не нужно — Adyen определяет необходимость challenge через AI-модель (exemptions: transaction risk analysis). Для B2B платежей с корпоративными картами SCA часто исключается через TRA-exemption.

Adyen webhook vs Adyen Reports — что использовать для сверки?

Webhooks: real-time события (AUTHORISATION, CAPTURE, REFUND). Adyen Reports: batch-файлы (CSV) доступные ежедневно через SFTP или Adyen Reporting API для бухгалтерской сверки. Для Kommo интеграции — webhooks. Для ERP/бухгалтерии — Reports API. Оба можно использовать параллельно.

Итого

  • Auth: заголовок X-API-Key, не Bearer token
  • Payment Link: POST /checkout/v71/paymentLinks, reference = Kommo deal ID
  • Webhook: ответ [accepted] (строка), HMAC-SHA256 верификация обязательна
  • Ключевые события: CAPTURE (деньги получены), CHARGEBACK (задача менеджеру), REFUND
  • Adyen split payments для marketplace: splits объект в payload

Если вы используете Adyen и Kommo и хотите видеть статус платежей в карточке сделки — опишите вашу платёжную модель (одиночные платежи или marketplace). Exceltic.dev настроит двустороннюю интеграцию с HMAC-верификацией.

Ещё статьи

Все →