Обзор
Система рекуррентных платежей позволяет автоматически продлевать подписки без участия пользователя. Состоит из двух компонентов:
- Автопродление (autopay) — автоматическое списание с баланса при приближении окончания подписки
- Сохранённые карты — автоматическое пополнение баланса через YooKassa, если средств на балансе недостаточно
Подписка истекает через N дней
↓
Баланс достаточен?
├─ Да → Списание с баланса → Продление подписки
└─ Нет → Есть сохранённая карта?
├─ Да → Списание с карты → Пополнение баланса → Продление
└─ Нет → Уведомление «Недостаточно средств»
Для полноценной работы необходимо включить оба компонента:
ENABLE_AUTOPAY=true — автопродление с баланса
YOOKASSA_RECURRENT_ENABLED=true — сохранение и использование карт
Без ENABLE_AUTOPAY сохранённые карты не будут использоваться автоматически. Без YOOKASSA_RECURRENT_ENABLED продление возможно только при достаточном балансе.
Автопродление (с баланса)
Как работает
Мониторинг-сервис ежедневно проверяет подписки с включённым автопродлением:
- Находит подписки, где
autopay_enabled=true и до окончания осталось ≤ autopay_days_before дней
- Рассчитывает стоимость продления (с учётом промо-скидок)
- Если баланс достаточен — списывает и продлевает подписку
- Если баланс недостаточен — пытается списать с сохранённой карты (при наличии)
- Отправляет уведомление о результате
Пробные (trial) и суточные подписки исключены из автопродления. Также обрабатываются подписки, истекшие не более 48 часов назад.
Настройка
ENABLE_AUTOPAY=true
DEFAULT_AUTOPAY_ENABLED=true
DEFAULT_AUTOPAY_DAYS_BEFORE=3
MIN_BALANCE_FOR_AUTOPAY_KOPEKS=10000
| Переменная | Тип | По умолчанию | Описание |
|---|
ENABLE_AUTOPAY | bool | false | Глобальное включение автопродления |
DEFAULT_AUTOPAY_ENABLED | bool | false | Автопродление включено у новых пользователей по умолчанию |
DEFAULT_AUTOPAY_DAYS_BEFORE | int | 3 | За сколько дней до окончания подписки запускать продление |
MIN_BALANCE_FOR_AUTOPAY_KOPEKS | int | 10000 | Минимальный баланс для срабатывания (100 руб) |
SUBSCRIPTION_RENEWAL_BALANCE_THRESHOLD_KOPEKS | int | 20000 | Порог баланса для автопродления (200 руб) |
Интерфейс пользователя
В боте
Пользователь управляет автопродлением через меню подписки:
| Кнопка | Действие |
|---|
| Включить/Выключить | Переключение autopay_enabled |
| Настроить дни | Выбор за сколько дней до окончания списывать |
Отображается текущий статус: включено/выключено и количество дней.
В Cabinet
PATCH /cabinet/subscription/autopay
Запрос:
{
"enabled": true,
"days_before": 3
}
Автопродление недоступно для суточных тарифов. При попытке включить на суточном тарифе API вернёт ошибку.
Сохранённые карты (YooKassa)
Как сохраняются карты
При оплате через YooKassa карта сохраняется автоматически:
- Пользователь создаёт платёж через YooKassa
- Если
YOOKASSA_RECURRENT_REQUIRED=true — карта сохраняется принудительно
- Если
YOOKASSA_RECURRENT_REQUIRED=false — YooKassa показывает чекбокс «Сохранить карту»
- После успешной оплаты webhook возвращает
payment_method.saved=true
- Бот сохраняет данные карты в таблицу
saved_payment_methods
Пользователь YooKassa Бот
│ │ │
│ 1. Оплата │ │
│───────────────────────>│ │
│ │ │
│ 2. Чекбокс │ │
│ «Сохранить карту» │ │
│ (или принудительно) │ │
│───────────────────────>│ │
│ │ │
│ │ 3. Webhook │
│ │ payment_method.saved │
│ │──────────────────────────>│
│ │ │ 4. Сохранение
│ │ │ в БД
│ │ │
│ 5. Карта сохранена │ │
│<──────────────────────────────────────────────────│
Что сохраняется
| Поле | Описание |
|---|
yookassa_payment_method_id | Токен YooKassa для повторных списаний |
method_type | Тип: bank_card, yoo_money, sberbank и др. |
card_first6 | Первые 6 цифр (BIN) |
card_last4 | Последние 4 цифры |
card_type | Платёжная система: Visa, MasterCard, Mir |
card_expiry_month/year | Срок действия |
title | Отображаемое имя, напр. «Visa *4444» |
Дедупликация: если карта с таким payment_method_id уже существует, новая запись не создаётся. Если карта была ранее деактивирована — она реактивируется.
Настройка
YOOKASSA_RECURRENT_ENABLED=true
YOOKASSA_RECURRENT_REQUIRED=false
| Переменная | Тип | По умолчанию | Описание |
|---|
YOOKASSA_RECURRENT_ENABLED | bool | false | Включить сохранение карт и рекуррентные платежи |
YOOKASSA_RECURRENT_REQUIRED | bool | false | Принудительно сохранять карту при каждой оплате |
Управление картами (Cabinet API)
Список карт
GET /cabinet/balance/saved-cards
Ответ:
{
"cards": [
{
"id": 42,
"method_type": "bank_card",
"card_last4": "4444",
"card_type": "Visa",
"title": "Visa *4444",
"created_at": "2026-01-15T12:00:00Z"
}
],
"recurrent_enabled": true
}
Удаление карты
DELETE /cabinet/balance/saved-cards/{card_id}
Мягкое удаление — карта помечается как is_active=false. При повторной оплате той же картой она реактивируется автоматически.
Автоматическое списание с карты
Процесс
Если при автопродлении на балансе недостаточно средств и у пользователя есть сохранённые карты:
- Рассчитывается сумма для продления подписки
- Бот перебирает все активные карты пользователя
- Для каждой карты создаётся платёж через YooKassa API (без подтверждения пользователя)
- При успехе — баланс пополняется, подписка продлевается
- При неудаче — пробуется следующая карта
- Если все карты не сработали — отправляется уведомление
Мониторинг Recurrent Service YooKassa
│ │ │
│ 1. Баланс недостаточен │ │
│──────────────────────────>│ │
│ │ │
│ │ 2. Карта #1 │
│ │ create_autopayment() │
│ │───────────────────────>│
│ │ │
│ │ 3. Результат │
│ │<───────────────────────│
│ │ │
│ │ [Если ошибка] │
│ │ 4. Карта #2... │
│ │───────────────────────>│
│ │ │
│ 5. Результат │ │
│<──────────────────────────│ │
Каждый платёж использует ключ идемпотентности, что предотвращает повторные списания при сетевых ошибках.
Защита от дублирования
Сервис рекуррентных платежей выполняется не чаще одного раза в сутки (daily guard). Подписки, уже обработанные за текущий день, пропускаются.
Уведомления
Предупреждения об окончании подписки
Настраиваются через AUTOPAY_WARNING_DAYS (по умолчанию 3,1 — за 3 дня и за 1 день):
| Условие | Сообщение |
|---|
| Автопродление вкл. + карта сохранена | «Автоматическое списание с карты» |
| Автопродление вкл. + нет карты | «Подписка продлится автоматически» (с баланса) |
| Автопродление выкл. | «Не забудьте продлить вручную!» |
Результат автопродления
| Событие | Описание |
|---|
| Успешное продление с баланса | «Подписка продлена на N дней за X руб» |
| Успешное списание с карты | «Автоплатёж выполнен, баланс пополнен на X руб» |
| Недостаточно средств (нет карты) | «Не удалось списать X руб, баланс: Y руб» + кнопки |
| Все карты не сработали | «Не удалось списать ни с одной сохранённой карты» |
Уведомление о недостаточном балансе отправляется с cooldown 24 часа — повторное уведомление в течение суток не приходит.
Администрирование
Управление автопродлением пользователя
POST /cabinet/admin/users/{user_id}/subscription-action
{
"action": "toggle_autopay",
"autopay_enabled": true
}
Просмотр статистики
Сервис рекуррентных платежей возвращает статистику после каждого запуска:
| Метрика | Описание |
|---|
checked | Подписок проверено |
payments_created | Успешных автоплатежей |
insufficient_no_card | Недостаточно средств, нет карты |
all_cards_failed | Все карты не сработали |
already_processed | Уже обработано сегодня |
errors | Ошибки при обработке |
Полная настройка
Минимальная конфигурация
# Автопродление с баланса
ENABLE_AUTOPAY=true
# Рекуррентные платежи через YooKassa
YOOKASSA_ENABLED=true
YOOKASSA_SHOP_ID=123456
YOOKASSA_SECRET_KEY=live_xxxxxxxxxxxxx
YOOKASSA_RECURRENT_ENABLED=true
Расширенная конфигурация
# Автопродление
ENABLE_AUTOPAY=true
DEFAULT_AUTOPAY_ENABLED=true
DEFAULT_AUTOPAY_DAYS_BEFORE=3
MIN_BALANCE_FOR_AUTOPAY_KOPEKS=10000
SUBSCRIPTION_RENEWAL_BALANCE_THRESHOLD_KOPEKS=20000
AUTOPAY_WARNING_DAYS=3,1
# Рекуррентные платежи
YOOKASSA_RECURRENT_ENABLED=true
YOOKASSA_RECURRENT_REQUIRED=false
| Переменная | Тип | По умолчанию | Описание |
|---|
ENABLE_AUTOPAY | bool | false | Глобальное включение автопродления |
DEFAULT_AUTOPAY_ENABLED | bool | false | Автопродление для новых пользователей |
DEFAULT_AUTOPAY_DAYS_BEFORE | int | 3 | За сколько дней до окончания списывать |
MIN_BALANCE_FOR_AUTOPAY_KOPEKS | int | 10000 | Минимальный баланс для срабатывания (100 руб) |
SUBSCRIPTION_RENEWAL_BALANCE_THRESHOLD_KOPEKS | int | 20000 | Порог баланса для автопродления (200 руб) |
AUTOPAY_WARNING_DAYS | str | 3,1 | Дни для предупреждений (через запятую) |
YOOKASSA_RECURRENT_ENABLED | bool | false | Включить сохранение карт |
YOOKASSA_RECURRENT_REQUIRED | bool | false | Принудительное сохранение карт |
Устранение проблем
Автопродление не срабатывает
ENABLE_AUTOPAY=true?
- У пользователя включено автопродление? Проверьте
autopay_enabled на подписке
- Подписка не trial и не суточная?
- Баланс достаточен или есть сохранённая карта?
- Проверьте логи:
docker logs <bot_container> 2>&1 | grep -i "autopay\|recurrent"
Карты не сохраняются
YOOKASSA_RECURRENT_ENABLED=true?
- Пользователь дал согласие на сохранение (или
YOOKASSA_RECURRENT_REQUIRED=true)?
- Webhook от YooKassa приходит? Проверьте:
docker logs <bot_container> 2>&1 | grep -i "payment_method.*saved"
Списание с карты не проходит
- Срок действия карты не истёк?
- На карте достаточно средств?
- Банк не блокирует рекуррентные платежи?
- Проверьте ответ YooKassa в логах:
docker logs <bot_container> 2>&1 | grep -i "recurrent\|autopayment"
Пользователь получает повторные уведомления
Cooldown на уведомления о недостаточном балансе — 24 часа. Если Redis недоступен, cooldown не работает и уведомления могут дублироваться. Проверьте подключение к Redis.