Lemon Squeezy - Merchant of Record платформа для SaaS-продуктов: принимает платежи, обрабатывает НДС, возвраты, подписки. Kommo - CRM, где ведётся воронка продаж. Без интеграции момент оплаты существует в Lemon Squeezy, но не в Kommo: менеджер не видит, заплатил ли клиент, и сделка зависает в ручном режиме. Интеграция замыкает этот разрыв: событие order_created от Lemon Squeezy автоматически закрывает сделку в Kommo и создаёт запись об оплате.
Lemon Squeezy набирает популярность у indie SaaS и небольших B2B продуктов как альтернатива Paddle и Stripe. В отличие от Stripe, Lemon Squeezy берёт на себя налоговую ответственность (Merchant of Record) - компания не занимается VAT, sales tax и соблюдением регуляций самостоятельно. Это критично для команд, продающих в США, ЕС и других регионах одновременно.
Ключевая техническая возможность: Lemon Squeezy позволяет передавать произвольные данные через checkout_data.custom при создании checkout-сессии. Эти данные возвращаются в каждом webhook-событии в поле meta.custom_data. Именно через этот механизм передаётся kommo_lead_id - идентификатор сделки в Kommo.
Почему нативная интеграция не существует
На момент написания этой статьи Lemon Squeezy не имеет готового коннектора с Kommo. Zapier имеет триггеры для Lemon Squeezy, но не умеет работать с кастомными полями Kommo API и не поддерживает webhook-верификацию. Единственный надёжный способ - прямая интеграция через API.
Merchant of Record - модель, при которой платёжная платформа (а не ваша компания) является продавцом перед законом: несёт ответственность за налоги, возвраты, соблюдение PCI DSS. Для SaaS компаний это означает отсутствие необходимости регистрировать VAT в каждой стране продаж.
Техническая архитектура
Kommo CRM
-> deal.status_changed (к стадии "Отправлен счёт")
-> POST /v1/checkouts {custom: {kommo_lead_id}}
<- checkout URL
-> Записать URL как поле сделки / отправить клиенту
Клиент
-> Открыть checkout URL, оплатить
Lemon Squeezy
-> POST /your-server/webhooks/lemon {event: order_created, meta.custom_data.kommo_lead_id}
Ваш сервер
-> Верифицировать X-Signature (HMAC-SHA256)
-> PUT /api/v4/leads/{kommo_lead_id} {status_id: 142} # Успешно реализовано
-> POST /api/v4/leads/{kommo_lead_id}/notes {text: "Оплата получена: $99"}
Реализация: создание checkout
При переходе сделки в нужную стадию формируем checkout-сессию в Lemon Squeezy:
import requests, os
LS_API_KEY = os.environ["LEMON_SQUEEZY_API_KEY"]
LS_STORE_ID = os.environ["LS_STORE_ID"]
LS_VARIANT_ID = os.environ["LS_VARIANT_ID"] # ID тарифного плана
def create_checkout(kommo_lead_id: str, client_email: str, client_name: str) -> str:
# Create Lemon Squeezy checkout and return URL.
r = requests.post(
"https://api.lemonsqueezy.com/v1/checkouts",
headers={
"Authorization": f"Bearer {LS_API_KEY}",
"Accept": "application/vnd.api+json",
"Content-Type": "application/vnd.api+json",
},
json={
"data": {
"type": "checkouts",
"attributes": {
"checkout_data": {
"email": client_email,
"name": client_name,
"custom": {
"kommo_lead_id": str(kommo_lead_id)
}
},
"expires_at": None, # без срока истечения
"preview": False,
},
"relationships": {
"store": {"data": {"type": "stores", "id": LS_STORE_ID}},
"variant": {"data": {"type": "variants", "id": LS_VARIANT_ID}},
}
}
},
timeout=10,
)
r.raise_for_status()
return r.json()["data"]["attributes"]["url"]
Полученный URL сохраняется в кастомное поле сделки в Kommo и/или отправляется клиенту через email.
Реализация: обработка webhook
import hmac, hashlib, os, time
from flask import Flask, request, jsonify
app = Flask(__name__)
KOMMO_DOMAIN = os.environ["KOMMO_DOMAIN"]
KOMMO_TOKEN = os.environ["KOMMO_TOKEN"]
LS_SECRET = os.environ["LS_WEBHOOK_SECRET"]
KOMMO_BASE = f"https://{KOMMO_DOMAIN}/api/v4"
KOMMO_HEADERS = {"Authorization": f"Bearer {KOMMO_TOKEN}"}
KOMMO_WON_STATUS = 142 # ID статуса "Успешно реализовано"
def verify_signature(raw_body: bytes, header: str) -> bool:
expected = hmac.new(LS_SECRET.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)
@app.route("/webhooks/lemon", methods=["POST"])
def lemon_webhook():
sig = request.headers.get("X-Signature", "")
if not verify_signature(request.data, sig):
return jsonify({"error": "invalid signature"}), 401
data = request.json
event = data.get("meta", {}).get("event_name", "")
custom = data.get("meta", {}).get("custom_data", {})
lead_id = custom.get("kommo_lead_id")
if not lead_id:
return jsonify({"status": "no_lead_id"}), 200
if event == "order_created":
attrs = data["data"]["attributes"]
status = attrs.get("status")
if status == "paid":
handle_payment(lead_id, attrs)
elif event == "subscription_cancelled":
handle_cancellation(lead_id, data["data"]["attributes"])
return jsonify({"status": "ok"}), 200
def handle_payment(lead_id: str, attrs: dict):
amount = attrs.get("total", 0) / 100 # cents -> dollars
product = attrs.get("first_order_item", {}).get("product_name", "")
# Закрыть сделку как выигранную
requests.patch(
f"{KOMMO_BASE}/leads",
headers=KOMMO_HEADERS,
json=[{"id": int(lead_id), "status_id": KOMMO_WON_STATUS}],
)
# Добавить заметку об оплате
requests.post(
f"{KOMMO_BASE}/leads/{lead_id}/notes",
headers=KOMMO_HEADERS,
json=[{
"note_type": "common",
"params": {"text": f"Lemon Squeezy: оплата ${amount:.2f} - {product}"}
}],
)
def handle_cancellation(lead_id: str, attrs: dict):
ends_at = attrs.get("ends_at", "")
requests.post(
f"{KOMMO_BASE}/leads/{lead_id}/tasks",
headers=KOMMO_HEADERS,
json=[{
"task_type_id": 1,
"text": f"Клиент отменил подписку. Истекает: {ends_at}. Выяснить причину.",
"complete_till": int(time.time()) + 86400, # завтра
"responsible_user_id": None,
}],
)
Обратный поток: self-serve signups
Если продукт работает по модели self-serve, клиент оплачивает напрямую через встроенный checkout на сайте (без участия менеджера). В этом случае интеграция работает в обратном направлении:
- Клиент оплачивает на сайте - в
checkout_data.customпередаётся источник (utm_source, plan name) - Webhook
order_created-> создать новую сделку в Kommo черезPOST /api/v4/leads - Создать контакт и привязать к сделке
- Назначить менеджера по round-robin или зоне ответственности
Это позволяет менеджерам видеть всех платящих пользователей в Kommo и инициировать upsell/cross-sell процессы.
Реальный кейс
B2B SaaS для управления командами: продажи ведутся через Kommo, оплата - через Lemon Squeezy. До интеграции: менеджеры проверяли LS вручную 2-3 раза в день и вручную обновляли статусы в CRM. Около 15% сделок зависали в промежуточном статусе дольше суток.
После интеграции:
- Статус сделки обновляется в течение 30 секунд после оплаты
- Команда CSM получает задачу при отмене подписки автоматически
- Среднее время от оплаты до онбординга сократилось с 6 часов до 45 минут
Для кого актуально
Компании, которые:
- Используют Kommo как CRM для B2B-продаж
- Принимают платежи через Lemon Squeezy (или рассматривают переход с Stripe на MOR-модель)
- Тратят время на ручную синхронизацию статусов между CRM и платёжной системой
Особенно актуально для продуктов с годовыми/квартальными подписками, где важно отслеживать продления и отмены в CRM-контексте.
Если вы уже работаете с кастомными интеграциями в Kommo CRM, добавление Lemon Squeezy займёт 1-2 рабочих дня на разработку и тестирование.
Часто задаваемые вопросы
Как Lemon Squeezy подтверждает webhook-запросы?
Каждый POST-запрос содержит заголовок X-Signature - HMAC-SHA256 подпись тела запроса с использованием вашего signing secret (задаётся в настройках webhook в LS Dashboard). Верифицируйте подпись до обработки любых данных. Signing secret отличается от API ключа.
Можно ли передать несколько параметров в custom_data?
Да. checkout_data.custom принимает произвольный JSON-объект. Можно передавать kommo_lead_id, utm_source, plan_name, manager_id и любые другие данные. Все они вернутся в meta.custom_data каждого webhook-события.
Что происходит при возврате платежа?
Lemon Squeezy отправляет событие order_refunded. В обработчике создайте задачу в Kommo для менеджера с информацией о возврате и переведите сделку в статус “требует внимания” или восстановите в воронку.
Поддерживает ли Lemon Squeezy тестовые webhooks?
Да. В LS Dashboard есть режим Test Mode - создайте тестовые заказы без реальных платежей. Webhooks отправляются с теми же заголовками, что и боевые. Signing secret в Test Mode такой же, как в Production.
Итог
Kommo + Lemon Squeezy интеграция решает задачу синхронизации статусов между CRM и платёжной системой:
- Создание checkout со встроенным
kommo_lead_idвcustom_data - Webhook-обработчик с HMAC-верификацией
X-Signature order_created+status: paid-> закрыть сделку, добавить заметкуsubscription_cancelled-> создать задачу CSM- Self-serve:
order_created-> создать новую сделку в Kommo
Если ваша команда тратит время на ручное отслеживание оплат между Lemon Squeezy и Kommo - опишите задачу команде Exceltic.dev. Реализуем интеграцию под ваш стек.