Сначала проверьте
- Счета создаются прямо сейчас? Не разбирайтесь «на живую» — сначала kill-switch, разбор потом.
- Передаёте
external_order_id_idempotency? Если нет — любой ретрай даёт второй живой счёт. - Есть ли авторетраи на вашей стороне? Таймаут ≠ неуспех: счёт мог создаться, а ваш код, не дождавшись ответа, послал запрос ещё раз.
Экстренная остановка (kill-switch)
Полный протокол — у этой ситуации нет «мягкого» решения, действуйте по шагам:
- Откройте кабинет apipay.kz → Настройки → вкладка «Подключение».
- Удалите API-ключ(и), которыми пользуется сбоящая интеграция. Это мгновенно отключает её: счета перестанут выставляться. Безопасно: созданные счета, оплаты и деньги не затрагиваются. Учтите два последствия: вместе с ключом перестанут приходить вебхуки на привязанный к нему адрес — событие получит только ключ организации по умолчанию, поэтому пока интеграция выключена, проверяйте оплаты в разделе «Счета» или запросом
GET /invoices/{id}; удалённый ключ и его секрет подписи не восстанавливаются — после починки выпускается новый ключ с новым секретом, и его нужно прописать во всех местах. - Отмените лишние неоплаченные счета — в кабинете (раздел «Счета») или напишите в поддержку: с массовой отменой поможем быстрее.
- Найдите цикл у себя: триггер CRM, срабатывающий по кругу; ретрай без ограничения попыток; вебхук, который сам создаёт новый счёт.
- Внедрите идемпотентность: передавайте
external_order_id_idempotency(уникальный на заказ, ≤191 символ) — повторный запрос получит409 duplicate_idempotency_keyвместо нового счёта. - Выпустите новый ключ, пропишите вебхук и включайте интеграцию обратно.
Ветки диагностики
| Признак | Причина | Что сделать | Подробнее |
|---|---|---|---|
| Покупатель получил два пуша и оплатил оба | Два POST без ключа идемпотентности (ретрай кода) | Вернуть лишнюю оплату возвратом; внедрить external_order_id_idempotency |
«Как не выставить два счёта за один заказ?» в «Создании счёта по номеру»; возврат — «Возвраты» |
| Счета льются потоком без вашего участия | Зацикленная CRM/no-code-интеграция | Kill-switch выше, затем разбор триггеров | — (протокол в этой статье) |
| Дубль после «зависшего» счёта | Пересоздали счёт в processing |
Счёт в processing не пересоздавать, дождаться вебхука |
«Жизненный цикл счёта» |
Получаете 409 duplicate_idempotency_key |
Счёт с этим ключом уже существует | Взять invoice_id и status прежнего счёта из 409-ответа, новый не создавать |
Создание счёта по номеру |
Для «мёртвых» статусов (expired, cancelled, error) повторный POST с тем же ключом создаёт новый счёт — это штатное перевыставление, а не сбой защиты.
Частые вопросы
Покупатель оплатил оба дубля — что делать?
Верните одну из оплат возвратом (кабинет или POST /invoices/{id}/refund), а в интеграцию добавьте идемпотентность, чтобы не повторилось.
Мой order_id длиннее 191 символа — как быть?
Передавайте хэш (например md5) от вашей строки заказа — идемпотентности важна уникальность, а не читаемость.