ApiPay.kz — плейбук интеграции для ИИ-агентов

Один документ — весь сценарий подключения приёма платежей Kaspi Pay. Для GPT, Claude, Cursor, Copilot и других ИИ-ассистентов.

Это исполняемая инструкция, а не справочник. Выполняй шаги 0–8 по порядку. Всё необходимое (адреса, формат запросов, проверка подписи, ошибки) есть прямо здесь — открывать большие файлы (openapi.json, docs.html) НЕ нужно.

Шаг 0. Как себя вести (читает ИИ-ассистент)

Шаг 1. Объясни клиенту, что такое ApiPay

Передай клиенту своими словами 3 факта:

  1. ApiPay — это автоматизация вашего же Kaspi Pay. Мы не банк и не платёжная система. Мы даём программный доступ (API) к приёму платежей через ваш Kaspi Business.
  2. Деньги идут напрямую вам в Kaspi. Мы их не храним и не трогаем — только показываем вам и вашему сайту, что клиент оплатил.
  3. Комиссии с продаж нет. Оплачивается только подписка на сервис (от 10 000 ₸/мес). После подключения — 3 дня бесплатного доступа.

Шаг 2. Проверь предпосылки — ДО написания кода

2a. Кассир Kaspi — нужен только для рабочего режима

Это не блокер для начала интеграции. Новые организации создаются в песочнице по умолчанию — там счета создаются и без подключённого кассира: деньги никуда не идут, всё имитируется. Можно прямо сейчас писать код, создавать тестовые счета и принимать вебхуки.

Кассир Kaspi обязателен только при переходе в рабочий режим (шаг 8): без активной сессии кассира реальные счета будут падать с kaspi_session_not_configured. До этого момента подключение кассира — параллельная задача клиента, она не должна тормозить разработку.

Когда клиент будет готов идти в прод, ему нужно подключить Kaspi Business как «Кассира» — это делается один раз, двумя способами на выбор. Кратко суть (передай клиенту, чтобы запустил это параллельно):

  1. Клиент регистрируется на apipay.kz (вход по номеру телефона через WhatsApp). Это обязательно для обоих способов — кассир привязывается к аккаунту ApiPay, без регистрации подключить организацию нельзя. Регистрировать нужно на личный номер владельца/руководителя — на него идут уведомления о счетах и ошибках; это не номер кассира.
  2. Клиент берёт отдельный номер телефона (SIM), не привязанный к его личному Kaspi.
  3. В приложении Kaspi Business: Профиль → Сотрудники → добавить сотрудника с ролью «Кассир» на этот номер. После добавления Kaspi пришлёт на номер кассира ссылку на скачивание приложения Kaspi Business — устанавливать ничего не нужно, это сообщение лишь означает, что сотрудник-кассир создан и готов.
  4. Способ 1 (рекомендуемый): в личном кабинете ApiPay открывает Настройки → вкладка «Авторизация Kaspi», вводит номер кассира и код из SMS — занимает около 1 минуты.
  5. Способ 2: пишет в поддержку ApiPay в WhatsApp +7 708 516 74 89 номер кассира и название организации — специалист подключит за 15–30 минут.
  6. Подробная инструкция по обоим способам: apipay.kz/connect-cashier.
Не жди подключения кассира. Иди по шагам 3–7 в песочнице — там всё работает без кассы. Параллельно дай клиенту ссылку /connect-cashier — она пригодится на шаге 8, когда будете переходить в рабочий режим.

2b. Где будет жить интеграция — нужен сервер

Определи тип проекта клиента:

Важно: X-API-Key — это секрет. Он используется ТОЛЬКО на сервере. Никогда не помещай ключ в код, который выполняется в браузере клиента (frontend) — его увидят все посетители сайта.

Шаг 3. Получи доступы в личном кабинете

Это делает человек в кабинете на apipay.kz. Нужно войти под ролью Владелец или Разработчик — у роли «Менеджер» доступа к ключам нет. Точный путь по меню:

API-ключ

  1. Настройки → вкладка «Подключение».
  2. Кнопка «Создать новый ключ».
  3. Ключ показывается один раз в окне — нажми «Копировать» и сохрани сразу.

Адрес для вебхука (куда слать уведомления об оплате)

  1. Там же, на карточке созданного ключа — кнопка «Изменить».
  2. Поле «Адрес для уведомлений» — впиши публичный HTTPS-адрес обработчика (например https://ваш-сайт.kz/webhooks/apipay).

Секрет вебхука (для проверки подписи)

  1. На карточке ключа — кнопка «Создать подпись».
  2. Секрет тоже показывается один раз — скопируй сразу.
СТОП. Попроси у клиента 3 значения: API-ключ, секрет вебхука и подтверждение, что адрес вебхука вписан в кабинете. Напомни: вебхук и секрет настраиваются на каждый ключ отдельно — отдельной общей страницы «вебхуки» нет.

Сохрани значения в переменные окружения сервера (никогда — в репозиторий):

APIPAY_API_KEY=...
APIPAY_WEBHOOK_SECRET=...
APIPAY_BASE_URL=https://bpapi.bazarbay.site/api/v1

Шаг 4. Встрой создание счёта

ПараметрЗначение
Базовый адрес APIhttps://bpapi.bazarbay.site/api/v1
Авторизациязаголовок X-API-Key: ваш_ключ (только на сервере)
Content-Typeapplication/json
Лимит запросов60 в минуту на ключ
Базовый адрес API всегда https://bpapi.bazarbay.site/api/v1 — он не зависит от того, на каком сайте ты читаешь эту инструкцию. Не используй адрес документации (например localhost или apipay.kz) как адрес API.

Создать счёт — POST /invoices

// выполняется на сервере
const res = await fetch('https://bpapi.bazarbay.site/api/v1/invoices', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.APIPAY_API_KEY,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    phone_number: '87001234567',     // обязательно. Формат строго 8XXXXXXXXXX (11 цифр)
    amount: 15000,                   // обязательно. Сумма в тенге
    description: 'Заказ №123',       // необязательно, до 500 символов
    external_order_id: 'order_123'   // необязательно — ваш ID заказа для сверки
  })
})
const invoice = await res.json()
// → { id, amount, status: 'processing', phone, created_at }

Клиент получит уведомление в приложении Kaspi и оплатит там. Сохрани invoice.id рядом со своим заказом.

Проверить статус — GET /invoices/{id}

Жизненный цикл статуса: processingpendingpaid (или cancelled / expired / error). Массовая проверка нескольких счетов сразу — POST /invoices/status/check с телом {"invoice_ids":[1,2,3]}.

Шаг 5. Принимай вебхук об оплате

Когда счёт оплачен, ApiPay сам отправляет POST на твой адрес вебхука. Тело события:

{
  "event": "invoice.status_changed",
  "invoice": {
    "id": 42,
    "external_order_id": "order_123",
    "amount": "15000.00",
    "status": "paid",
    "paid_at": "2026-05-19T14:35:00+06:00"
  },
  "source": "имя вашего ключа",
  "timestamp": "2026-05-19T14:35:01+06:00"
}

Другие события: invoice.refunded, subscription.payment_succeeded, subscription.payment_failed, subscription.grace_period_started, subscription.expired, webhook.test.

Проверка подписи — обязательно

Каждый вебхук содержит заголовок X-Webhook-Signature: sha256=<hex> — это HMAC-SHA256 от сырого тела запроса с секретом вебхука.

const crypto = require('crypto')

// rawBody — НЕОБРАБОТАННОЕ тело запроса (Buffer/строка), НЕ результат JSON.parse.
// В Express: app.post('/webhooks/apipay', express.raw({ type: 'application/json' }), ...)
function verifyWebhook(rawBody, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex')
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
}
Две частые ошибки: (1) подпись считают по уже разобранному JSON — нужно именно сырое тело; (2) обработчик отвечает дольше 5 секунд. Отвечай 2xx сразу, тяжёлую работу делай в фоне.

Повторные доставки и идемпотентность

Если твой сервер не ответил 2xx за 5 секунд (плюс до 3 секунд на соединение), ApiPay повторит доставку. Всего до 11 попыток (первая + 10 повторов) с нарастающей задержкой: 10с, 30с, 1м, 1.5м, 2м, 5м, 10м, 15м, 30м, 1ч — около 2 часов. 5xx/429/таймаут → повтор; прочие 4xx → без повтора.

Сделай обработчик идемпотентным: одно и то же событие об оплате может прийти несколько раз. Перед выдачей товара проверяй, не обработан ли уже этот invoice.id.

Если сервера нет (шаг 2b) — вместо вебхука опрашивай статус: периодически вызывай GET /invoices/{id}, пока не будет paid. Опрос полезен и при наличии вебхука — как сверка, если все 11 попыток не прошли.

Шаг 6. Протестируй интеграцию

Тестируй в тестовом режиме (песочнице) — счета не уходят в реальный Kaspi, деньги не двигаются. Новая организация в песочнице по умолчанию.

  1. Проверка вебхука. Попроси клиента в кабинете (Настройки → «Подключение», карточка ключа) нажать кнопку «Проверить уведомления» — ApiPay пошлёт на адрес тестовое событие webhook.test. Убедись, что твой обработчик его принял и подпись сошлась.
  2. Проверка оплаты. Создай тестовый счёт через POST /invoices. В песочнице оплату счёта имитируют из кабинета — попроси клиента это сделать; придёт вебхук invoice.status_changed со статусом paid.
  3. Локальная разработка. Если сервер пока на localhost, ApiPay до него не достучится — подними туннель (ngrok). Инструкция: apipay.kz/local-testing.
СТОП. Действия в кабинете («Проверить уведомления», имитация оплаты) выполняет человек. Дай ему точные указания и дождись результата, затем проверь, что событие дошло до кода.

Шаг 7. Отладка

Счёт не создаётся — таблица ошибок POST /invoices

HTTP / поле errorПричинаЧто сделать
422, ошибка в phone_numberТелефон не в формате 8XXXXXXXXXX Ровно 8 и 10 цифр, без +7, пробелов и скобок
401Неверный, отсутствующий или истёкший X-API-Key Проверь ключ и заголовок (шаг 3)
400 organization_requiredОрганизация не подключена Вернись к шагу 2 — подключить кассира
400 Organization not found or not verified Организация есть, но не верифицирована (рабочий режим) Дождаться верификации организации; пока тестируй в песочнице
400 kaspi_session_not_configured Сессия Kaspi не настроена (только рабочий режим — в песочнице эта ошибка не возникает) Клиенту подключить кассира: кабинет → Настройки → «Авторизация Kaspi», либо поддержка WhatsApp. Подробно — шаг 2a и /connect-cashier
503 kaspi_session_invalid Сессия Kaspi истекла или неисправна (только рабочий режим) Переподключить кассу по SMS (см. ниже)
422 connection_ambiguousУ организации несколько касс, основная не выбрана Передать в запросе kaspi_connection_id нужной кассы
400 sandbox_invoice_limitЛимит тестовых счетов исчерпан Очистить песочницу в кабинете
429Больше 60 запросов в минуту Подожди и повтори; смотри retry_after

Полная таблица ошибок (QR-счета, возвраты) — в docs.html.

Счёт создался (201), но статус стал error

Создание счёта асинхронное: POST /invoices возвращает 201 со статусом processing, дальше счёт уходит в Kaspi в фоне. Если что-то пошло не так на стороне Kaspi — это не ошибка HTTP, а статус error у счёта. Проверяй через GET /invoices/{id}:

Ошибки Kaspi приходят сырым текстом, фиксированных кодов у них нет. «Номер не в Kaspi» — попроси клиента дать номер с установленным приложением Kaspi. «Временно недоступен» — повтори создание счёта позже.

Сессия кассира Kaspi (kaspi_session_invalid)

Сессия кассира стабильна и обычно живёт месяцами — ежедневно или по расписанию переподключать кассу не нужно. Прерваться она может только при конкретных событиях: владелец вошёл в Kaspi с другого устройства или Kaspi сам сбросил сессию. В этом случае API отдаёт 503 kaspi_session_invalid. Ретраи запроса не помогут: владелец организации один раз переподключает кассу по SMS — кабинет, Настройки → «Авторизация Kaspi», либо через поддержку. Инструкция: /connect-cashier.

Оплата прошла, но подтверждение не пришло

Открой в кабинете Настройки → «Лог уведомлений» — там видно каждую отправку вебхука: адрес, HTTP-код ответа твоего сервера, отправленное тело и полученный ответ. Это главный инструмент диагностики:

Шаг 8. Переход в рабочий режим

Когда тесты в песочнице прошли:

СТОП. Кассир Kaspi обязателен именно здесь. Если клиент ещё не подключил кассира — самое время. Без активной сессии кассира любой счёт в рабочем режиме сразу падает с kaspi_session_not_configured (см. шаг 7). Инструкция: /connect-cashier. Дождись подтверждения, что кассир подключён, и только после этого переключай режим.
  1. Кассир Kaspi подключён по /connect-cashier, подписка оплачена (после подключения — 3 дня бесплатно).
  2. В кабинете, Настройки → «Подключение» — переключатель ТЕСТОВЫЙ → РАБОЧИЙ режим.
  3. В рабочем режиме счета уходят в реальный Kaspi, клиенты платят настоящими деньгами.

Готово — интеграция приёма платежей завершена.


Краткий справочник

ЧтоЗначение
Базовый адрес APIhttps://bpapi.bazarbay.site/api/v1
АвторизацияX-API-Key (заголовок, только на сервере)
Создать счётPOST /invoices
Статус счётаGET /invoices/{id}, POST /invoices/status/check
Отмена / возвратPOST /invoices/{id}/cancel, POST /invoices/{id}/refund
Подписки (рекуррент)POST /subscriptions + /pause, /resume, /cancel
Подпись вебхукаX-Webhook-Signature: sha256=…, HMAC-SHA256 от сырого тела
ПоддержкаWhatsApp +7 708 516 74 89

Полная документация (нужна редко — этого плейбука достаточно для типовой интеграции): docs.html · apipay-api-docs.md · openapi.json · llms.txt

CRM и платформам, которые подключают чужих мерчантов: см. Partner API (другой механизм — X-Partner-Key).