Машинная дорожка
Дайте ИИ-ассистенту любой из этих URL — этого достаточно, чтобы он понял весь API и построил интеграцию:
https://apipay.kz/llms.txt— компактный контекст для LLM: методы, проверенные факты, чейнджлог.https://apipay.kz/openapi.json— полная OpenAPI 3.0 спецификация (для генерации типизированного клиента).https://apipay.kz/apipay-api-docs.md— полный Markdown с примерами на разных языках (для парсинга).https://apipay.kz/docs.html— HTML-документация для людей.- Пошаговый плейбук для ИИ — apipay.kz/for-ai; готовые промпты — apipay.kz/prompts.
Фраза-подсказка: «Скопируй ИИ-ассистенту ссылку https://apipay.kz/llms.txt (или openapi.json) и попроси построить приём платежей ApiPay — дальше следуй промптам из этой статьи».
Что такое ApiPay (контекст для ИИ)
ApiPay — независимый сервис приёма платежей Kaspi Pay по номеру телефона в Казахстане через REST API; он не аффилирован с Kaspi и не является «официальной интеграцией Kaspi». Мерчант создаёт счёт → покупатель получает уведомление в приложении Kaspi и платит → приходит вебхук об оплате. Деньги идут напрямую на Kaspi-счёт мерчанта, без договора с банком и без процента с продаж. Два способа: кабинет (без кода) и REST API (X-API-Key, /api/v1).
Автономный sandbox-тест-цикл
Это ядро интеграции: ИИ-агент проверяет всё end-to-end сам, без Kaspi и без телефонов. Схема: создать sandbox-счёт → simulate-status → проверить вебхук через webhook-logs → (при paid) возврат.
Шаг 1. Создать sandbox-счёт. POST /invoices (по номеру) или POST /invoices/qr (QR). Если организация в песочнице, счёт помечается is_sandbox: true. Ответ — 201, статус сначала processing, затем вебхук переводит его в pending. Дождитесь pending (поллингом GET /invoices/{id} или по вебхуку), прежде чем симулировать статус.
Шаг 2. Симулировать статус — POST /invoices/{invoice}/simulate-status:
- Работает только для sandbox-счёта (
is_sandbox: true). Для не-sandbox счёта →403 not_sandbox. - Тело:
{ "status": <enum> }, гдеstatus ∈ { paid, cancelled, expired, error, qr_scanned }. paid/cancelled/expired— счёт уходит в терминальный статус, отправляется вебхукinvoice.status_changed. Опционально можно задатьkaspi_source_type(GOLD|RED|LOAN|BUSINESSACCOUNT|BANKINTEGRATIONACCOUNT) иkaspi_sale_type(Remote|QR|Static|Restaurant); иначе приpaidони выбираются случайно.error— счёт получаетerror_code: sandbox_simulated_errorиerror_message(свой текст в необязательном параметреerror_message, ≤255 символов; по умолчанию «Симулированная ошибка (sandbox).»). Вебхук уходит как при реальной ошибке.qr_scanned— только для QR-счёта: статус остаётсяpending, уходит вебхукinvoice.qr_scannedсqr_substate: "scanned". Повтор →400 already_scanned; для не-QR счёта →400 not_qr_invoice.- Если счёт не в
pending→400 invalid_status_transition(в ответеcurrent_statusиallowed_from: ["pending"]). - Успех —
200 { "message": "Invoice status simulated", "invoice": {…} }. Свой лимит — 60/мин на ключ (не расходует общий лимит 200/мин).
Шаг 3. Проверить доставку вебхука — GET /webhook-logs:
- Read-only логи доставок вашей организации. Фильтры:
invoice_id,event(invoice.status_changed,invoice.qr_scanned,invoice.refunded),status(success|failed),date_from/date_to,sort_by(created_at|response_time_ms|response_status),sort_order,per_page≤ 100. - Пагинация плоская:
{ current_page, data, total }. GET /webhook-logs/{id}— одна доставка с полнымиrequest_body/response_body; чужой лог →404(защита от перебора).- Поля записи
WebhookLogEntry:id,event,invoice_id,url,request_body(полный payload JSON-строкой),response_body(обрезан до 4096 байт),response_status,status(success|failed),response_time_ms,error_message,created_at,retry_of. Логи хранятся 14 дней; повторная отправка (retry) через API недоступна — только из кабинета. - Критерий успеха цикла: по каждому симулированному статусу в
/webhook-logsпоявляется записьstatus: successс ожидаемымevent.
Шаг 4 (опционально). Проверить возврат. При paid — POST /invoices/{id}/refund (полный или частичный amount), затем GET /invoices/{id}/refunds.
Полный воспроизводимый цикл (замените YOUR_API_KEY; номер — маска или sandbox-константа, не реальный):
BASE="https://api.apipay.kz/api/v1"
KEY="YOUR_API_KEY"
ID=$(curl -s -X POST "$BASE/invoices" -H "X-API-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{"amount":10000,"phone_number":"87770000001","description":"Sandbox test"}' \
| jq -r '.id')
curl -s -X POST "$BASE/invoices/$ID/simulate-status" -H "X-API-Key: $KEY" \
-H "Content-Type: application/json" -d '{"status":"paid"}'
curl -s "$BASE/webhook-logs?invoice_id=$ID&event=invoice.status_changed" \
-H "X-API-Key: $KEY" | jq '.data[] | {event, status, response_status}'
Готовые промпты для вашего ИИ
Скопируйте нужный промпт своему ассистенту. Каждый дан на русском и английском.
1. Сгенерировать клиент по OpenAPI.
RU: «Открой
https://apipay.kz/openapi.jsonи сгенерируй типизированный клиент для ApiPay: base URLhttps://api.apipay.kz/api/v1, заголовок авторизацииX-API-Key. РеализуйPOST /invoices(счёт по номеру),GET /invoices/{id}(статус),POST /invoices/{id}/refund(возврат). Ключ бери из переменной окружения, не хардкодь.» EN: “Openhttps://apipay.kz/openapi.jsonand generate a typed client for ApiPay: base URLhttps://api.apipay.kz/api/v1, auth headerX-API-Key. ImplementPOST /invoices(invoice by phone),GET /invoices/{id}(status),POST /invoices/{id}/refund. Read the key from an environment variable, never hardcode it.”
2. Вебхук-приёмник с проверкой HMAC.
RU: «Построй HTTP-эндпоинт-приёмник вебхуков ApiPay. Проверяй подпись
X-Webhook-Signature: sha256=<hex>=hash_hmac('sha256', <сырое тело>, webhook_secret)по сырому телу запроса (до парсинга JSON). Отвечай HTTP 2xx в пределах 5 секунд. Дедуплицируй по(invoice.id, invoice.status). Обработай событияinvoice.status_changed,invoice.qr_scanned,invoice.refunded.» EN: “Build an ApiPay webhook receiver. Verify theX-Webhook-Signature: sha256=<hex>header =hash_hmac('sha256', <raw body>, webhook_secret)against the raw request body (before JSON parsing). Reply HTTP 2xx within 5 seconds. Deduplicate by(invoice.id, invoice.status). Handleinvoice.status_changed,invoice.qr_scanned,invoice.refunded.”
3. Полный sandbox-цикл с проверкой.
RU: «В песочнице: создай счёт через
POST /invoices, дождисьpending, затем прогониPOST /invoices/{id}/simulate-statusдляpaid,errorи (для QR-счёта)qr_scanned; после каждого убедись черезGET /webhook-logs?invoice_id=…, что доставкаstatus: successс ожидаемымevent. Учти:simulate-statusработает только для sandbox-счетов.» EN: “In sandbox: create an invoice viaPOST /invoices, wait forpending, then runPOST /invoices/{id}/simulate-statusforpaid,errorand (for a QR invoice)qr_scanned; after each, confirm viaGET /webhook-logs?invoice_id=…that delivery isstatus: successwith the expectedevent. Note:simulate-statusworks only for sandbox invoices.”
4. Обработка ошибок по error_code.
RU: «Реализуй ветвление по полю
error_code(каталог —/errors): для асинхронной ошибки счёта (вебхукinvoice.status_changed,status=error) читайerror_code/error_message; на429уважай заголовокRetry-After; на422разбирайerrors. „Повтор“ приstatus=errorозначает создать новую операцию, а не ретраить ту же.» EN: “Branch on theerror_codefield (catalog at/errors): for an async invoice error (webhookinvoice.status_changed,status=error) readerror_code/error_message; on429respect theRetry-Afterheader; on422parseerrors. A ‘retry’ onstatus=errormeans creating a new operation, not retrying the same one.”
Проверка вебхуков и HMAC
- Заголовок подписи:
X-Webhook-Signature: sha256=<hex>— присылается только если у ключа заданwebhook_secret. - Подпись =
sha256=+hash_hmac('sha256', <тело запроса>, webhook_secret). Верифицируйте по сырому телу (raw body), а не по перепарсенному JSON. - Успех доставки — любой HTTP 2xx; приёмник должен ответить в пределах 5 секунд, тяжёлую работу выносите в фон.
- Ретраи: до 11 попыток (1 + 10) с экспоненциальным backoff (~2 часа суммарно); в sandbox для invoice-вебхуков — 3 попытки. Ретраятся только HTTP ≥ 500, ровно
429и сетевые ошибки;3xx/4xx(кроме429) считаются доставленными. - Circuit breaker на ключ: 5 подряд неудач → пауза 5 мин, 10 → 30 мин, 20 → 2 ч, 50 → полная остановка до ручного вмешательства.
- Дедуп обязателен (ретрай после частичной доставки уходит всем приёмникам). Ключи дедупа:
(invoice.id, invoice.status),(refund.id, refund.status),(event, subscription.id, invoice_id). - Даты в вебхуках — ISO 8601 UTC (
+00:00). У каждого payload естьsource(имя ключа, может бытьnull) иis_sandbox. Всего 12 событий вебхуков (перечислены вllms.txt). Настройка и код проверки подписи — в разборе настройки вебхуков.
Обработка ошибок
- Определяйте тип ошибки по стабильному полю
error_code(snake_case), а не по тексту;message/errorоставлены для обратной совместимости. Каталог кодов — на странице apipay.kz/errors, у каждой ошибки естьdoc_url. - Асинхронные ошибки Kaspi.
POST /invoicesиPOST /invoices/qrотвечают201соstatus=processing. Если Kaspi не смог, статус станетerror, причина — вerror_message, код — вerror_code(напримерclient_not_found,network_unavailable,kaspi_throttled). Забирайте черезGET /invoices/{id}или из вебхукаinvoice.status_changed(status=error). Не пересоздавайте счёт, пока он вprocessing— получите два живых счёта. - HTTP-статусы:
401(ключ отсутствует/невалиден/деактивирован),403(организация не верифицирована или заблокирована),404,409(дубль идемпотентности),422(валидация — детали вerrors),429(Retry-After/retry_after),502(сторона Kaspi),503(сессия Kaspi). sandbox_simulated_error— тестовый код: появляется только в песочнице черезsimulate-statusсоstatus=error, в бою его не бывает.
Sandbox vs Production и предупреждения
- При регистрации создаётся sandbox-организация; тестовые счета и подписки помечены
is_sandbox: true. Переключение в рабочий режим — через личный кабинет; ключи и вебхуки переносятся автоматически. Что именно меняется — в разборе песочница и рабочий режим. simulate-statusдоступен только для sandbox-счетов — в проде недоступен ни при каких условиях.- Гигиена ключа:
X-API-Keyне коммитьте в репозиторий, храните в переменных окружения или секретах. В примерах — толькоYOUR_API_KEY. Разница ключа и секрета подписи — в разборе API-ключ и вебхук-секрет. - Запрет массового перебора
POST /clients/check: сервер детектит аномалии (honeypot, всплеск, низкая конверсия) и деактивирует ключ без предупреждения. Sandbox-режим:87770000001→has_kaspi: true,"Иван И.";87770000002→has_kaspi: false; любой другой →false. - Отмена в проде асинхронна:
POST /invoices/{id}/cancel→202+ статусcancelling, реальный итог придёт вебхуком.
Краткий справочник
- Статусы счёта:
processing,pending,cancelling,paid,cancelled,expired,error,partially_refunded. Статусаrefundedнет — полный возврат оставляетpaid+is_fully_refunded: true. - Ключевые эндпоинты цикла:
POST /invoices,POST /invoices/qr,GET /invoices/{id},POST /invoices/{id}/simulate-status(sandbox),GET /webhook-logs,GET /webhook-logs/{id},POST /invoices/{id}/refund. - Машинные файлы:
/llms.txt,/openapi.json,/apipay-api-docs.md,/docs.html. - Partner API (
X-Partner-Key,/api/partner) — отдельная тема для CRM и платформ, которые онбордят чужих мерчантов: см. разбор как партнёру подключить организацию мерчанта. - Как ставится приём Kaspi целиком (регистрация, кассир, первый счёт) — в разборе настройки за 15 минут.
In English
ApiPay is an independent Kazakhstani service for accepting Kaspi Pay payments over the merchant's own Kaspi Pay — not affiliated with Kaspi, and never an "official Kaspi integration". A merchant creates an invoice, the buyer pays inside the Kaspi app, and a webhook confirms it. Money goes straight to the merchant's Kaspi account.
Give your AI assistant one of the machine files and it can build the integration: https://apipay.kz/llms.txt, https://apipay.kz/openapi.json, or https://apipay.kz/apipay-api-docs.md. Base URL: https://api.apipay.kz/api/v1; auth header X-API-Key; Content-Type: application/json; 200 req/min per key.
Autonomous sandbox test loop (no Kaspi, no phone numbers): create a sandbox invoice with POST /invoices (wait for pending), drive its status with POST /invoices/{id}/simulate-status (paid|cancelled|expired|error|qr_scanned, sandbox only — 403 not_sandbox otherwise; 400 invalid_status_transition if not pending), then verify webhook delivery via GET /webhook-logs?invoice_id=… (flat pagination {current_page, data, total}). Success = a status: success row with the expected event. Optionally refund with POST /invoices/{id}/refund.
Webhooks: signature X-Webhook-Signature: sha256=<hex> = hash_hmac('sha256', <raw body>, webhook_secret) — verify against the raw body, reply 2xx within 5 seconds, deduplicate by (invoice.id, invoice.status). Up to 11 delivery attempts; circuit breaker at 5/10/20/50 failures. Errors: branch on the stable error_code (catalog at /errors); POST /invoices is async (201 status=processing); respect Retry-After on 429. Never hardcode X-API-Key; don't bulk-probe POST /clients/check — anomaly detection deactivates the key. The ready-made prompts above are provided in English too.
Частые вопросы
Что дать ИИ-агенту, чтобы он начал интеграцию?
Любую машинную ссылку: https://apipay.kz/llms.txt (компактный контекст) или https://apipay.kz/openapi.json (полная спека для генерации клиента). Base URL API — https://api.apipay.kz/api/v1, авторизация заголовком X-API-Key.
Можно ли проверить интеграцию без реального Kaspi и без телефонов?
Да, в этом суть sandbox-цикла: создаёте sandbox-счёт, гоните POST /invoices/{id}/simulate-status и проверяете вебхук через GET /webhook-logs. Настоящий Kaspi и SMS не задействуются, номера — только маска или sandbox-константы.
Работает ли simulate-status в рабочем режиме?
Нет. simulate-status доступен только для sandbox-счетов (is_sandbox: true); в проде вызов вернёт 403 not_sandbox.
Почему POST /invoices вернул 201, но статус processing?
Это не ошибка: создание счёта асинхронное. Счёт перейдёт в pending вебхуком; при сбое Kaspi станет error с error_code/error_message. Не пересоздавайте счёт в processing — иначе получите два живых счёта.
Как отличить X-API-Key от X-Partner-Key?
X-API-Key — обычный мерчантский ключ для /api/v1 (счета, вебхуки, возвраты). X-Partner-Key — для Partner API (/api/partner), которым платформы онбордят чужих мерчантов; это отдельная тема.