Рекуррентные платежи

Создание подписки

Создаёт подписку и возвращает ссылку, на которой клиент подтверждает автоматические списания

POST/integration/subscriptions

Подписки создаются только в RUB. Все примеры выполняются с вашего сервера с API-ключом мерчанта, которому подключён метод sbp_recurring. Комиссии в примерах ответов иллюстративные: при 5% за счёт мерчанта платёж 100 ₽ даёт 95 ₽ после комиссии. Для вашей кассы используйте суммы ответа.

Параметры запроса

ПолеТипОписание
agreement_modestringh2h по умолчанию или hosted. В обоих режимах клиент открывает confirmation_url
emailstringEmail плательщика до 254 символов. Обязателен в h2h; при hosted можно не передавать. Переданный непустой email должен быть корректным
amountобязательноеintegerБазовая сумма каждого списания в копейках, от 1 до 9007199254740991. Например, 1999 означает 19,99 ₽. Фактическая сумма ограничивается настройками метода, комиссиями и холдом. Передавайте JSON-число, не строку
external_idобязательноеstringВаш уникальный ID подписки, до 255 символов. Разрешены латинские буквы, цифры, точка, дефис и подчёркивание
intervalобязательноеstringdaily, weekly, monthly или yearly
interval_countintegerКоличество единиц периода между списаниями. По умолчанию 1. Для daily: 1–31, weekly: 1–4, monthly: 1–12, yearly: 1–3. Передавайте целое JSON-число, не строку
descriptionобязательноеstringНазначение регулярного платежа, до 255 символов
callback_urlstring (https)URL для вебхуков. Если поле не передано, используется callback URL из настроек мерчанта. URL должен быть публичным и использовать HTTPS

Пример

201 Created
curl -X POST https://api.cashera.cash/api/v1/integration/subscriptions \
-H "X-Api-Key: $CASHERA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 10000,
"external_id": "subscription-pro-10428",
"interval": "monthly",
"interval_count": 1,
"description": "Тариф Pro",
"email": "customer@example.com"
}'

Два способа подключения

h2h сохраняет существующий процесс с email плательщика.hosted направляет клиента на страницу подтверждения провайдера; email в запросе необязателен. В обоих случаях открывайте только возвращённыйconfirmation_url, не собирайте ссылку самостоятельно и не запрашивайте у клиента банковские коды на вашем сервере.

201 Created
curl -X POST https://api.cashera.cash/api/v1/integration/subscriptions \
-H "X-Api-Key: $CASHERA_API_KEY" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"agreement_mode": "hosted",
"amount": 1999,
"external_id": "subscription-hosted-10428",
"interval": "monthly",
"interval_count": 1,
"description": "Тариф 19,99 ₽ в месяц"
}'

До выдачи ссылки Cashera подтверждает финансовые параметры у провайдера. В обоих режимах сумма клиента gross_amount может превышать базовуюamount из-за комиссии. Не вычисляйте её на клиенте из фиксированного процента: показывайте значения ответа. Email в ответ не включается.

Подписки на 3, 6, 9 и 24 месяца

Отправляйте эти тела на тот же POST /integration/subscriptions с тем же заголовком X-Api-Key. Меняются только параметры периода и ваш уникальный external_id. Сумма 29900 — 299 ₽ за одно списание; interval_count не умножает сумму или комиссию автоматически.

{
"amount": 29900,
"external_id": "subscription-3-months-10428",
"interval": "monthly",
"interval_count": 3,
"description": "Доступ на 3 месяца",
"email": "customer@example.com"
}
Существующая интеграция сохраняется

Если interval_count не передан, используется 1: старый запрос с monthly по-прежнему создаёт ежемесячную подписку. Для 24 месяцев используйте yearly и 2; сочетание monthly и 24 вернёт 422. Расписание определяет провайдер; период не означает отсрочку первого списания.

Обязательно откройте confirmation_url

Для новой подписки ответ 201 ещё не означает, что она активна. Перенаправьте клиента наconfirmation_url и дождитесь статуса active через вебхукили запрос состояния.

При повторе создания уже действующей подписки может вернуться 200со статусом active или past_due и сохранённой ссылкой. Это та же подписка, а не новая привязка. Не просите клиента подтверждать её повторно: проверьте состояние и историю платежей. Сам повтор создания не является новым списанием.

Ссылка подтверждения

Поле confirmation_url имеет тип string | null. В ответе подписки нет поля payment_url. Для cancelled и failed ссылка равна null. Email не возвращается в ответе.

Если ссылка пока недоступна

Ответ 200 OK с confirmation_url: null и all_providers_unavailable: true означает, что провайдеры подтверждения недоступны. Повторите тот же запрос с тем же external_id и параметрами. При наличии сохранённой ссылки повтор вернёт её, а не создаст новую.

Точный срок действия ссылки API не возвращает. Открывайте её сразу после получения. Ошибка банка при открытии не означает успешной привязки или оплаты.

Ошибка получения ссылки подтверждения

Запись подписки, подтверждение её финансовых параметров и получение ссылки выполняются последовательно. Ошибка внешнего запроса может прийти после сохранения подписки, поэтому ответ 5xx не означает, что подписка не создана.

503 Service Unavailable
{
"message": "Subscription status lookup failed."
}
  1. Запросите подписку через GET по external_id. Ответ с ошибкой содержит только message, а не объект подписки.
  2. pending_agreement с confirmation_url: null означает, что подтверждение ещё не завершено и доступной ссылки нет. Это не доказательство оплаты или окончательного отказа. Не перенаправляйте клиента на пустую ссылку.
  3. Не создавайте замену с новым external_id и не запускайте бесконечные повторы. Повтор исходного POST продолжает незавершённый этап: проверку параметров или получение ссылки h2h. Hosted не запускает h2h-подтверждение. GET только читает сохранённое состояние. При повторяющихся ошибках передайте поддержке UUID, external_id и время ошибки с часовым поясом.

all_providers_unavailable: false не доказывает, что ссылка получена: при ошибке запроса это поле может остаться false. Для открытия подтверждения нужна непустая confirmation_url. Статусы failed и cancelledокончательные — повторный POST их не возобновляет.

Идемпотентность

  • первый успешный запрос возвращает 201 Created
  • повтор с тем же external_id и теми же параметрами использует существующую подписку. При успешной обработке ответ — 200 OK; если ссылка ещё не получена, повтор может снова завершиться ошибкой провайдера
  • тот же external_id с другим режимом подключения, email, суммой, интервалом, количеством периодов (interval_count), описанием или callback URL возвращает 409 Conflict

Отсутствующий interval_count и явно переданный 1 эквивалентны при повторе. Период существующей подписки изменить повторным POST нельзя.

После смены тарифа повтор создания по-прежнему должен содержать первоначальные параметры, а не новую цену. Для изменения используйте отдельный endpoint update иIdempotency-Key. Полный сценарий перехода с пробной оплаты есть на странице «Триал и смена тарифа».

Не повторяйте неоднозначную операцию новым external_id

При статусе creation_unknown провайдер мог создать подписку, даже если Cashera не получила окончательный ответ. Не создавайте замену автоматически: повторите запрос с тем же external_idи, если статус не изменился, обратитесь в поддержку.

Ошибки

ПолеТипОписание
400Bad RequestПровайдер отклонил запрос. Явный отказ создания сохраняется как failed. При получении ссылки h2h финальный статус устанавливается только для распознанной отмены или истечения окна согласия. Другой 400 на этом этапе не доказывает отмену; получите текущее состояние подписки
401UnauthorizedОтсутствует или неверен X-Api-Key
403ForbiddenМерчант отключён, заморожен или не может создавать подписки
409ConflictКонфликт параметров для существующего external_id
422Unprocessable EntityНет обязательного email в h2h либо передан некорректный email, ошибка других полей, небезопасный callback URL, неподдерживаемый интервал, некорректный interval_count (null, строка, дробь или выход за пределы выбранного периода), неподходящая итоговая сумма или неверная конфигурация метода
500Internal Server ErrorВ том числе ошибка провайдера при получении ссылки подтверждения: Subscription agreement request failed. Проверьте подписку по тому же external_id; она могла сохраниться до ошибки
502Bad GatewayПровайдер не дал корректный однозначный ответ. Получите подписку GET-запросом по тому же external_id