Коротко
API-ключ — секретная строка, которая передаётся в заголовке X-API-Key. По нему API узнаёт вашу организацию.
POST /api/v1/invoices
X-API-Key: qp_live_a1b2c3d4…
Ключи бывают двух видов: qp_live_… работает с настоящими деньгами, qp_test_… — в песочнице. Ключ создаётся в кабинете и показывается только один раз — если не скопировали сразу, посмотреть его заново нельзя, придётся создавать новый.
Главное правило: ключ должен жить только на сервере.
Виды ключей
| Префикс | Режим | Что происходит |
|---|---|---|
qp_test_ | Песочница | Kaspi не вызывается, настоящих денег нет, оплату вы симулируете сами |
qp_live_ | Боевой | Настоящий Kaspi QR, настоящие деньги, нужен кассир Kaspi |
Режимы не смешиваются: ключом песочницы боевые счета не увидеть, и наоборот. Путаница с режимом — самая частая причина «у меня всё работало, а в проде перестало»: Чем песочница отличается от боевого режима.
При неверном формате ключа приходит invalid_api_key, при недействительном или удалённом — unauthorized: API отвечает 401.
Создание
Кабинет → Интеграции → API-ключи → создать ключ. При создании вы решаете три вещи:
| Что | Пояснение |
|---|---|
| Название | Для себя: «Сайт», «Telegram-бот», «1С». Потом по нему поймёте, кто что делает |
| Права (scopes) | Включайте только нужные: Права доступа (scopes) |
| Кассир | При желании ключ привязывается к конкретному кассиру |
Ключ показывается на экране один раз. Скопируйте его тут же и сразу положите в хранилище секретов или в .env на сервере. Дальше в кабинете видно только название и последние символы.
Где хранить
| Место | Можно |
|---|---|
Файл .env на сервере | Да |
| Секреты хостинга или CI | Да |
| Хранилище секретов вроде Vault | Да |
| JavaScript, выполняемый в браузере | Нет |
| Внутри мобильного приложения (APK/IPA) | Нет |
| Публичный репозиторий, история git | Нет |
| Скриншот, чат, трекер задач | Нет |
| Строка прямо в коде | Нет |
Причина простая: ключ, попавший в браузер или в приложение, может достать любой желающий. В мобильном приложении порядок должен быть таким: приложение → ваш сервер → Qut Pay → Kaspi.
Не забудьте добавить .env в .gitignore, а в коде читать переменную окружения:
// правильно
const KEY = process.env.QUTPAY_API_KEY;
// неправильно — ключ уедет в репозиторий вместе с кодом
const KEY = 'qp_live_a1b2c3d4e5f6';
Если ключ всё-таки утёк, первое действие — немедленно удалить его, и только потом создавать новый.
Отдельный ключ на каждую интеграцию
Один ключ на все системы выглядит удобно, но это плохая идея. Заведите отдельный ключ на каждую интеграцию:
| Плюс | Что даёт |
|---|---|
| Видимость | В журнале видно, какая система какой счёт создала |
| Изоляция | Утёк один — удаляете только его, остальное продолжает работать |
| Минимум прав | Боту только invoices:write, системе отчётности только invoices:read |
| Отчётность | Раздельный учёт по точкам или проектам |
Пример разделения:
Сайт → invoices:write, invoices:read
Telegram-бот → invoices:write, invoices:read
Отчётность → invoices:read
Панель возвратов → invoices:read, refunds:write
Привязка к кассиру
Ключ можно привязать к конкретному кассиру Kaspi. Тогда:
- боевые счета с этим ключом идут только через этого кассира;
- ключ вообще не видит счета других кассиров — по ним вернётся
invoice_not_found; - если привязать к ключу ещё и адрес вебхука, каждый проект получит только свои события.
Если в организации несколько точек или несколько проектов, это самый чистый способ их разделить: Можно ли подключить несколько кассиров.
Кассира, к которому привязан ключ, удалить нельзя: сначала переназначьте ключ на другого кассира, иначе придёт connection_has_keys.
Счета песочницы к кассиру не привязываются — это общие тестовые данные организации.
Ротация без простоя
Ключ полезно периодически менять: уволился разработчик, ключ где-то засветился или просто пришло время по плану.
Порядок замены без остановки сервиса:
- Создайте новый ключ. Старый пока не удаляйте — они спокойно работают параллельно.
- Выдайте новому те же права и того же кассира.
- Замените переменную окружения на сервере и перезапустите приложение.
- Проверьте: создайте счёт в песочнице или один небольшой боевой счёт.
- Понаблюдайте несколько часов — не осталось ли забытого места, которое ещё ходит со старым ключом.
- Удалите старый ключ.
В аварийной ситуации (ключ утёк) порядок обратный: сначала удаляете старый, потом ставите новый. Несколько минут простоя будут, но это безопаснее, чем позволить постороннему выставлять счета от вашего имени.
Удаление
Удаление ключа действует немедленно. Никакой отсрочки и «мягкого отключения» нет.
| Что происходит | Пояснение |
|---|---|
| Запросы | Все запросы с этим ключом получают unauthorized (401) |
| Ранее созданные счета | Не удаляются, видны в кабинете, продолжают оплачиваться |
| Вебхуки | События по прежним счетам продолжают приходить |
| Подписки | Расписание не ломается, они привязаны к кассиру |
То есть теряется только доступ. Восстановить удалённый ключ невозможно: создаёте новый и обновляете интеграции: Удалил API-ключ.
Связанные ошибки
| Код | HTTP | Причина |
|---|---|---|
unauthorized | 401 | Ключ не передан, недействителен или удалён |
invalid_api_key | 422 | Неверный формат ключа |
insufficient_scope | 403 | У ключа нет права на это действие |
forbidden | 403 | Ресурс принадлежит другой организации |
invoice_not_found | 404 | Счёт не принадлежит кассиру, к которому привязан ключ |
Вопросы и ответы
Можно ли посмотреть ключ повторно? Нет. Он показывается один раз. Потеряли — создаёте новый.
Сколько ключей может быть у организации? Несколько. Заводить отдельный ключ на каждую интеграцию ничто не мешает.
Истекает ли ключ сам? Нет. Он действует, пока вы его не удалите или не замените.
Что будет, если использовать ключ песочницы в проде? Счета создадутся, но в Kaspi не уйдут — покупатель никогда не сможет оплатить. Это самая частая причина жалоб «оплата не приходит».
Можно ли дать ключ подрядчику? Создайте ему отдельный ключ с минимальными правами. По окончании работ удалите именно его, не трогая остальные интеграции.