Kommo + Chargebee: автоматическое создание подписок из выигранных сделок

Kommo + Chargebee: автоматическое создание подписок из выигранных сделок

Chargebee — платформа управления подписками для SaaS с REST API на Basic Auth. Без интеграции с Kommo менеджер после Won вручную создаёт клиента в Chargebee, выбирает план, запускает подписку — и забывает обновить CRM. С интеграцией Won в Kommo запускает всю цепочку автоматически: Customer создаётся в Chargebee, подписка активируется, а статус оплаты и события отмены попадают обратно в карточку сделки.

Что даёт связка Kommo + Chargebee

Без интеграции:
— Won в Kommo -> менеджер вручную открывает Chargebee, создаёт клиента, настраивает план
— Среднее время от Won до активации подписки — 1–3 часа
— При отмене подписки в Chargebee Kommo не знает — сделка висит как Won без обновления
— Бухгалтер и менеджер работают в разных системах, нет единой картины

С интеграцией:
— Won -> Customer + Subscription в Chargebee через 3 минуты
payment_succeeded -> Note в Kommo: «Оплата получена, $X»
subscription_cancelled -> задача менеджеру + изменение поля в сделке
invoice_generated -> ссылка на инвойс в Kommo

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

Kommo -> Chargebee:
— Имя и email контакта -> Customer в Chargebee
— Тариф из кастомного поля -> Plan ID подписки
— Сумма сделки -> billing amount (если не стандартный тариф)
— ID сделки -> metadata клиента (для обратной трассировки)

Chargebee -> Kommo:
payment_succeeded -> Note с суммой и датой
invoice_generated -> Note со ссылкой на инвойс
subscription_cancelled -> кастомное поле subscription_status = cancelled + задача менеджеру
subscription_renewed -> Note «Подписка продлена»

Архитектура

Kommo Webhook: сделка перешла в Won
  ↓ Backend
  1. GET /api/v4/leads/{id} + contacts
     -> имя, email, тариф из кастомных полей
  2. Chargebee: POST /api/v2/customers
     -> first_name, last_name, email, cf_kommo_deal_id
  3. Chargebee: POST /api/v2/subscriptions
     -> customer_id + plan_id + billing_cycles
     -> получить subscription_id
  4. Kommo: PATCH /leads/{id}
     -> chargebee_customer_id, subscription_id, subscription_status = active

Chargebee Webhook: payment_succeeded
  ↓ Backend
  1. Из payload: customer_id, amount, invoice_id
  2. Найти deal_id по cf_kommo_deal_id в Customer
  3. Kommo: POST /leads/{deal_id}/notes
     -> «Оплата получена: ${amount}. Инвойс: {invoice_url}»

Chargebee Webhook: subscription_cancelled
  ↓ Backend
  1. Из payload: customer_id, cancellation_reason
  2. Найти deal_id
  3. Kommo: PATCH /leads/{deal_id}
     -> subscription_status = cancelled
  4. Kommo: POST /tasks
     -> задача менеджеру: «Подписка отменена: {reason}»

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

Base URL: https://{site}.chargebee.com/api/v2/. Аутентификация: Basic Auth, username = API_KEY, password — пусто.

Создать клиента:

import chargebee

chargebee.configure(api_key='your_api_key', site='your-site')

def create_customer(email: str, first_name: str, last_name: str,
                    deal_id: int) -> str:
    result = chargebee.Customer.create({
        'email': email,
        'first_name': first_name,
        'last_name': last_name,
        'cf_kommo_deal_id': str(deal_id)  # кастомное поле для трассировки
    })
    return result.customer.id

Создать подписку:

def create_subscription(customer_id: str, plan_id: str,
                        billing_cycles: int = None) -> dict:
    params = {
        'customer_id': customer_id,
        'subscription_items': [{
            'item_price_id': plan_id  # например 'pro-monthly-usd'
        }]
    }
    if billing_cycles:
        params['subscription_items'][0]['billing_cycles'] = billing_cycles

    result = chargebee.Subscription.create_with_items(params)
    sub = result.subscription
    return {
        'id': sub.id,
        'status': sub.status,
        'current_term_end': sub.current_term_end
    }

Обработка Chargebee webhook:

from flask import Flask, request

app = Flask(__name__)

@app.route('/webhooks/chargebee', methods=['POST'])
def chargebee_webhook():
    payload = request.json
    event_type = payload.get('event_type')
    content = payload.get('content', {})

    if event_type == 'payment_succeeded':
        transaction = content.get('transaction', {})
        customer_id = transaction.get('customer_id')
        amount = transaction.get('amount', 0) / 100  # центы -> доллары

        deal_id = get_deal_id_by_customer(customer_id)
        if deal_id:
            create_kommo_note(deal_id, f'Chargebee: оплата получена ${amount:.2f}')

    elif event_type == 'subscription_cancelled':
        subscription = content.get('subscription', {})
        customer_id = subscription.get('customer_id')
        reason = subscription.get('cancel_reason', 'не указана')

        deal_id = get_deal_id_by_customer(customer_id)
        if deal_id:
            update_kommo_deal(deal_id, {'subscription_status': 'cancelled'})
            create_kommo_task(deal_id, f'Подписка Chargebee отменена. Причина: {reason}')

    elif event_type == 'invoice_generated':
        invoice = content.get('invoice', {})
        customer_id = invoice.get('customer_id')
        invoice_url = invoice.get('invoice_url', '')
        total = invoice.get('total', 0) / 100

        deal_id = get_deal_id_by_customer(customer_id)
        if deal_id:
            create_kommo_note(deal_id, f'Инвойс Chargebee: ${total:.2f}. Ссылка: {invoice_url}')

    return '', 200

Идемпотентность: Chargebee может повторить webhook при ошибке. Сохраняйте event_id из payload и проверяйте перед обработкой — иначе Note создастся дважды.

Маппинг тарифов Kommo -> Chargebee

План Chargebee (plan_id) задаётся в виде {plan}-{period}-{currency}, например pro-monthly-usd. Связь с кастомным полем Kommo:

PLAN_MAP = {
    'Starter':    'starter-monthly-usd',
    'Pro':        'pro-monthly-usd',
    'Enterprise': 'enterprise-monthly-usd',
    'Pro Annual': 'pro-yearly-usd'
}

def get_plan_id(kommo_tariff_field: str) -> str:
    return PLAN_MAP.get(kommo_tariff_field, 'pro-monthly-usd')

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

B2B SaaS (EU-рынок, 40–60 новых клиентов в квартал, Kommo + Chargebee + Stripe):

  • До: менеджер после Won тратил 15–30 минут на создание клиента в Chargebee. Нередко ошибался в плане или пропускал billing_cycles — клиент получал бессрочную подписку.
  • После: Won -> подписка активна через 5 минут. Менеджер не заходит в Chargebee вообще. При первой оплате — автоматический Note в Kommo, при отмене — задача с причиной.
  • Дополнительно: Customer Success видит в карточке сделки всю историю платежей — без доступа к Chargebee.

Аналогичный паттерн для Stripe — там payment links, здесь полноценное управление подпиской через Chargebee.

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

  • SaaS-компании с Chargebee как платформой управления подписками
  • Нужно замкнуть Won в CRM на активацию подписки без ручного шага
  • Customer Success должен видеть статус подписки и историю платежей в Kommo
  • 20+ новых подписок в месяц — ручная работа становится нерентабельной

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

Chargebee API — нужен OAuth или достаточно API-ключа?

Для серверных интеграций — API Key через Basic Auth: ключ как username, пустой password. OAuth не нужен. Ключ создаётся в Chargebee: Settings -> Configure Chargebee -> API Keys.

Как настроить webhook в Chargebee?

Settings -> Configure Chargebee -> Webhooks -> Add Webhook URL. Выбрать события: payment_succeeded, subscription_cancelled, invoice_generated, subscription_renewed. Chargebee подписывает payload через HMAC-SHA256, заголовок Chargebee-Webhook-Signature для верификации.

Chargebee vs Stripe Billing для интеграции с Kommo?

Оба работают на API Key. Chargebee специализируется на управлении подписками: смена плана, pause, dunning management, revenue recognition. Stripe Billing — проще, меньше функций out-of-the-box. Если используете Chargebee — интеграция через Chargebee API. Если только Stripe — через Stripe Payment Links или Stripe Billing API.

Как хранить связь customer_id -> deal_id?

Два варианта: кастомное поле в Chargebee Customer (cf_kommo_deal_id) — доступно из webhook payload, не требует отдельного хранилища. Или таблица в БД {chargebee_customer_id -> kommo_deal_id}. Первый вариант проще, работает без дополнительного storage.

Что делать если клиент обновляет план?

Webhook subscription_changed содержит старый и новый plan_id. Обновить кастомное поле в Kommo и создать Note: «Тариф изменён: {old_plan} -> {new_plan}».

Итого

  • Chargebee REST API: Basic Auth (API key), Python SDK pip install chargebee
  • Создание Customer + Subscription при Won через chargebee.Customer.create и Subscription.create_with_items
  • Webhook события: payment_succeeded, subscription_cancelled, invoice_generated -> Notes и задачи в Kommo
  • Идемпотентность: сохранять event_id для защиты от дублей при retry
  • Типовой срок разработки — 1–2 недели

Если вы используете Chargebee и Kommo и хотите автоматизировать активацию подписок при закрытии сделок — опишите вашу структуру планов и схему биллинга. Exceltic.dev настроит маппинг и обработку событий оплаты.

Ещё статьи

Все →