1. API-ключ — только на сервере
Зачем. X-API-Key даёт право создавать счета от имени вашей организации, а при включённом флаге — управлять кассирами. Попав в браузер, публичный репозиторий или клиентскую часть Telegram-бота, ключ становится доступен любому.
Как проверить. Прогоните git grep по значению ключа во всей истории репозитория; убедитесь, что .env в .gitignore; откройте DevTools на своём сайте и проверьте, что в сетевых запросах фронтенда нет заголовка X-API-Key — запрос в ApiPay должен уходить с вашего бэкенда, а не из браузера покупателя.
2. Проверяйте подпись вебхука по сырому телу
Зачем. Без проверки подписи любой, кто узнал ваш webhook URL, пришлёт фейковое «счёт оплачен». Подпись — единственная гарантия, что уведомление действительно от ApiPay.
Как проверить. Подпись считается так: 'sha256=' . hash_hmac('sha256', <сырое тело запроса>, webhook_secret). Ключевое слово — сырое: считайте HMAC до того, как фреймворк распарсит и пересоберёт JSON (перестановка полей или пробелы изменят байты и сломают сверку). Нажмите в кабинете «Проверить уведомления» на карточке ключа — придёт событие webhook.test с боевой подписью; убедитесь, что ваш обработчик её принял.
3. Не путайте API-ключ и секрет подписи
Зачем. Это две разные строки с разными ролями, и их регулярно путают. API-ключ (X-API-Key) — в заголовке ваших исходящих запросов к ApiPay. Секрет подписи вебхука (webhook secret) — для проверки входящих уведомлений (X-Webhook-Signature). Ключ наружу не показывают; секрет наружу тоже не показывают — но перепутать их местами означает, что ни аутентификация, ни проверка подписи не сработают. Подробный разбор — в статье «API-ключ и секрет подписи вебхука».
4. Защититесь от дублей идемпотентностью
Зачем. Сетевой сбой или ретрай на вашей стороне не должен превращаться во второй счёт покупателю. Передавайте external_order_id_idempotency (до 191 символа, уникален в пределах организации) — повтор с тем же ключом вернёт 409 с invoice_id и статусом ранее созданного счёта, а не создаст новый.
Как проверить. Отправьте один и тот же запрос дважды подряд: первый — 201, второй — 409 с прежним invoice_id. Гонка двух параллельных запросов тоже безопасна — её разрешает уникальный индекс в базе.
5. При утечке ключа — «Сменить ключ» (секрет при этом НЕ меняется)
Зачем. Если ключ мог утечь — не ждите. В кабинете: Настройки → Подключение → карточка ключа → меню «Ещё» → «Сменить ключ». Старый ключ мгновенно перестаёт работать. Важно: смена ключа не трогает секрет подписи вебхука — это отдельная запись. Если у вас есть основания думать, что утёк и секрет, смените его отдельно: в том же меню «Ещё» — «Сменить секретный ключ подписи» (кнопка появляется только когда у ключа задан адрес уведомлений).
Как проверить. После смены ключа убедитесь, что старый возвращает 401, а новый работает; после смены секрета — что ваш обработчик пересчитывает подпись новым значением.
6. Ключ и секрет показываются только один раз
Зачем. На финальном экране создания ключа кабинет прямо предупреждает: «API-ключ и секретный ключ показываются только один раз». Позже в карточке виден лишь хвост — ****key_hint. Восстановить полное значение нельзя, только перегенерировать.
Как проверить. Сразу при создании нажмите «Скопировать всё для ИИ-помощника» или сохраните ключ и секрет в секрет-менеджер (Vault, GitHub Actions Secrets, переменные окружения хостинга) — не в заметки и не в код.
7. Ревизуйте доступы к кабинету
Зачем. В организацию можно пригласить до 5 менеджеров. Уволенный сотрудник с доступом — это открытая дверь.
Как проверить. Раз в квартал открывайте Настройки → Менеджеры и убирайте тех, кто больше не должен иметь доступ.
8. Sandbox-ключи не работают в проде
Зачем. Ключ жёстко привязан к организации. Тестовая организация «архитектурно» не может стать боевой, а при переходе в рабочий режим тестовые организации и их ключи удаляются. Значит песочный ключ в продакшене просто не аутентифицируется.
Как проверить. В прод-конфиге должен лежать ключ боевой организации, а не тот, что вы получили в песочнице. Держите их в разных переменных окружения. Подробнее — «Песочница и рабочий режим».
9. Не логируйте полные ключи
Зачем. Логи утекают чаще, чем базы. Полный ключ в логе приложения = утечка.
Как проверить. В своих логах маскируйте секреты до последних 4 символов (****key_hint — ровно то, что показывает кабинет). Настройте фильтр логгера, который вырезает заголовки X-API-Key и X-Webhook-Signature.
10. Доверяйте подписи, а не IP
Зачем. Webhook URL должен быть HTTPS — приватные и внутренние адреса ApiPay отклоняет с 422 (защита от SSRF). Но подлинность отправителя гарантирует подпись, а не IP-адрес.
Как проверить. Не фильтруйте вебхуки по «белому списку IP» — фиксированный список адресов мы не публикуем. Единственная надёжная проверка — HMAC-подпись из пункта 2.
11. Ограничьте право ключа управлять кассирами
Зачем. У API-ключа есть флаг «управление кассирами». По умолчанию он выключен, и включить его может только владелец. Иначе утёкший ключ смог бы отключить кассира и остановить приём платежей.
Как проверить. Оставляйте флаг выключенным для всех ключей, которым это не нужно (обычные интеграции создания счетов в нём не нуждаются).
12. Ротируйте по расписанию и при смене команды
Зачем. Даже неутёкший ключ стоит менять периодически и обязательно — при уходе разработчика, у которого он мог остаться.
Как проверить. Заведите регламент: плановая ротация ключей и секретов, внеплановая — при любом кадровом или инфраструктурном изменении. Механика — пункт 5.
Частые ошибки
- Секрет подписи не проверяют вообще — обработчик принимает любой POST. Первым делом включите проверку HMAC (пункт 2).
- Считают подпись по пересобранному JSON, а не по raw body — сверка всегда падает.
- Меняют ключ и ждут, что сменится секрет — это разные кнопки (пункт 5).
- Хардкодят sandbox-ключ, потом удивляются 401 в проде (пункт 8).
- Отдают ключ фронтенду ради «простоты» — и раздают всем посетителям сайта.
Вопросы и ответы
Смена API-ключа меняет секрет подписи вебхука?
Нет. Это две разные операции над записью ключа: «Сменить ключ» меняет только сам ключ, «Сменить секретный ключ подписи» — только секрет. Вебхук продолжает работать со старым секретом, пока вы не смените его отдельно.
Как проверить подпись вебхука?
Посчитайте hash_hmac('sha256', сырое_тело_запроса, webhook_secret), добавьте префикс sha256= и сравните с заголовком X-Webhook-Signature. Сравнивайте по сырому телу до парсинга JSON.
Что делать, если API-ключ утёк?
В кабинете: карточка ключа → «Ещё» → «Сменить ключ». Старый умрёт сразу. Если мог утечь и секрет подписи — смените его отдельной кнопкой.
Можно ли восстановить потерянный ключ или секрет?
Нет, они показываются один раз при создании. Потеряли — перегенерируйте (это сделает старое значение недействительным).
Можно ли ограничить вебхуки по IP отправителя?
Не рекомендуем: фиксированный список IP мы не публикуем. Полагайтесь на проверку подписи — это надёжнее.
Видит ли ApiPay мои деньги и берёт ли процент с оплат?
Нет. Деньги идут напрямую на ваш Kaspi-счёт, ApiPay доступа к ним не имеет; тариф — фиксированная подписка без процента с оборота. Kaspi удерживает свою обычную комиссию за приём платежа (~0,95%) — она не связана с ApiPay и действует при любом способе приёма через Kaspi Pay.