Как настроить полностью автоматический приём Kaspi для клиентов?

Обновлено 10 июля 2026 · Для вашего бизнеса · Версия в Markdown
Содержание
  1. Два ключа
  2. Шаг 1. Создать организацию клиента (авто)
  3. Шаг 2. Авторизация кассира — единственный ручной штрих
  4. Шаг 3. Выдать клиенту ключ и вебхук (авто)
  5. Шаг 4. Счета, каталог, статусы, возвраты (авто)
  6. Шаг 5. Оплата тарифа ApiPay — два способа
  7. Шаг 6. Вебхуки закрывают петлю без поллинга
  8. Сначала — sandbox
  9. Частые вопросы

Два ключа

У клиента нет отдельного аккаунта ApiPay: организацию, ключи и вебхук держит партнёр. Единственное действие клиента — подтвердить код из Kaspi-SMS при авторизации кассира (код приходит на номер кассира клиента, а не партнёру).

Ключей два, не путайте их — это главный источник ошибок:

  • X-Partner-Key — партнёрский, server-to-server: онбординг, авторизация кассира, выдача ключей, тариф, health. Хост .../api/partner.
  • per-org X-API-Key — ключ конкретного клиента: счета, статусы, возвраты, каталог, lookup. Хост .../api/v1.

Разбор пары ключей — в статье «Partner API white-label».

Шаг 1. Создать организацию клиента (авто)

POST /organizations с телом { "external_id": "crm-client-42", "name": "ТОО Клиент", "has_catalog": false }. external_id — ваш референс клиента в CRM и ключ идемпотентности: повторный вызов с тем же external_id вернёт ту же организацию (200), дубля не будет. В ответе — organization.id, status: "pending", sandbox_mode. Никакого участия клиента.

Шаг 2. Авторизация кассира — единственный ручной штрих

Три вызова подряд: POST /organizations/{id}/kaspi-auth/initsend-phoneverify-otp. На send-phone вы передаёте телефон кассира клиента в формате 7XXXXXXXXXX; Kaspi шлёт SMS-код на этот номер. Клиент диктует код — вы отправляете его в verify-otp ({ "otp": "1234" }). Всё, организация становится verified.

  • process_id живёт ~10 минут — уложите send-phone + verify-otp в это окно.
  • Слетела сессия позже — переавторизация тем же флоу с init + "force": true.

Ветки отказа send-phone и verify-otp (в том числе 409 cashier_unavailable, суточный 429 rate_limited и терминальные исходы, после которых нужен новый init) разобраны в статье «Как партнёру подключить организацию» — заложите их обработку до запуска.

Шаг 3. Выдать клиенту ключ и вебхук (авто)

POST /organizations/{id}/api-key с обязательным webhook_url (проходит SSRF-валидацию — приватный адрес 422); указывайте постоянный домен своего сервиса, не шортенер и не временный адрес-перехватчик. В ответе key (это X-API-Key) и webhook_secret приходят в открытом виде ровно один раз — сохраните оба сразу в своё секрет-хранилище, привязав к записи клиента. Клиент этих ключей не видит и не хранит — они живут у партнёра. Как ключ соотносится с секретом подписи — «API-ключ и вебхук-секрет».

Шаг 4. Счета, каталог, статусы, возвраты (авто)

Дальше работаете выданным X-API-Key против https://api.apipay.kz/api/v1 — от имени клиента, но без его участия:

  • Счёт по номеру — POST /api/v1/invoices (201, обработка асинхронная).
  • QR-счёт на экране кассы — POST /api/v1/invoices/qr. Срок жизни берите из qr_expires_at, а не из константы: окно и лимиты — в статье «QR-счёт: окно и лимиты».
  • Возврат — POST /api/v1/invoices/{id}/refund (полный или частичный).

Полный платёжный API — в мерчантской документации /docs; создание счетов по номеру — «Счёт Kaspi по номеру».

Шаг 5. Оплата тарифа ApiPay — два способа

Партнёр сам платит ApiPay за подписку клиента (start/business/pro/pro_max — сетка в статье «Тарифы и комиссия»). Это подписочная плата клиент→ApiPay, не оборот клиента. Два независимых способа оплатить один тариф:

  • По телефонуPOST /organizations/{id}/tariff/pay с { "tier_id", "period_months", "phone": "8XXXXXXXXXX" }. Push-счёт через Kaspi на телефон плательщика; активация асинхронная после оплаты.
  • Счётом на юрлицоPOST /organizations/{id}/tariff/invoice с реквизитами покупателя (buyer_bin 12 цифр, buyer_name, опц. buyer_address/contract). Синхронно возвращает download_url — публичную ссылку на PDF-счёт, который оплачивается банковским переводом. Тариф активируется вручную владельцем ApiPay после поступления средств — автоактивации у счёта нет.

Неоплаченный счёт (payment_method=invoice) не блокирует tariff/pay, и наоборот — способы независимы. Точную сумму за выбранный период возвращает сервер (GET /tariff-plans), не считайте её сами.

Шаг 6. Вебхуки закрывают петлю без поллинга

Два события снимают необходимость постоянно опрашивать статусы:

  • invoice.status_changed — приходит на per-org webhook_url клиента, когда счёт меняет статус (в т.ч. paid — клиент оплатил). Реагируйте на последний статус: легитимны и cancelled → paid, и expired → paid.
  • tariff.activated — приходит на webhook_url партнёра, когда владелец ApiPay вручную активировал тариф по выписанному счёту. Плоский payload с payment_id, invoice_number, tier, amount, expires_at.

Обе подписи — X-Webhook-Signature: sha256=HMAC-SHA256(raw body, secret), проверяйте по сырому телу, до JSON-парсинга (готовые приёмники — «Настройка вебхуков ApiPay»). Секреты разные: invoice.status_changed подписан per-org webhook_secret организации клиента, а tariff.activatedпартнёрским webhook_secret, который выдан вместе с X-Partner-Key. Доставка tariff.activated повторяется автоматически (до 11 попыток); при окончательном сбое активацию видно поллингом GET .../tariff/payments (pending → completed).

Сначала — sandbox

Весь пайплайн прогоняется в песочнице партнёра без реального Kaspi и SMS, детерминированно (магические номера кассира, OTP 0000) — можно покрыть автотестами. Пока партнёр в sandbox, боевые вызовы отбиваются 403 production_access_required; production открывается после ручного одобрения.

Не путайте телефоны: кассир — 7XXXXXXXXXX (авторизация Kaspi), плательщик — 8XXXXXXXXXX (кому выставляете счёт).

Частые вопросы

Заходит ли клиент в кабинет ApiPay?

Нет, ни разу. У клиента нет отдельного аккаунта ApiPay: организацию, X-API-Key и вебхук держит партнёр. Единственное действие клиента — продиктовать код из Kaspi-SMS при авторизации кассира.

Как узнать, что тариф по счёту активирован?

Придёт вебхук tariff.activated на webhook_url партнёра. Если он не дошёл (после ретраев), тот же факт виден поллингом GET .../tariff/payments: статус платежа payment_method=invoice переходит pending → completed. Автоактивации у tariff/invoice нет.

Куда приходят вебхуки об оплате счетов клиента?

На per-org webhook_url клиента (ключа, создавшего счёт). Партнёрский webhook_url в доставке invoice/refund не участвует — на него идёт только tariff.activated.

Интеграция с ИИ

Не настраивайте вручную — поручите ИИ

Скопируйте готовый промпт и вставьте своему ИИ-ассистенту — он настроит интеграцию за вас: получит доступы, создаст тестовый счёт в песочнице и подключит вебхуки. Техническую часть делает ИИ, а не вы.

Для разработчика. Полная спецификация API — apipay.kz/docs. Эта статья в Markdown — /guides/besshovnyy-priyom-kaspi-dlya-klientov-partnyora.md. Справочник фактов для ИИ-агента — apipay.kz/llms.txt.