Главная › Блог › Интеграция в CRM
Интеграция Kaspi Pay в Bitrix24, Kommo и amoCRM
Готовые сценарии: выставить счёт из карточки сделки одним кликом, автоматически перевести сделку в «Успешно» по вебхуку оплаты, отправить клиенту ссылку на чек. С примерами кода для каждой CRM и общей логикой, которую можно адаптировать под Pipedrive, Salesforce или Zoho.
Общая схема
Независимо от конкретной CRM, картинка одна:
- Менеджер открывает карточку сделки и нажимает кнопку «Выставить счёт».
- CRM (или ваш middleware) собирает корзину из товаров сделки и зовёт POST /v1/invoice/create в PayProverkaBot.
- В ответ приходит
paymentId, который сохраняется в поле сделки. - Клиент получает push в Kaspi.kz, открывает и оплачивает.
- PayProverkaBot шлёт вебхук
payment.successна ваш URL. - Обработчик находит сделку по
paymentId, переводит её в статус «Успешно», прикладываетreceiptUrl, ставит задачу на отгрузку.
Главный нюанс: первая часть (CRM → API) — синхронный HTTP, простой. Вторая часть (API → CRM) — асинхронный вебхук, требует идемпотентного приёмника с проверкой подписи.
Bitrix24
Шаг 1. Действие «Выставить счёт» в роботе
В Bitrix24 у сделок есть роботы и триггеры. Самый простой путь — настроить «Робот: исходящий вебхук» при перемещении сделки в стадию «Готов к оплате». Робот шлёт POST на ваш middleware, который и зовёт PayProverkaBot.
Зачем middleware между Bitrix24 и нами: чтобы спрятать X-API-Key и
привести данные в нужный формат. Робот Bitrix24 не позволяет переименовать
поля так, как требует наша схема.
# middleware.py — небольшой FastAPI-приёмник между Bitrix24 и PayProverkaBot
import os, requests
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()
KASPI_KEY = os.environ["KASPI_API_KEY"]
BX_TOKEN = os.environ["BX_INCOMING_TOKEN"] # секрет, который Bitrix шлёт в POST
@app.post("/bitrix/issue-invoice")
async def issue_invoice(request: Request):
payload = await request.json()
if payload.get("auth", {}).get("application_token") != BX_TOKEN:
raise HTTPException(401)
deal_id = payload["data"]["FIELDS"]["ID"]
phone = payload["data"]["FIELDS"]["PHONE"]
products = payload["data"]["PRODUCTS"] # [{NAME, PRICE, QUANTITY}, ...]
items = [
{"name": p["NAME"], "price": float(p["PRICE"]), "count": int(p["QUANTITY"])}
for p in products
]
r = requests.post(
"https://pay.proverkacheka.kz/api/v1/invoice/create",
headers={"X-API-Key": KASPI_KEY, "Content-Type": "application/json"},
json={"phoneNumber": phone, "comment": f"Сделка #{deal_id}", "items": items},
timeout=15,
)
r.raise_for_status()
payment_id = r.json()["paymentId"]
# Сохраняем paymentId в кастомное поле сделки UF_PAYMENT_ID
bitrix_update_deal(deal_id, {"UF_PAYMENT_ID": payment_id})
return {"ok": True, "payment_id": payment_id}
Шаг 2. Кастомное поле UF_PAYMENT_ID
В справочнике сделок добавьте пользовательское поле UF_PAYMENT_ID
типа «Строка». Туда middleware пишет идентификатор счёта. По нему мы потом
найдём сделку при приходе вебхука.
Шаг 3. Приём вебхука и перевод сделки
import hmac, hashlib
@app.post("/kaspi/webhook")
async def kaspi_webhook(request: Request):
body = await request.body()
sig = request.headers.get("X-Webhook-Signature", "")
expected = "sha256=" + hmac.new(
os.environ["KASPI_WEBHOOK_SECRET"].encode(), body, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, sig):
raise HTTPException(401)
event = await request.json()
if event["event"] != "payment.success":
return {"ok": True} # игнорируем остальные в этом коннекторе
deal = bitrix_find_deal_by_field("UF_PAYMENT_ID", event["paymentId"])
if not deal:
return {"ok": True, "note": "no deal found"}
bitrix_update_deal(deal["ID"], {
"STAGE_ID": "WON",
"UF_RECEIPT_URL": event.get("receiptUrl") or "",
"UF_PAID_AT": event["timestamp"],
})
bitrix_create_task(
deal["ASSIGNED_BY_ID"],
title=f"Отгрузить заказ по сделке {deal['ID']}",
deadline_hours=24,
)
return {"ok": True}
STAGE_ID для «Успешно» в Bitrix24 — обычно
WON, но в кастомных воронках это C5:WON или схожий
префикс. Проверьте через crm.dealcategory.stage.list.
Kommo (бывший amoCRM)
Шаг 1. Виджет или Salesbot
В Kommo два пути:
- Salesbot — встроенный визуальный конструктор сценариев. Он умеет звать HTTP-запросы, поэтому может вызывать PayProverkaBot напрямую.
- Виджет — JavaScript-расширение карточки сделки, кнопка прямо в интерфейсе. Сложнее, но даёт UX уровня нативной CRM.
Для большинства команд достаточно Salesbot. Сценарий: «когда сделка перешла в этап «Готов к оплате», спросить менеджера ‘Выставить счёт?’, при ‘Да’ вызвать наш middleware».
# Тот же middleware из примера Bitrix24, но другой эндпоинт
@app.post("/kommo/issue-invoice")
async def kommo_issue(request: Request):
payload = await request.json()
# Salesbot шлёт {"lead_id": ..., "items": [...]} согласно вашему JSON-конструктору
items = payload["items"]
phone = payload["phone"]
lead_id = payload["lead_id"]
r = requests.post(
"https://pay.proverkacheka.kz/api/v1/invoice/create",
headers={"X-API-Key": KASPI_KEY, "Content-Type": "application/json"},
json={"phoneNumber": phone, "comment": f"Сделка #{lead_id}", "items": items},
timeout=15,
)
r.raise_for_status()
payment_id = r.json()["paymentId"]
# Записываем в кастомное поле сделки Kommo
kommo_patch_lead(lead_id, custom_fields={"payment_id": payment_id})
return {"ok": True, "payment_id": payment_id}
Шаг 2. Кастомное поле сделки
В Kommo: Настройки → Поля → Сделки → Добавить. Тип «Текст», название
«Kaspi Payment ID». ID поля сохраните — оно нужно в API-вызовах
(kommo_patch_lead).
Шаг 3. Вебхук
Логика идентична Bitrix24-варианту: получили payment.success,
проверили подпись, нашли сделку по custom-полю, перевели в «Успешно»,
создали задачу. Меняется только клиент API (Kommo вместо Bitrix24).
Kommo считает «Успешно» статусом 142 (закрытое и реализованное)
— этот ID одинаков у всех аккаунтов.
amoCRM
Технически amoCRM и Kommo — одна платформа (Kommo — это amoCRM, переименованный
в Казахстане/СНГ после ребрендинга 2022 года). API одинаковый, шаги совпадают
с предыдущим разделом. Единственное отличие — домен авторизации:
your-account.amocrm.ru вместо your-account.kommo.com.
Если ваш бизнес работает с обеими версиями, средний слой можно держать общим: middleware принимает один и тот же формат полей, а наружу ходит по разным OAuth-токенам.
Что важно для всех трёх
Идемпотентность вебхука
PayProverkaBot может ретраить payment.success 2–10 раз
(см. политику ретраев).
Если обработчик каждый раз ставит новую задачу или меняет статус — менеджеры
утонут в дублях. Минимальная защита:
def handle_event(event):
key = f'{event["paymentId"]}:{event["event"]}'
if redis.set(f"kaspi:processed:{key}", "1", nx=True, ex=86400 * 7) is None:
# уже обрабатывали в последние 7 дней — выходим
return
# дальше — основная логика
Карта полей: счёт ↔ сделка
Минимально нужно три кастомных поля в каждой CRM:
| Поле в CRM | Что хранит | Когда заполняется |
|---|---|---|
payment_id | paymentId из Kaspi | В момент создания счёта |
receipt_url | Ссылка на фискальный чек | При payment.success |
paid_at | Дата/время оплаты | При payment.success |
Обратная связь клиенту
После успешной оплаты полезно отправить клиенту ссылку на чек
receiptUrl — это снимает 80% «а где мой чек?» в поддержку.
Все три CRM позволяют послать SMS / email-шаблон по триггеру.
Если же бизнес наоборот — принимает чеки от клиентов как доказательство оплаты на другой счёт — у нашей экосистемы есть парный сервис ProverkaCheka.kz, который валидирует фискальные чеки Kaspi через API. Те же команды, те же ИИН/БИН, но в обратную сторону.
Типичные ошибки
| Симптом | Причина | Что делать |
|---|---|---|
400 invalid_phone при создании счёта |
В CRM-сделке телефон записан как +7 (701) ... с лишними символами |
Сервис нормализует, но если в строке меньше 10 цифр — упадёт. Очищайте на стороне middleware: оставьте только цифры, проверьте длину. |
412 kaspi_session_required |
Кто-то залогинился в Kaspi Pay на телефоне — сессия сервиса выгнана | Откройте бот → /login и пройдите SMS. После этого вебхуки и API возобновятся. |
| Вебхук приходит, но сделка не находится | paymentId не записался в CRM (например, middleware ответил 5xx) |
В middleware: сначала запись в CRM, потом возврат успешного ответа. Если запись упала — Kaspi-счёт уже создан, но «осиротевший». |
| Менеджер получает несколько одинаковых задач | Нет защиты от ретраев вебхука | Идемпотентность по (paymentId, event), см. выше. |
| Сделка переведена в «Успешно», но клиент потом запросил возврат | Не подписаны на payment.refunded |
Подпишитесь и в обработчике переводите сделку обратно в специальный этап «Возврат». |
Чек-лист продакшна
- Middleware развёрнут на HTTPS, не на ipv4-доменах второго уровня (CRM не пустит).
X-API-Keyхранится в env-переменной, не в коде CRM-виджета.- Шаринг секрета между подпиской и обработчиком — через env или секрет-менеджер.
- В CRM есть три кастомных поля:
payment_id,receipt_url,paid_at. - Обработчик идемпотентен по
(paymentId, event). - Подписка на
payment.success+payment.refundedминимум; для UX — такжеpayment.failedиpayment.expired, чтобы не оставлять «висящие» сделки. - Логи middleware пишут
X-Webhook-Attempt— поможет понять, повторная ли это доставка. - Тестовая сделка с реальным телефоном перед запуском в продакшн.
Что почитать дальше
- Вебхуки или поллинг — когда вам вообще не нужен middleware и хватит поллинга.
- HMAC-SHA256 без багов — как корректно проверить подпись вебхука.
- Документация API — формат запроса
POST /v1/invoice/create. - Если вы интегрируете приём чеков (а не выставление счетов) — например, валидируете чеки клиентов в той же CRM — смотрите «Виджет Kommo для проверки чеков» на ProverkaCheka.kz.