Kommo + Mollie: платёжные ссылки и подписки из воронки продаж

Kommo + Mollie: платёжные ссылки и подписки из воронки продаж

Mollie — европейский платёжный шлюз с поддержкой iDEAL, SEPA Direct Debit, Bancontact, Klarna и всех основных карточных сетей. В отличие от Stripe, Mollie изначально ориентирован на EU-рынок и не требует отдельной настройки для каждого европейского метода оплаты. Без интеграции с Kommo менеджер после Won вручную открывает Mollie, создаёт платёж и отправляет ссылку клиенту. С интеграцией Won автоматически генерирует payment link с нужной суммой и методами оплаты, а событие payment.paid мгновенно переводит сделку на следующий этап.

Почему Mollie для EU-команд

Mollie поддерживает 25+ методов оплаты — большинство из них без дополнительных заявок:
iDEAL (Нидерланды, 60%+ онлайн-платежей в стране)
SEPA Direct Debit (для B2B-подписок в еврозоне)
Bancontact (Бельгия), Sofort (Германия/Австрия), Przelewy24 (Польша)
Klarna (рассрочка), Apple Pay, Google Pay

Для команды с клиентами в разных EU-странах Mollie означает один API вместо отдельных интеграций с локальными шлюзами. В сравнении с платёжными ссылками через Stripe — Mollie выигрывает по охвату европейских методов без дополнительных форм одобрения.

Что синхронизируется

Kommo -> Mollie:
— Won -> создать Mollie Payment Link с суммой из сделки
— Won -> создать Mollie Customer + Subscription (для recurring)
— Смена суммы сделки -> обновить черновик платежа

Mollie -> Kommo:
payment.paid -> Note: «Mollie: платёж получен» + смена этапа
payment.failed -> Note + задача менеджеру: «Платёж не прошёл»
payment.expired -> Note: «Ссылка истекла без оплаты»
subscription.charged_back -> Note + задача: «Чарджбэк — проверить»

Архитектура

Kommo Webhook: сделка перешла в Won
  ↓ Backend
  1. GET /api/v4/leads/{id} + contacts
     -> email, имя, сумма, описание
  2. Mollie API: POST /v2/payments
     -> amount, description, redirectUrl, webhookUrl
     -> method: ["ideal","creditcard","sepadirectdebit"] или null (выбор клиента)
  3. Kommo: PATCH /leads/{id}
     -> кастомное поле mollie_payment_id = payment.id
     -> кастомное поле mollie_checkout_url = payment._links.checkout.href
  4. Kommo: POST /leads/{id}/notes
     -> «Mollie: ссылка для оплаты отправлена»
  5. (опционально) Отправить checkout URL клиенту через WhatsApp/email

Mollie Webhook: payment.paid
  ↓ Backend
  1. GET /v2/payments/{id} -> верификация статуса
  2. Найти сделку по mollie_payment_id
  3. Kommo: PATCH /leads/{id} -> смена этапа «Оплата получена»
  4. Kommo: POST /leads/{id}/notes -> «Mollie: оплата подтверждена, метод: {method}»

Mollie API: ключевые запросы

Base URL: https://api.mollie.com/v2.
Аутентификация: Bearer token — Authorization: Bearer {api_key}.
Тестовые ключи начинаются на test_, production — на live_.

Создать платёж:

import requests

MOLLIE_API_KEY = "live_your_api_key"
MOLLIE_BASE_URL = "https://api.mollie.com/v2"

headers = {
    "Authorization": f"Bearer {MOLLIE_API_KEY}",
    "Content-Type": "application/json"
}

def create_payment(amount_eur: float, description: str,
                   redirect_url: str, webhook_url: str,
                   customer_email: str = None) -> dict:
    payload = {
        "amount": {
            "currency": "EUR",
            "value": f"{amount_eur:.2f}"  # Mollie требует строку "99.00"
        },
        "description": description,
        "redirectUrl": redirect_url,
        "webhookUrl": webhook_url,
        "locale": "nl_NL",   # или "en_US", "de_DE" и т.д.
    }
    if customer_email:
        payload["metadata"] = {"customer_email": customer_email}

    resp = requests.post(f"{MOLLIE_BASE_URL}/payments", headers=headers, json=payload)
    resp.raise_for_status()
    return resp.json()

def on_deal_won(lead: dict, contact: dict):
    amount = lead.get("price", 0) / 100  # Kommo хранит в копейках/центах
    email = get_contact_email(contact)
    deal_id = lead["id"]

    payment = create_payment(
        amount_eur=float(amount),
        description=f"Оплата сделки #{deal_id} - {lead.get('name', '')}",
        redirect_url=f"https://yoursite.com/payment/success?deal={deal_id}",
        webhook_url=f"https://your-backend.com/webhooks/mollie",
        customer_email=email
    )

    checkout_url = payment["_links"]["checkout"]["href"]
    payment_id = payment["id"]

    update_kommo_deal(deal_id, {
        "mollie_payment_id": payment_id,
        "mollie_checkout_url": checkout_url
    })
    create_kommo_note(deal_id, f"Mollie: ссылка для оплаты создана\n{checkout_url}")

Обработка Mollie Webhook:

Mollie не подписывает webhook HMAC — вместо этого всегда делайте GET /v2/payments/{id} для проверки статуса. Не доверяйте status из тела webhook.

from flask import Flask, request

app = Flask(__name__)

@app.route("/webhooks/mollie", methods=["POST"])
def mollie_webhook():
    payment_id = request.form.get("id")  # Mollie присылает form-encoded, не JSON
    if not payment_id:
        return "", 200

    # Верификация: всегда запрашиваем статус через API
    resp = requests.get(
        f"{MOLLIE_BASE_URL}/payments/{payment_id}",
        headers=headers
    )
    if not resp.ok:
        return "", 200

    payment = resp.json()
    status = payment.get("status")
    method = payment.get("method", "unknown")

    deal_id = find_deal_by_field("mollie_payment_id", payment_id)
    if not deal_id:
        return "", 200

    if status == "paid":
        update_kommo_deal(deal_id, {"stage_id": STAGE_PAYMENT_RECEIVED})
        create_kommo_note(deal_id,
            f"Mollie: платёж получен (метод: {method}, статус: paid)")

    elif status == "failed":
        create_kommo_note(deal_id, "Mollie: платёж не прошёл")
        create_kommo_task(deal_id, "Уточнить способ оплаты - Mollie зафиксировал ошибку")

    elif status == "expired":
        create_kommo_note(deal_id, "Mollie: ссылка для оплаты истекла без оплаты")
        create_kommo_task(deal_id, "Отправить новую ссылку - предыдущая истекла")

    return "", 200

Подписки (для recurring-платежей):

def create_mollie_subscription(customer_id: str, amount_eur: float,
                                interval: str = "1 month") -> dict:
    resp = requests.post(
        f"{MOLLIE_BASE_URL}/customers/{customer_id}/subscriptions",
        headers=headers,
        json={
            "amount": {"currency": "EUR", "value": f"{amount_eur:.2f}"},
            "interval": interval,  # "1 month", "1 week", "1 year"
            "description": "Ежемесячная подписка",
            "webhookUrl": "https://your-backend.com/webhooks/mollie",
        }
    )
    resp.raise_for_status()
    return resp.json()

Важно: Mollie webhook присылает данные как application/x-www-form-urlencoded, а не JSON. Используйте request.form.get("id"), а не request.json.

Европейские методы оплаты: что указывать в method

Если передать "method": null — Mollie покажет клиенту все доступные методы для его страны. Для конкретного рынка:

# Нидерланды - iDEAL
payload["method"] = "ideal"

# Германия - приоритет SEPA + карты
payload["method"] = ["sepadirectdebit", "creditcard"]

# Бельгия
payload["method"] = ["bancontact", "creditcard"]

# Международный B2B
payload["method"] = ["creditcard", "sepadirectdebit", "paypal"]

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

SaaS (Нидерланды, B2B, Kommo + Mollie, 30–40 новых клиентов в месяц):

  • До: менеджер после Won вручную создавал платёж в Mollie и отправлял ссылку в email. Задержка 1–4 часа. Клиенты из Нидерландов хотели iDEAL, из Германии — SEPA, из Бельгии — Bancontact. Три разных сценария — три ручных шага.
  • После: Won -> ссылка с мультиметодом за 5 секунд. Клиент видит свой предпочтительный метод автоматически по геолокации. payment.paid -> Note + смена этапа без вмешательства менеджера.
  • Дополнительно: истечение ссылки через 48 часов без оплаты -> автозадача менеджеру -> повторная отправка. Конверсия незакрытых платежей выросла на 22%.

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

  • EU-компании с клиентами в Нидерландах, Германии, Бельгии, Польше — там Mollie покрывает локальные методы лучше Stripe
  • SaaS с recurring subscription через SEPA Direct Debit
  • B2B с крупными счетами — Mollie поддерживает платежи до €100k без отдельного одобрения
  • Команды без Merchant of Record — в отличие от Paddle, Mollie не берёт на себя налоговую ответственность

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

Mollie webhook — нужно ли верифицировать подпись?

Нет — у Mollie нет HMAC-подписи webhook. Вместо этого обязательно делайте GET /v2/payments/{id} для проверки реального статуса. Не обрабатывайте платёж только на основе данных из webhook-тела — это уязвимость.

Mollie поддерживает тестовые платежи без реальных денег?

Да. Тестовые API-ключи (test_...) позволяют создавать платежи с симуляцией всех статусов: paid, failed, expired, cancelled. В Mollie Dashboard -> Test mode — полная среда для разработки.

Чем Mollie отличается от Stripe для EU?

Mollie зарегистрирован и регулируется в Нидерландах (DNB), не требует отдельного одобрения для iDEAL/Bancontact/SEPA. Stripe поддерживает те же методы, но некоторые (SEPA Direct Debit) требуют подачи заявки. Для NL/BE-рынка Mollie — меньше бюрократии. Для глобального рынка и Stripe Connect — Stripe.

Как передать данные клиента в Mollie для SEPA?

Для SEPA Direct Debit сначала создайте Mollie Customer (POST /customers с name и email), затем используйте customerId при создании платежа. Клиент пройдёт mandating flow один раз, дальнейшие списания — без его участия.

Итого

  • Mollie API: Bearer token, https://api.mollie.com/v2
  • Создать платёж: POST /paymentsamount.value должен быть строкой "99.00"
  • Webhook: приходит application/x-www-form-urlencoded с id — всегда верифицировать через GET /payments/{id}
  • Нет HMAC-подписи — верификация через API-запрос, не через заголовок
  • Хранить mollie_payment_id в кастомном поле Kommo для обратного поиска
  • Метод null = клиент выбирает сам из доступных в его стране

Если вы работаете с EU-клиентами и хотите автоматизировать выставление счетов через Mollie из воронки Kommo — опишите используемые методы оплаты. Exceltic.dev настроит маппинг и webhook-обработчик.

Ещё статьи

Все →