# API-ключи — создание, хранение, ротация

> Чем отличаются ключи qp_live_ и qp_test_, как создать ключ в кабинете, где его хранить и где хранить категорически нельзя, зачем отдельный ключ на каждую интеграцию, как заменить ключ без простоя и что происходит при удалении.

## Коротко

API-ключ — секретная строка, которая передаётся в заголовке `X-API-Key`. По нему API узнаёт вашу организацию.

```http
POST /api/v1/invoices
X-API-Key: qp_live_a1b2c3d4…
```

Ключи бывают двух видов: **`qp_live_…`** работает с настоящими деньгами, **`qp_test_…`** — в песочнице. Ключ создаётся в кабинете и показывается **только один раз** — если не скопировали сразу, посмотреть его заново нельзя, придётся создавать новый.

Главное правило: ключ должен жить **только на сервере**.

## Виды ключей

| Префикс | Режим | Что происходит |
|---|---|---|
| `qp_test_` | Песочница | Kaspi не вызывается, настоящих денег нет, оплату вы симулируете сами |
| `qp_live_` | Боевой | Настоящий Kaspi QR, настоящие деньги, нужен кассир Kaspi |

Режимы не смешиваются: ключом песочницы боевые счета не увидеть, и наоборот. Путаница с режимом — самая частая причина «у меня всё работало, а в проде перестало»: [Чем песочница отличается от боевого режима](/kb/ru/sandbox-vs-live).

При неверном формате ключа приходит `invalid_api_key`, при недействительном или удалённом — `unauthorized`: [API отвечает 401](/kb/ru/api-401).

## Создание

Кабинет → **Интеграции** → API-ключи → создать ключ. При создании вы решаете три вещи:

| Что | Пояснение |
|---|---|
| Название | Для себя: «Сайт», «Telegram-бот», «1С». Потом по нему поймёте, кто что делает |
| Права (scopes) | Включайте только нужные: [Права доступа (scopes)](/kb/ru/scopes) |
| Кассир | При желании ключ привязывается к конкретному кассиру |

Ключ показывается на экране **один раз**. Скопируйте его тут же и сразу положите в хранилище секретов или в `.env` на сервере. Дальше в кабинете видно только название и последние символы.

## Где хранить

| Место | Можно |
|---|---|
| Файл `.env` на сервере | Да |
| Секреты хостинга или CI | Да |
| Хранилище секретов вроде Vault | Да |
| JavaScript, выполняемый в браузере | **Нет** |
| Внутри мобильного приложения (APK/IPA) | **Нет** |
| Публичный репозиторий, история git | **Нет** |
| Скриншот, чат, трекер задач | **Нет** |
| Строка прямо в коде | **Нет** |

Причина простая: ключ, попавший в браузер или в приложение, может достать любой желающий. В мобильном приложении порядок должен быть таким: **приложение → ваш сервер → Qut Pay → Kaspi**.

Не забудьте добавить `.env` в `.gitignore`, а в коде читать переменную окружения:

```js
// правильно
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`;
- если привязать к ключу ещё и адрес вебхука, каждый проект получит только свои события.

Если в организации несколько точек или несколько проектов, это самый чистый способ их разделить: [Можно ли подключить несколько кассиров](/kb/ru/two-cashiers).

Кассира, к которому привязан ключ, удалить нельзя: сначала переназначьте ключ на другого кассира, иначе придёт `connection_has_keys`.

Счета песочницы к кассиру не привязываются — это общие тестовые данные организации.

## Ротация без простоя

Ключ полезно периодически менять: уволился разработчик, ключ где-то засветился или просто пришло время по плану.

Порядок замены без остановки сервиса:

1. **Создайте новый ключ.** Старый пока не удаляйте — они спокойно работают параллельно.
2. **Выдайте новому те же права и того же кассира.**
3. **Замените переменную окружения на сервере** и перезапустите приложение.
4. **Проверьте:** создайте счёт в песочнице или один небольшой боевой счёт.
5. Понаблюдайте несколько часов — не осталось ли забытого места, которое ещё ходит со старым ключом.
6. **Удалите старый ключ.**

В аварийной ситуации (ключ утёк) порядок обратный: сначала удаляете старый, потом ставите новый. Несколько минут простоя будут, но это безопаснее, чем позволить постороннему выставлять счета от вашего имени.

## Удаление

Удаление ключа действует **немедленно**. Никакой отсрочки и «мягкого отключения» нет.

| Что происходит | Пояснение |
|---|---|
| Запросы | Все запросы с этим ключом получают `unauthorized` (401) |
| Ранее созданные счета | Не удаляются, видны в кабинете, продолжают оплачиваться |
| Вебхуки | События по прежним счетам продолжают приходить |
| Подписки | Расписание не ломается, они привязаны к кассиру |

То есть теряется только доступ. Восстановить удалённый ключ невозможно: создаёте новый и обновляете интеграции: [Удалил API-ключ](/kb/ru/deleted-key).

## Связанные ошибки

| Код | HTTP | Причина |
|---|---|---|
| `unauthorized` | 401 | Ключ не передан, недействителен или удалён |
| `invalid_api_key` | 422 | Неверный формат ключа |
| `insufficient_scope` | 403 | У ключа нет права на это действие |
| `forbidden` | 403 | Ресурс принадлежит другой организации |
| `invoice_not_found` | 404 | Счёт не принадлежит кассиру, к которому привязан ключ |

## Вопросы и ответы

**Можно ли посмотреть ключ повторно?** Нет. Он показывается один раз. Потеряли — создаёте новый.

**Сколько ключей может быть у организации?** Несколько. Заводить отдельный ключ на каждую интеграцию ничто не мешает.

**Истекает ли ключ сам?** Нет. Он действует, пока вы его не удалите или не замените.

**Что будет, если использовать ключ песочницы в проде?** Счета создадутся, но в Kaspi не уйдут — покупатель никогда не сможет оплатить. Это самая частая причина жалоб «оплата не приходит».

**Можно ли дать ключ подрядчику?** Создайте ему отдельный ключ с минимальными правами. По окончании работ удалите именно его, не трогая остальные интеграции.
