Документация для разработчиков

Kalkhan API позволяет выполнять проверку лицензий, активацию и дилерскую интеграцию из ваших собственных систем. Этот документ описывает структуру запросов и ответов, способ аутентификации и коды ошибок. Обзор Kalkhan API — это ресурсно-ориентированный REST-интерфейс. Тела всех запросов и ответов передаются в формате JSON в кодировке UTF-8. Соединения обязательно шифруются по TLS 1.2 и выше; незашифрованные (HTTP) запросы не принимаются. Base URLhttps://api.kalkhan.com.tr/v1 ПротоколHTTPS / TLS 1.2+ (обязательно) Формат данныхJSON · Content-Type: application/json; charset=utf-8 КодировкаUTF-8 Формат датыISO 8601 (UTC), например 2027-07-23T00:00:00Z Ограничение частоты120 запросов в минуту (на каждый ключ) Адреса всех эндпоинтов используются вместе с base URL. Например, полный адрес эндпоинта проверки лицензии — https://api.kalkhan.com.tr/v1/licenses/verify. Установка и начало работы Если вы начинаете интеграцию с нуля, выполните шаги ниже по порядку. Весь процесс занимает в среднем 15 минут и не требует устанавливать на сервере какое-либо ПО Kalkhan — достаточно среды, способной отправлять HTTPS-запросы. Предварительные требования Подтверждённый дилерский аккаунт Kalkhan (заявка: форма обратной связи) Серверная среда с поддержкой TLS 1.2 или выше (PHP 7.4+, Node.js 16+, .NET 6+, Python 3.8+ и т. п.) Способ хранить API-ключ на стороне сервера (рекомендуются переменные окружения) Пошаговая настройка Войдите в дилерскую панель. Доступ к панели открывается после того, как аккаунт подтвердит администратор. Создайте API-ключ. В разделе «API-ключи» сначала выпустите тестовый ключ с префиксом klk_test_. Задайте переменные окружения. Добавьте ключ и base URL в конфигурацию приложения (пример ниже). Отправьте первый запрос. Чтобы проверить соединение, вызовите эндпоинт проверки доступности. Зарегистрируйте адрес вебхука. Чтобы получать события лицензий мгновенно, укажите в панели свой HTTPS-адрес для вебхуков. Переходите в продакшен. После тестов замените ключ на klk_live_; в коде меняется только ключ. 1) Конфигурация окружения # .env KALKHAN_API_URL=https://api.kalkhan.com.tr/v1 KALKHAN_API_KEY=klk_test_xxxxxxxxxxxxxxxx KALKHAN_WEBHOOK_SECRET=whsec_xxxxxxxxxxxx 2) Проверка соединения curl -s https://api.kalkhan.com.tr/v1/ping \ -H "Authorization: Bearer $KALKHAN_API_KEY" # Успешный ответ { "ok": true, "env": "test", "dealer": "BYI-1001" } Совет: лицензии, созданные в тестовой среде, не попадают в реальную тарификацию и могут быть удалены в любой момент. Перед запуском интеграции в продакшен прогоните тестовым ключом все сценарии: создание, проверку и отмену лицензии. Аутентификация Запросы к API авторизуются по схеме Bearer с использованием вашего API-ключа. Ключ нужно передавать в заголовке Authorization каждого запроса. Authorization: Bearer klk_live_xxx Content-Type: application/json; charset=utf-8 Accept: application/json Создать и отозвать API-ключ можно в разделе «API-ключи» вашей дилерской панели. Для тестовой среды используются ключи с префиксом klk_test_, для боевой — klk_live_. Важно о безопасности: API-ключ — это секретные учётные данные, храните его только на стороне сервера. Не встраивайте ключ в браузерный код, в пакет мобильного приложения или в публичный репозиторий. При малейшем подозрении на утечку немедленно отзовите ключ в панели и выпустите новый. Проверка лицензии Используется, чтобы узнать статус лицензионного ключа, его продукт и оставшийся срок. Лицензионные ключи имеют вид KLKN-XXXX-XXXX-XXXX-XXXX. МетодЭндпоинтПрава POST/licenses/verifyBearer API-ключ Запрос POST /v1/licenses/verify HTTP/1.1 Host: api.kalkhan.com.tr Authorization: Bearer klk_live_xxx Content-Type: application/json; charset=utf-8 { "key": "KLKN-4F2A-9C71-B3D8-A15E", "deviceId": "WIN-8f2c41ab9d", "productId": "kalkhan-av-windows" } Ответ (200 OK) { "key": "KLKN-4F2A-9C71-B3D8-A15E", "status": "active", "product": "Kalkhan Anti-Virüs (Windows)", "edition": "business", "seats": 25, "seatsUsed": 11, "expiresAt": "2027-03-14T23:59:59Z" } ПолеТипОписание statusstringactive, expired, suspended или cancelled productstringОтображаемое имя продукта, к которому относится лицензия editionstringhome, business или enterprise seatsintegerОбщее число устройств, покрываемых лицензией expiresAtstringДата окончания в формате ISO 8601 (UTC) Для несуществующего или недействительного ключа возвращается 404 not_found, для ключа с неверным форматом — 400 invalid_request. Создание лицензии (дилер) Используется авторизованными дилерами для выпуска новой лицензии на имя конечного пользователя. Этот эндпоинт доступен только API-ключам с дилерскими правами. МетодЭндпоинтПрава POST/licensesДилерский API-ключ Запрос POST /v1/licenses HTTP/1.1 Host: api.kalkhan.com.tr Authorization: Bearer klk_live_xxx Content-Type: application/json; charset=utf-8 { "productId": "kalkhan-av-windows", "edition": "business", "seats": 25, "customer": { "name": "Örnek Bilişim A.Ş.", "email": "muhasebe@ornekbilisim.com.tr", "taxId": "1234567890", "taxOffice": "Ümraniye", "address": "Mithatpaşa Cd. No:116, Ümraniye/İstanbul" } } Ответ (201 Created) { "key": "KLKN-7B10-CE45-2F93-D8A6", "status": "active", "product": "Kalkhan Anti-Virüs (Windows)", "edition": "business", "seats": 25, "amount": 34750.00, "currency": "TRY", "expiresAt": "2027-07-23T23:59:59Z" } При корпоративных продажах (когда edition равен business или enterprise) для выставления счёта обязательно передавать поля customer.taxId (налоговый номер), customer.taxOffice (налоговая инспекция) и customer.address. Если этих полей нет, запрос отклоняется с ошибкой 400 invalid_request. Для частных (home) лицензий налоговые поля необязательны. Повторные запросы по одному и тому же клиенту и продукту за короткий промежуток времени получают ответ 409 duplicate; чтобы исключить выпуск дублирующих лицензий, рекомендуем добавлять к запросу заголовок Idempotency-Key. Дилерский API Для просмотра собственного портфеля лицензий и сводки по продажам дилерам доступны два эндпоинта. МетодЭндпоинтОписание GET/dealer/licensesПостраничный список лицензий дилера GET/dealer/statsСводка по числу продаж, обороту и ближайшим продлениям Параметры постраничного вывода ПараметрПо умолчаниюОписание page1Номер отображаемой страницы (нумерация с 1) limit25Количество записей на страницу (не более 100) status—Необязательный фильтр: active, expired, cancelled Запрос и ответ GET /v1/dealer/licenses?page=2&limit=25&status=active HTTP/1.1 Host: api.kalkhan.com.tr Authorization: Bearer klk_live_xxx { "data": [ { "key": "KLKN-4F2A-9C71-B3D8-A15E", "status": "active", "product": "Kalkhan Anti-Virüs (Windows)", "edition": "business", "seats": 25, "customer": "Örnek Bilişim A.Ş.", "expiresAt": "2027-03-14T23:59:59Z" } ], "pagination": { "page": 2, "limit": 25, "total": 143, "totalPages": 6 } } Эндпоинт GET /dealer/stats возвращает сводку за период: общее число активных лицензий, количество продаж за месяц, суммарный оборот и число лицензий, истекающих в ближайшие 30 дней. Webhooks Чтобы мгновенно получать в свои системы изменения жизненного цикла лицензии, используйте вебхуки. Адрес назначения задаётся в дилерской панели; Kalkhan отправляет на него JSON-тело запросом POST. СобытиеКогда срабатывает license.createdПри создании новой лицензии license.expiredПо истечении срока действия лицензии license.cancelledПри отмене лицензии или оформлении возврата Пример тела события POST /webhooks/kalkhan HTTP/1.1 X-Kalkhan-Signature: t=1784937600,v1=9f8c1d2b7e4a63f05c8b1a9d4e7f2c30b6a5d8e1f4c7b0a3d6e9f2c5b8a1d4e7 Content-Type: application/json; charset=utf-8 { "id": "evt_01J9X2K7QF", "event": "license.created", "createdAt": "2026-07-23T09:41:02Z", "data": { "key": "KLKN-7B10-CE45-2F93-D8A6", "product": "Kalkhan Anti-Virüs (Windows)", "edition": "business", "seats": 25, "expiresAt": "2027-07-23T23:59:59Z" } } Проверка подписи Каждый запрос вебхука подписывается заголовком X-Kalkhan-Signature. Подпись — это HMAC-SHA256 от метки времени и сырого тела запроса, вычисленный на вашем секретном ключе вебхука. Обязательно проверяйте подпись до обработки запроса и отклоняйте метки времени старше 5 минут. // Node.js — проверка подписи const crypto = require('crypto'); function dogrula(rawBody, header, secret) { const parts = Object.fromEntries( header.split(',').map(p => p.split('=')) ); const beklenen = crypto .createHmac('sha256', secret) .update(parts.t + '.' + rawBody) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(beklenen), Buffer.from(parts.v1) ); } Если ваш сервер не вернёт ответ 2xx, событие будет повторно отправлено не более 5 раз с нарастающими интервалами (1 мин, 5 мин, 30 мин, 2 ч, 12 ч). Исходите из того, что одно и то же событие может прийти несколько раз, и проверяйте повторы по полю id. Коды ошибок Ошибки возвращаются со стандартными HTTP-статусами, а тело ответа содержит машиночитаемый code и поясняющий message. HTTPКодОписание 400invalid_requestПараметр отсутствует или имеет неверный формат 401unauthorizedAPI-ключ не передан, недействителен или отозван 403forbiddenУ ключа нет прав на этот эндпоинт 404not_foundЗапрошенная лицензия или ресурс не найдены 409duplicateЗапись уже существует; дублирующий запрос отклонён 429rate_limitedПревышено ограничение частоты; дождитесь времени из заголовка Retry-After 500server_errorНепредвиденная ошибка на стороне сервера Пример ответа с ошибкой HTTP/1.1 400 Bad Request Content-Type: application/json; charset=utf-8 { "error": { "code": "invalid_request", "message": "Поле customer.taxId обязательно для корпоративных лицензий.", "field": "customer.taxId", "requestId": "req_01J9X2K7QF" } } Указывайте значение requestId из ответа при обращении в поддержку — так проблему разберут быстрее. SDK и примеры Официальные SDK находятся в разработке. Пока вы можете обращаться к API напрямую по HTTP. Ниже приведены варианты запроса на проверку лицензии для cURL и JavaScript. cURL curl -X POST https://api.kalkhan.com.tr/v1/licenses/verify \ -H "Authorization: Bearer klk_live_xxx" \ -H "Content-Type: application/json; charset=utf-8" \ -d '{ "key": "KLKN-4F2A-9C71-B3D8-A15E", "deviceId": "WIN-8f2c41ab9d", "productId": "kalkhan-av-windows" }' JavaScript (fetch) const API_KEY = process.env.KALKHAN_API_KEY; // храните на стороне сервера async function lisansDogrula(key, deviceId) { const res = await fetch( 'https://api.kalkhan.com.tr/v1/licenses/verify', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json; charset=utf-8' }, body: JSON.stringify({ key, deviceId, productId: 'kalkhan-av-windows' }) } ); if (!res.ok) { const { error } = await res.json(); throw new Error(`${error.code}: ${error.message}`); } return res.json(); // { status, product, edition, seats, expiresAt } } lisansDogrula('KLKN-4F2A-9C71-B3D8-A15E', 'WIN-8f2c41ab9d') .then(lisans => console.log(lisans.status, lisans.expiresAt)) .catch(err => console.error('Проверка не удалась:', err.message)); Эти примеры предназначены только для серверной части. Не размещайте API-ключ в коде, который выполняется в браузере; проводите запросы проверки от клиентских приложений через собственный бэкенд. Закрытая бета API сейчас находится в стадии закрытой беты; чтобы получить доступ, напишите на bilgi@kalkhan.com.tr. Во время беты в эндпоинтах возможны обратно несовместимые изменения.