KSPX · API
← В консоль

API документация

REST-API сервиса удалённой оплаты Kaspi. Твои продукты (сайт, бот, 1С) выставляют счета и получают вебхук об оплате.

Base URL: http://localhost:3200 (локально). Все тела — JSON, кодировка UTF-8.

Авторизация

Публичные эндпоинты счетов требуют заголовок X-API-Key — ключ магазина (получаешь при создании магазина, формат kpk_…).

X-API-Key: kpk_35a046ba417358733cf53862163ccb40ccef33ed
Админ-эндпоинты (магазины, онбординг, метрики, доставки) сейчас без авторизации — рассчитаны на доверенный localhost/панель. В проде закрой их сетевым доступом или прокси с аутентификацией.

Форматы номеров

КонтекстФорматПример
Клиент (в счёте)11 цифр, начинается с 777001234567
Кассир (онбординг)10 цифр, начинается с 7 (без 8 и без +7)7001234567

Суммы — целые, в тенге (минимальная единица — 1 ₸).

Счета

Основной сценарий: создаёшь счёт → клиент платит в Kaspi → тебе прилетает вебхук. Все эндпоинты требуют X-API-Key.

post/invoicesX-API-Key

Выставить счёт с push-уведомлением в приложение клиента.

ПолеОписание
phonerequiredНомер клиента, 11 цифр (77XXXXXXXXX)
amountrequiredСумма в тенге, целое > 0
commentoptionalКомментарий, до 255 символов
idempotencyKeyoptionalКлюч идемпотентности — повтор с тем же ключом вернёт существующий счёт (200)
# запрос
curl -X POST http://localhost:3200/invoices \
  -H "X-API-Key: kpk_…" -H "Content-Type: application/json" \
  -d '{"phone":"77001234567","amount":1500,"comment":"Заказ №42","idempotencyKey":"order-42"}'
// 201 Created
{
  "id": "16286362815",
  "kaspiInvoiceId": "16286362815",
  "status": "PENDING",
  "amount": 1500,
  "phone": "77001234567",
  "kind": "phone",
  "createdAt": "2026-07-01T07:08:39.000Z"
}

409 {"code":"NEEDS_REAUTH"} — сессия кассира истекла, нужен повторный вход. 502 — ошибка провайдера Kaspi.

Создать оплату по ссылке (QR-токен Kaspi). Номер клиента не нужен — ссылку pay.kaspi.kz отправляешь в любой мессенджер.

ПолеОписание
amountrequiredСумма в тенге
commentoptionalКомментарий
idempotencyKeyoptionalКлюч идемпотентности
curl -X POST http://localhost:3200/invoices/link \
  -H "X-API-Key: kpk_…" -H "Content-Type: application/json" \
  -d '{"amount":1500}'
// 201 Created
{
  "id": "16286370001",
  "kaspiInvoiceId": "16286370001",
  "status": "PENDING",
  "amount": 1500,
  "kind": "link",
  "payUrl": "https://pay.kaspi.kz/pay/abc123",
  "expireAt": "2026-07-01T07:20:00.000Z",
  "createdAt": "2026-07-01T07:08:39.000Z"
}
get/invoicesX-API-Key

Список счетов магазина (новые сверху, до 50). Возвращает массив Invoice.

get/invoices/:idX-API-Key

Один счёт по внутреннему id. 404 если не найден.

post/invoices/:id/cancelX-API-Key

Отменить счёт. Возвращает обновлённый Invoice со статусом CANCELED.

post/invoices/:id/refundX-API-Key

Возврат по оплаченному счёту. Тело {"amount": 500} — необязательно (по умолчанию полная сумма).

curl -X POST http://localhost:3200/invoices/ID/refund \
  -H "X-API-Key: kpk_…" -H "Content-Type: application/json" \
  -d '{"amount":500}'   # → {"ok":true,"refunded":500}
get/clients/lookup?phone=X-API-Key

Имя клиента Kaspi по номеру (11 цифр) — для подтверждения перед выставлением счёта.

curl http://localhost:3200/clients/lookup?phone=77001234567 \
  -H "X-API-Key: kpk_…"   # → {"name":"Иван И."}

Вебхуки

При смене статуса счёта сервис делает POST на webhook.url магазина. Ретраи 5с/30с при неуспехе.

Заголовки: X-Webhook-Signature: sha256=<hmac>, X-Webhook-Id, X-Idempotency-Key: invoiceId:event.

// тело POST на твой endpoint
{
  "event": "invoice.paid",        // paid | failed | expired | canceled
  "invoiceId": "16286362815",
  "kaspiInvoiceId": "16286362815",
  "status": "PAID",
  "amount": 1500,
  "phone": "77001234567",
  "occurredAt": "2026-07-01T07:10:12.000Z"
}

Проверка подписи (Node.js)

import { createHmac, timingSafeEqual } from 'node:crypto';

function verify(rawBody, header, secret) {
  const mac = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
  const a = Buffer.from(mac), b = Buffer.from(header || '');
  return a.length === b.length && timingSafeEqual(a, b);
}
Подписывай/сравнивай именно сырое тело запроса (до JSON.parse), иначе подпись не сойдётся.

Кассир · онбординг

Трёхшаговый вход по SMS. processId из шага 1 прокидывается дальше. Номер кассира — 10 цифр (см. форматы).

post/onboarding/:merchantId/init

Инициализация. Ответ: {"success":true,"processId":"…"}.

post/onboarding/:merchantId/send-phone

Тело {"phoneNumber":"7001234567","processId":"…"}. Kaspi отправит код (SMS или пуш в приложение — зависит от аккаунта). Ответ содержит success, view, desc.

post/onboarding/:merchantId/verify-otp

Тело {"otp":"123456","processId":"…"}. Ответ {"connected":true,"orgName":"…"} — сессия кассира сохранена.

Аккаунты со входом по паролю (KPEnterLoginPassword, обычно бизнес-профили) текущим флоу не поддерживаются — используй клиентский номер кассира с SMS-входом.
post/merchants/:id/disconnect

Отключить кассира (удалить сессию на нашей стороне). Полностью отозвать «устройство» можно только в приложении Kaspi владельца: Настройки → Безопасность → Устройства.

Магазины

Управление арендаторами (админ). Каждый магазин = свой API-ключ + webhook + выделенный sidecar.

post/merchants

Создать магазин. Тело {"name":"Мой магазин","webhookUrl":"https://shop/hook"}.

// 201 — секреты показываются один раз
{ "id": "m-f2b547a7", "name": "Мой магазин",
  "apiKey": "kpk_…", "webhookSecret": "87ba…",
  "sidecarUrl": "http://sidecar1:3000" }
get/merchants

Список магазинов со статусом кассира (connected, sessionStatus, orgName, webhookUrl).

patch/merchants/:id

Изменить name / webhookUrl / webhookSecret. Тело — любые из этих полей.

delete/merchants/:id

Удалить магазин (каскадно чистит сессию и счета, освобождает слот пула sidecar).

post/merchants/:id/test-webhook

Отправить пробное invoice.paid на webhook магазина. Ответ {"delivered":true,"statusCode":200,"attempts":1}.

Статус и метрики

get/merchants/:id/status

{"sidecarUp":true,"connected":true,"sessionStatus":"ACTIVE","orgName":"…"}

get/merchants/:id/stats
{ "todayRevenue": 12500, "todayPaidCount": 8, "pendingCount": 2,
  "totalCount": 40, "conversion": 0.65,
  "series": [{ "day": "06-25", "revenue": 3000 }, … ] }

Доставки вебхуков

get/merchants/:id/deliveries

Журнал отправок: event, invoiceId, delivered, statusCode, attempts, lastAt.

post/deliveries/:id/resend

Повторить доставку. Ответ {"delivered":true,"statusCode":200}.

Объект Invoice

ПолеТипОписание
idstringВнутренний id счёта
kaspiInvoiceIdstringid операции в Kaspi (QrOperationId)
statusstringСм. статусы
amountnumberСумма, ₸
phonestringНомер клиента (пусто для ссылки)
kindstringphone | link
payUrlstring?Ссылка pay.kaspi.kz (для link)
expireAtstring?Истечение ссылки (ISO)
commentstring?Комментарий
receiptUrlstring?Квитанция Kaspi ОФД
createdAt / paidAtstringВремя (ISO 8601)

Статусы счёта

PENDING ожидает оплату   PAID оплачено   FAILED отклонено   EXPIRED истёк срок   CANCELED отменён

KSPX · внутренний API. Механизм — реверс мобильного POS Kaspi, продовое использование на свой риск.