Kommo + QuickBooks: автоматические инвойсы из воронки продаж
QuickBooks Online — доминирующая бухгалтерская платформа в США, Канаде и ряде EU-рынков. REST API QuickBooks (Intuit API v3) поддерживает создание клиентов, инвойсов, получение статусов оплат и webhook-уведомления. Связка с Kommo закрывает стандартный паттерн: сделка Won -> Customer в QuickBooks -> Invoice -> после оплаты CRM обновляется, без ручного копирования данных.
QuickBooks vs Zoho Books vs Wave: когда QuickBooks
| Параметр | QuickBooks Online | Zoho Books | Wave |
|---|---|---|---|
| API | REST v3 | REST | GraphQL |
| Доминирующий рынок | США, Канада | Глобально | США, Канада |
| Цена (Simple Start) | $35/мес | Бесплатно до $50k | Бесплатно (базовый) |
| Мультивалютность | Plus и выше | Все тарифы | Частично |
| Встроенный эквайринг | Да (QuickBooks Payments) | Да | США/Канада |
| Экосистема | 750+ интеграций | Zoho 50+ продуктов | Независимый |
QuickBooks выбирают компании, работающие с US/CA клиентами или аудиторами — это де-факто стандарт в Северной Америке. Сравнение с Zoho Books и Wave — в отдельных статьях.
Что синхронизируется
Kommo -> QuickBooks:
— Контакт сделки -> Customer в QB (дедупликация по email через query API)
— Название и сумма сделки -> строки Invoice
— Срок оплаты из кастомного поля -> Terms (Net 15, Net 30 и т.д.)
— ID инвойса и ссылка -> кастомные поля в карточке Kommo
QuickBooks -> Kommo:
— Webhook Payment с txnStatus = Completed -> поле «Оплачено» в сделке
— Перевод сделки на этап «Оплата получена»
— Дата и сумма оплаты -> Note на сделке
Архитектура
Kommo Webhook: сделка Won
↓ Backend
1. GET /api/v4/leads/{id} + contacts
-> имя, email, сумма, срок оплаты
2. QB API: POST /v3/company/{realmId}/query
-> SELECT * FROM Customer WHERE PrimaryEmailAddr = '{email}'
-> найден: использовать Id
-> не найден: POST /v3/company/{realmId}/customer
3. QB API: POST /v3/company/{realmId}/invoice
-> CustomerRef + Line items + DueDate
-> получить Id, DocNumber, InvoiceLink
4. QB API: POST /v3/company/{realmId}/invoice/{id}/send
-> отправить инвойс клиенту по email
5. Kommo: PATCH /leads/{id}
-> обновить поля qb_invoice_id, invoice_url
QuickBooks Webhook: Payment (txnStatus = Completed)
↓ Backend
1. GET /v3/company/{realmId}/payment/{paymentId}
-> найти LinkedTxn с Invoice Id
2. GET хранилища: найти kommo_deal_id по qb_invoice_id
3. Kommo: PATCH /leads/{deal_id}
-> этап -> «Оплачено», поле payment_date
QuickBooks REST API: ключевые запросы
QuickBooks API использует OAuth 2.0 (Authorization Code Flow). Все запросы идут на https://quickbooks.api.intuit.com/v3/company/{realmId}/. Обязательный параметр: ?minorversion=75.
Поиск или создание Customer:
import requests
QB_BASE = f'https://quickbooks.api.intuit.com/v3/company/{REALM_ID}'
HEADERS = {
'Authorization': f'Bearer {access_token}',
'Accept': 'application/json',
'Content-Type': 'application/json'
}
PARAMS = {'minorversion': '75'}
def find_or_create_customer(email: str, display_name: str, company: str) -> str:
# Поиск по email
query = f"SELECT * FROM Customer WHERE PrimaryEmailAddr = '{email}'"
resp = requests.post(
f'{QB_BASE}/query',
params={**PARAMS, 'query': query},
headers=HEADERS
)
customers = resp.json()['QueryResponse'].get('Customer', [])
if customers:
return customers[0]['Id']
# Создание нового клиента
payload = {
'DisplayName': display_name,
'CompanyName': company,
'PrimaryEmailAddr': {'Address': email}
}
resp = requests.post(
f'{QB_BASE}/customer',
params=PARAMS,
json=payload,
headers=HEADERS
)
return resp.json()['Customer']['Id']
Создание инвойса:
from datetime import date, timedelta
def create_invoice(customer_id: str, deal_name: str, amount: float,
due_days: int = 30) -> dict:
today = date.today().isoformat()
due_date = (date.today() + timedelta(days=due_days)).isoformat()
payload = {
'CustomerRef': {'value': customer_id},
'DueDate': due_date,
'TxnDate': today,
'Line': [
{
'Amount': amount,
'DetailType': 'SalesItemLineDetail',
'SalesItemLineDetail': {
'ItemRef': {'value': '1', 'name': 'Services'}, # item из QB
'Qty': 1,
'UnitPrice': amount
},
'Description': deal_name
}
]
}
resp = requests.post(
f'{QB_BASE}/invoice',
params=PARAMS,
json=payload,
headers=HEADERS
)
invoice = resp.json()['Invoice']
return {'id': invoice['Id'], 'doc_number': invoice['DocNumber']}
Отправка инвойса клиенту:
def send_invoice(invoice_id: str, client_email: str):
requests.post(
f'{QB_BASE}/invoice/{invoice_id}/send',
params={**PARAMS, 'sendTo': client_email},
headers=HEADERS
)
# QuickBooks отправляет стандартное письмо с ссылкой на оплату
Обновление OAuth-токена
Access token QuickBooks истекает через 60 минут. Refresh token — через 100 дней. Важно реализовать автообновление:
def refresh_access_token(refresh_token: str) -> dict:
import base64
credentials = base64.b64encode(
f'{CLIENT_ID}:{CLIENT_SECRET}'.encode()
).decode()
resp = requests.post(
'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer',
headers={
'Authorization': f'Basic {credentials}',
'Content-Type': 'application/x-www-form-urlencoded'
},
data={
'grant_type': 'refresh_token',
'refresh_token': refresh_token
}
)
return resp.json() # access_token + refresh_token (rotating)
QuickBooks использует rotating refresh tokens — при каждом обновлении выдаётся новый refresh token, старый инвалидируется. Это важно хранить в БД, а не в конфиге.
Webhook на оплату
QuickBooks Webhooks настраиваются в Intuit Developer Portal. Payload — минималистичный: только entity type, ID и операция. Полные данные нужно запрашивать отдельно:
from flask import Flask, request
app = Flask(__name__)
@app.route('/webhooks/quickbooks', methods=['POST'])
def qb_webhook():
payload = request.json
for notification in payload.get('eventNotifications', []):
realm_id = notification['realmId']
for entity in notification.get('dataChangeEvent', {}).get('entities', []):
if entity['name'] == 'Payment' and entity['operation'] == 'Create':
payment_id = entity['id']
# Запрос деталей платежа
payment = get_payment_details(realm_id, payment_id)
# Обновить Kommo по invoice_id из payment
sync_payment_to_kommo(payment)
return '', 200
Реальный кейс
Консалтинговая компания (US-рынок, 20–30 проектов в квартал, клиенты в США и Канаде):
- До: менеджер после Won переключался в QuickBooks, вручную создавал клиента, формировал инвойс, отправлял письмо. Среднее время от Won до выставления счёта — 3–4 дня.
- После: Won в Kommo -> через 5 минут клиент получает инвойс из QuickBooks -> статус оплаты автоматически обновляется в Kommo при получении платежа.
- Дополнительно: бухгалтер перестал запрашивать у менеджеров данные для выставления счётов — всё приходит из CRM автоматически.
Похожий паттерн, но через Zoho Books — там REST API с региональным OAuth, но без rotating refresh tokens. Для US-рынка QuickBooks — стандартный выбор.
Для кого актуально
- Клиенты преимущественно в США, Канаде — QuickBooks де-факто стандарт
- 10+ инвойсов в месяц, которые сейчас создаются вручную
- Цикл: Won -> инвойс -> оплата -> следующий этап воронки
- Бухгалтер и менеджер работают в разных системах, нужна синхронизация
- Используется QuickBooks Payments для онлайн-оплат
Часто задаваемые вопросы
QuickBooks OAuth — насколько сложно поддерживать?
Glавная сложность — rotating refresh tokens: при каждом обновлении токена нужно сохранить новый refresh token. Если пропустить это — при следующем обновлении будет использован старый инвалидированный токен и авторизация сломается. На практике: хранить токены в БД с timestamp, обновлять за 5 минут до истечения access token, логировать каждый refresh.
Какой Item использовать при создании инвойса?
Item (товар/услуга) в QuickBooks — обязательный объект в Line. Можно создать один универсальный Item «Consulting Services» через QB UI и использовать его ID для всех инвойсов из Kommo. Или создавать Item динамически через POST /item — но проще использовать фиксированный для интеграции.
Есть ли лимиты QuickBooks API?
Да. 500 запросов в минуту на реаl OAuth app. Для типового объёма Kommo (до 100 сделок в месяц) лимиты нерелевантны. Webhook payload минималистичен — за каждым уведомлением нужен дополнительный GET-запрос, что нужно учитывать при высоком объёме.
QuickBooks Sandbox — как тестировать?
Intuit предоставляет Sandbox-среду: sandbox-quickbooks.api.intuit.com. Sandbox-компания создаётся автоматически при регистрации в Intuit Developer Portal. Webhook-тестирование — через ngrok или аналоги (Intuit не может отправить webhook на localhost).
Нужен ли QuickBooks Plus для API?
API доступен на всех тарифах, включая Simple Start ($35/мес). Мультивалютность — только на Plus и выше. Для US-рынка без мультивалюты Simple Start достаточен.
Итого
- QuickBooks Online REST API v3: OAuth 2.0 с rotating refresh tokens,
realmIdв каждом запросе - Поиск Customer через query API, создание инвойса с Line items, отправка через
/sendendpoint - Webhook на Payment -> автообновление этапа в Kommo
- Rotating refresh tokens — ключевой архитектурный нюанс, требует хранения в БД
- Типовой срок разработки — 2–3 недели
Если вы работаете на QuickBooks и Kommo и хотите автоматизировать выставление инвойсов — опишите вашу структуру ценообразования и схему оплат. Exceltic.dev разберёт маппинг и предложит архитектуру.