Сохраняйте правильные идентификаторы
| Идентификатор | Назначение | Повтор |
|---|---|---|
| external_id | Создание одной подписки | То же значение и исходные параметры создания |
| UUID подписки | Получение, изменение, отмена и запуск попытки списания | Берётся из объекта подписки Cashera |
| Idempotency-Key | Один update или одна внеочередная попытка | Тот же ключ, подписка, действие и JSON |
| UUID транзакции | Результат конкретного списания | Берётся из charge-now, истории или webhook |
Не подставляйте ID провайдера в URL Cashera. Не используйте UUID подписки как UUID каждого нового платежа. API-ключ хранится только на вашем сервере, не в браузере плательщика и не в публичном примере.
До первого запроса
- Создайте запись операции в вашей БД с уникальной связью с подпиской и бизнес-действием
- Сохраните исходный payload и ключ до внешнего HTTP, а не после ответа
- Повтор из очереди или второй обработчик события должен загрузить эту запись
- Не вычисляйте новый ключ из текущего времени при каждой попытке
Безопасность повторов зависит от ключа: разные ключи означают разные операции. Блокировка кнопки в браузере не заменяет серверное ограничение дублей.
Что делать с HTTP-ответом
| Ответ | Что означает | Действие |
|---|---|---|
| 200 / 201 создания | Возвращён объект новой или существующей подписки; HTTP-код не подтверждает оплату | При pending_agreement открыть непустой confirmation_url. При active/past_due проверить историю, не повторять привязку |
| 200 update | Новые условия подтверждены | Сохранить объект подписки; доступ не продлевать как за оплату |
| 202 charge-now | Попытка принята, либо возвращена та же попытка при повторе | Сохранить UUID транзакции, проверить её status и дождаться paid |
| 400 | Провайдер отклонил запрос | Проверить message и состояние. Если ранее был тайм-аут, не считать исходную операцию гарантированно неисполненной |
| 401 | Нет действующей авторизации Cashera | Проверить X-Api-Key. Не делать бесконечных повторов |
| 403 | Действие запрещено для мерчанта | Проверить включение кассы, заморозку и доступ к методу |
| 404 | Объект не найден или не принадлежит ключу | Проверить UUID и владельца. Для нового метода также уточнить, обновлена ли версия сервера |
| 409 | Конфликт параметров ключа, состояние не подходит, выполняется другая операция или удерживается блокировка | Прочитать message. Для той же незавершённой операции повторять исходный запрос с паузой, не менять ключ |
| 422 | Ошибка параметров или финансовых ограничений | Проверить JSON-типы, копейки, дату и пару interval/interval_count. При неясном результате предыдущей попытки сначала сверить её |
| 429 | Превышена допустимая частота запросов | Снизить частоту, учесть Retry-After при его наличии, сохранить исходный ключ |
| 5xx / тайм-аут / обрыв связи | Результат может быть неизвестен; запрос мог исполниться | Проверить локальное состояние и повторить ту же операцию ограниченно, без нового ключа |
{"message": "A subscription operation is still unresolved. Reuse its original idempotency key."}
Ошибки содержат message, а ошибки валидации могут дополнительно содержать errors по полям. Не рассчитывайте на полеtraceId провайдера в публичном ответе Cashera и не используйте точный текст сообщения как постоянный машинный код.
Создание завершилось тайм-аутом или без ссылки
- Получите подписку по исходному external_id
- Если
pending_agreementи ссылка непустая, используйте её; не создавайте новую подписку - Если
activeилиpast_due, не запускайте привязку повторно, проверьте историю платежей - Если
pending_agreementи ссылка отсутствует, ограниченно повторите тот же POST создания с исходными параметрами - Если
creation_unknown, автоматическая замена запрещена: обратитесь в поддержку с UUID и external_id - Если
failedилиcancelled, повтор создания не возобновляет эту подписку
GET читает сохранённое состояние и сам не запрашивает новую ссылку.all_providers_unavailable: false не гарантирует наличие ссылки: проверяйте именно confirmation_url.
Изменение или списание завершилось неопределённо
- Не помечайте операцию успешной и не выдавайте доступ только по факту отправки
- Для update запросите подписку; для charge-now, если UUID платежа уже известен, запросите этот платёж
- Повторите тот же POST с сохранённым Idempotency-Key и исходным JSON
- Используйте задержки с увеличением и случайным разбросом, ограничьте число автоматических попыток
- Если ошибка сохраняется, остановите автоматические запросы и обратитесь в поддержку; запись операции и ключ сохраните
Cashera сверяет незавершённые операции в фоне. Даже если GET показывает старые условия, предыдущий update мог уже примениться у провайдера, но ещё не подтвердиться локально. Не обходите это новым ключом. Здесь нет публичного метода «сбросить операцию» или «принудительно зачислить».
Успешный HTTP-код с отсутствующим, неполным или неразбираемым JSON также не подтверждает результат. Сохраните код и ключ, проверьте состояние и ограниченно повторите исходный запрос. Не подставляйте вымышленные значения суммы, UUID или статуса вместо отсутствующих полей.
Сначала установите результат исходной попытки. Полученный после тайм-аута отказ тоже не всегда доказывает, что исходная попытка не исполнилась. Пока результат не выяснен, не создавайте замену, даже если клиент повторно нажал кнопку.
Списание не удалось, но подписка не отменена
Статус платежа и статус подписки независимы. canceled илиfailed у одной транзакции не равен cancelled у подписки.past_due означает проблему со списанием и не является финальным статусом подписки. Смотрите next_charge_at и историю платежей.
Если next_charge_at равен null, не показывайте выдуманную дату повторной попытки. API не гарантирует отмену через N дней или фиксированный график повторов при отказе. Для прекращения подписки используйте отмену, для осознанной новой попытки после проверки предыдущей используйте charge-now.
Что передать в поддержку
- UUID подписки, её external_id и UUID платежа, если он известен
- HTTP-метод и путь запроса, время с часовым поясом, код ответа и message
- Ключ операции и отправленные параметры без API-ключей, секретов и данных плательщика
- Последовательность попыток: был ли тайм-аут, какие ответы пришли после него
Никогда не присылайте X-Api-Key, X-Secret, пароли, банковские коды или полную ссылку подтверждения. Для поиска достаточно идентификаторов.
