Интеграция Kommo с Xero позволяет автоматически создавать инвойс в бухгалтерской системе в момент, когда менеджер переводит сделку в статус «Выигрыш» - без переключения между интерфейсами, без ручного копирования данных. Xero API v2 поддерживает полную двустороннюю синхронизацию: создание контакта, выставление счёта, отслеживание оплаты.
Это типичная задача для кастомных интеграций Kommo: нативный виджет Xero в маркетплейсе Kommo существует, но закрывает лишь базовый сценарий и не поддерживает кастомные поля, налоговые коды или мультивалютность.
Почему нативный виджет Xero не работает в боевых условиях
Xero имеет официальный виджет в маркетплейсе Kommo. На практике он ломается по нескольким причинам.
Проблема 1 - маппинг полей. Виджет берёт только стандартные поля сделки: сумму и название. Кастомные поля (налоговый регион, скидка, номер PO-заказа) он игнорирует. В результате бухгалтерия получает инвойс с неверными данными и правит вручную.
Проблема 2 - дублирование контактов. Виджет создаёт нового Contact в Xero при каждой сделке. Если компания уже существует в Xero с другим написанием названия, появляется дубль. Через три месяца работы бухгалтерия видит сотни дублей.
Проблема 3 - налоговые коды. Xero требует явного указания TaxType для каждой строки инвойса. Виджет подставляет дефолтный код, который не соответствует реальной ставке для конкретного клиента или страны.
Проблема 4 - мультивалютность. Если у вас клиенты в EUR и GBP, виджет не умеет передавать валюту из поля сделки - счёт всегда создаётся в валюте по умолчанию.
Кастомная интеграция через Xero API v2 закрывает все четыре проблемы.
Архитектура интеграции
Стек: Python 3.11, Kommo Webhook, Xero API v2 (OAuth 2.0 с PKCE), PostgreSQL для хранения маппинга ContactID.
Схема работы:
- Kommo отправляет webhook при смене статуса сделки на «Выигрыш».
- Микросервис получает событие, извлекает поля сделки и контакта.
- Поиск или создание Contact в Xero с проверкой на дубль по email.
- Создание Invoice с правильными строками, TaxType и валютой.
- Запись XeroInvoiceID обратно в кастомное поле сделки Kommo.
- При получении payment webhook из Xero - обновление статуса сделки.
Аутентификация Xero: OAuth 2.0 с refresh token. Xero отзывает access token каждые 30 минут. Refresh token живёт 60 дней при активном использовании. Токены хранятся в зашифрованном виде в PostgreSQL, обновляются автоматически.
import requests
from datetime import datetime, timezone
import json
XERO_TOKEN_URL = "https://identity.xero.com/connect/token"
XERO_API_BASE = "https://api.xero.com/api.xro/2.0"
def refresh_xero_token(refresh_token: str, client_id: str, client_secret: str) -> dict:
resp = requests.post(XERO_TOKEN_URL, data={
"grant_type": "refresh_token",
"refresh_token": refresh_token,
"client_id": client_id,
"client_secret": client_secret,
})
resp.raise_for_status()
return resp.json()
def find_or_create_contact(tenant_id: str, access_token: str,
name: str, email: str) -> str:
headers = {
"Authorization": f"Bearer {access_token}",
"Xero-Tenant-Id": tenant_id,
"Accept": "application/json",
}
# Поиск по email
search = requests.get(
f"{XERO_API_BASE}/Contacts",
headers=headers,
params={"where": f'EmailAddress="{email}"'}
)
contacts = search.json().get("Contacts", [])
if contacts:
return contacts[0]["ContactID"]
# Создание нового контакта
payload = {"Name": name, "EmailAddress": email}
create = requests.post(
f"{XERO_API_BASE}/Contacts",
headers={**headers, "Content-Type": "application/json"},
json={"Contacts": [payload]}
)
create.raise_for_status()
return create.json()["Contacts"][0]["ContactID"]
def create_invoice(tenant_id: str, access_token: str,
contact_id: str, deal: dict) -> str:
headers = {
"Authorization": f"Bearer {access_token}",
"Xero-Tenant-Id": tenant_id,
"Content-Type": "application/json",
"Accept": "application/json",
}
invoice = {
"Type": "ACCREC",
"Contact": {"ContactID": contact_id},
"Date": datetime.now(timezone.utc).strftime("%Y-%m-%d"),
"DueDate": deal.get("due_date", ""),
"CurrencyCode": deal.get("currency", "USD"),
"Reference": deal.get("custom_po_number", ""),
"LineItems": [{
"Description": deal["name"],
"Quantity": 1.0,
"UnitAmount": float(deal["price"]),
"TaxType": deal.get("tax_type", "NONE"),
"AccountCode": deal.get("account_code", "200"),
}],
"Status": "AUTHORISED",
}
resp = requests.post(
f"{XERO_API_BASE}/Invoices",
headers=headers,
json={"Invoices": [invoice]}
)
resp.raise_for_status()
return resp.json()["Invoices"][0]["InvoiceID"]
Пошаговая реализация
Шаг 1 - регистрация Xero app. Заходите на developer.xero.com, создаёте приложение типа «Web app». Получаете Client ID и Client Secret. Указываете Redirect URI вашего микросервиса.
Шаг 2 - OAuth flow. Первичная авторизация выполняется один раз через браузер. Пользователь разрешает доступ к организации Xero. Получаете access + refresh токены, сохраняете в БД.
Шаг 3 - Kommo webhook. В настройках Kommo создаёте webhook на событие «Сделка: изменение этапа». URL - ваш микросервис. Фильтр: только переход в статус «Выигрыш».
Шаг 4 - маппинг полей. В конфигурационном файле описываете, какое кастомное поле Kommo соответствует какому параметру Xero:
{
"tax_type_field_id": "12345",
"currency_field_id": "67890",
"po_number_field_id": "11111",
"xero_invoice_id_field": "22222"
}
Шаг 5 - обратная синхронизация. Настраиваете Xero webhook на событие INVOICE с status PAID. При получении - ищете сделку по InvoiceID, меняете тег или кастомное поле в Kommo.
Шаг 6 - мониторинг. Все ошибки создания инвойса логируются и отправляются в Slack-канал бухгалтерии. Сделка при этом не блокируется - ошибка фиксируется в заметке к сделке Kommo.
Реальный кейс: SaaS-компания в Нидерландах
B2B SaaS-компания с командой 22 человека, 40-60 новых сделок в месяц. Клиенты в 12 странах с разными налоговыми ставками (NL VAT 21%, DE VAT 19%, UK VAT 20%, US без VAT).
До интеграции: финансовый менеджер тратил 3-4 часа в неделю на ручное создание инвойсов в Xero из Kommo. Ошибки в TaxType появлялись в 15% счетов - их обнаруживали при квартальной сверке.
После интеграции: инвойс создаётся автоматически через 30 секунд после закрытия сделки. Правильный налоговый код подставляется из кастомного поля «Страна клиента» через таблицу маппинга. За 6 месяцев работы - 0 ошибок TaxType.
Цифры: 3,5 часа/нед -> 0, экономия ~180 часов в год. Стоимость проекта окупилась за 8 недель.
Для кого подходит
Интеграция Kommo с Xero актуальна для компаний, у которых:
- Более 20 новых сделок в месяц и бухгалтерия на Xero
- Клиенты в нескольких странах с разными налоговыми ставками
- Кастомные поля в сделках (PO-номер, проект, подразделение)
- Требование двусторонней синхронизации: статус оплаты из Xero обратно в CRM
Xero популярен в Великобритании, Австралии, Новой Зеландии и растущем числе EU-компаний. Если ваша бухгалтерия работает в Xero - настройка воронки Kommo с автоматическим триггером на «Выигрыш» закрывает весь цикл от лида до оплаченного счёта.
Часто задаваемые вопросы
Работает ли интеграция с мультивалютностью Xero?
Да. Xero API принимает поле CurrencyCode в каждом инвойсе. В нашей интеграции валюта берётся из кастомного поля сделки Kommo или из поля «Сумма» с выбором валюты. Для мультивалютности нужно убедиться, что в Xero включена функция Multiple Currencies (доступна на планах Established и выше). Конвертация по курсу на дату инвойса происходит автоматически на стороне Xero.
Что происходит, если Xero недоступен в момент закрытия сделки?
Микросервис использует exponential backoff: первая повторная попытка через 30 секунд, затем через 2 минуты, затем через 10 минут. Если после 5 попыток инвойс не создан, в карточку сделки Kommo добавляется заметка с ошибкой и уведомление в Slack. Сделка при этом остаётся в статусе «Выигрыш» - процесс не блокируется.
Как избежать дублирования контактов в Xero?
Поиск контакта выполняется по email-адресу из карточки Kommo. Если контакт найден - инвойс создаётся для него. Если не найден - создаётся новый с проверкой уникальности названия компании. ContactID сохраняется в кастомном поле контакта Kommo - при следующих сделках от того же клиента поиск идёт сначала по этому полю.
Можно ли автоматически менять статус сделки при оплате?
Да. Xero отправляет webhook при переходе инвойса в статус PAID. Сервис получает событие, находит сделку по сохранённому XeroInvoiceID и может добавить тег «Оплачено» или переместить сделку в финальный этап. Конкретная логика настраивается под ваш процесс.
Поддерживается ли создание повторяющихся инвойсов для подписок?
Xero поддерживает RepeatingInvoices через API. Для SaaS с помесячными или годовыми подписками можно настроить создание не разового счёта, а шаблона с расписанием. Но для подписок мы обычно рекомендуем интеграцию с Chargebee или Recurly - они более гибко управляют billing-циклами.
Следующий шаг
Если вы работаете в Kommo, а бухгалтерия ведётся в Xero - опишите задачу команде Exceltic.dev. Разберём вашу структуру полей, налоговую логику и предложим архитектуру интеграции. Обычно такой проект занимает 2-3 недели и не требует изменений в текущих процессах бухгалтерии.