Skip to main content

Обзор

Система рекуррентных платежей позволяет автоматически продлевать подписки без участия пользователя. Состоит из двух компонентов:
  1. Автопродление (autopay) — автоматическое списание с баланса при приближении окончания подписки
  2. Сохранённые карты — автоматическое пополнение баланса через YooKassa, если средств на балансе недостаточно
Подписка истекает через N дней

Баланс достаточен?
   ├─ Да → Списание с баланса → Продление подписки
   └─ Нет → Есть сохранённая карта?
              ├─ Да → Списание с карты → Пополнение баланса → Продление
              └─ Нет → Уведомление «Недостаточно средств»
Для полноценной работы необходимо включить оба компонента:
  • ENABLE_AUTOPAY=true — автопродление с баланса
  • YOOKASSA_RECURRENT_ENABLED=true — сохранение и использование карт
Без ENABLE_AUTOPAY сохранённые карты не будут использоваться автоматически. Без YOOKASSA_RECURRENT_ENABLED продление возможно только при достаточном балансе.

Автопродление (с баланса)

Как работает

Мониторинг-сервис ежедневно проверяет подписки с включённым автопродлением:
  1. Находит подписки, где autopay_enabled=true и до окончания осталось ≤ autopay_days_before дней
  2. Рассчитывает стоимость продления (с учётом промо-скидок)
  3. Если баланс достаточен — списывает и продлевает подписку
  4. Если баланс недостаточен — пытается списать с сохранённой карты (при наличии)
  5. Отправляет уведомление о результате
Пробные (trial) и суточные подписки исключены из автопродления. Также обрабатываются подписки, истекшие не более 48 часов назад.

Настройка

ENABLE_AUTOPAY=true
DEFAULT_AUTOPAY_ENABLED=true
DEFAULT_AUTOPAY_DAYS_BEFORE=3
MIN_BALANCE_FOR_AUTOPAY_KOPEKS=10000
ПеременнаяТипПо умолчаниюОписание
ENABLE_AUTOPAYboolfalseГлобальное включение автопродления
DEFAULT_AUTOPAY_ENABLEDboolfalseАвтопродление включено у новых пользователей по умолчанию
DEFAULT_AUTOPAY_DAYS_BEFOREint3За сколько дней до окончания подписки запускать продление
MIN_BALANCE_FOR_AUTOPAY_KOPEKSint10000Минимальный баланс для срабатывания (100 руб)
SUBSCRIPTION_RENEWAL_BALANCE_THRESHOLD_KOPEKSint20000Порог баланса для автопродления (200 руб)

Интерфейс пользователя

В боте

Пользователь управляет автопродлением через меню подписки:
КнопкаДействие
Включить/ВыключитьПереключение autopay_enabled
Настроить дниВыбор за сколько дней до окончания списывать
Отображается текущий статус: включено/выключено и количество дней.

В Cabinet

PATCH /cabinet/subscription/autopay
Запрос:
{
  "enabled": true,
  "days_before": 3
}
Автопродление недоступно для суточных тарифов. При попытке включить на суточном тарифе API вернёт ошибку.

Сохранённые карты (YooKassa)

Как сохраняются карты

При оплате через YooKassa карта сохраняется автоматически:
  1. Пользователь создаёт платёж через YooKassa
  2. Если YOOKASSA_RECURRENT_REQUIRED=true — карта сохраняется принудительно
  3. Если YOOKASSA_RECURRENT_REQUIRED=false — YooKassa показывает чекбокс «Сохранить карту»
  4. После успешной оплаты webhook возвращает payment_method.saved=true
  5. Бот сохраняет данные карты в таблицу 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_ENABLEDboolfalseВключить сохранение карт и рекуррентные платежи
YOOKASSA_RECURRENT_REQUIREDboolfalseПринудительно сохранять карту при каждой оплате

Управление картами (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. При повторной оплате той же картой она реактивируется автоматически.

Автоматическое списание с карты

Процесс

Если при автопродлении на балансе недостаточно средств и у пользователя есть сохранённые карты:
  1. Рассчитывается сумма для продления подписки
  2. Бот перебирает все активные карты пользователя
  3. Для каждой карты создаётся платёж через YooKassa API (без подтверждения пользователя)
  4. При успехе — баланс пополняется, подписка продлевается
  5. При неудаче — пробуется следующая карта
  6. Если все карты не сработали — отправляется уведомление
Мониторинг                 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_AUTOPAYboolfalseГлобальное включение автопродления
DEFAULT_AUTOPAY_ENABLEDboolfalseАвтопродление для новых пользователей
DEFAULT_AUTOPAY_DAYS_BEFOREint3За сколько дней до окончания списывать
MIN_BALANCE_FOR_AUTOPAY_KOPEKSint10000Минимальный баланс для срабатывания (100 руб)
SUBSCRIPTION_RENEWAL_BALANCE_THRESHOLD_KOPEKSint20000Порог баланса для автопродления (200 руб)
AUTOPAY_WARNING_DAYSstr3,1Дни для предупреждений (через запятую)
YOOKASSA_RECURRENT_ENABLEDboolfalseВключить сохранение карт
YOOKASSA_RECURRENT_REQUIREDboolfalseПринудительное сохранение карт

Устранение проблем

Автопродление не срабатывает

  1. ENABLE_AUTOPAY=true?
  2. У пользователя включено автопродление? Проверьте autopay_enabled на подписке
  3. Подписка не trial и не суточная?
  4. Баланс достаточен или есть сохранённая карта?
  5. Проверьте логи:
    docker logs <bot_container> 2>&1 | grep -i "autopay\|recurrent"
    

Карты не сохраняются

  1. YOOKASSA_RECURRENT_ENABLED=true?
  2. Пользователь дал согласие на сохранение (или YOOKASSA_RECURRENT_REQUIRED=true)?
  3. Webhook от YooKassa приходит? Проверьте:
    docker logs <bot_container> 2>&1 | grep -i "payment_method.*saved"
    

Списание с карты не проходит

  1. Срок действия карты не истёк?
  2. На карте достаточно средств?
  3. Банк не блокирует рекуррентные платежи?
  4. Проверьте ответ YooKassa в логах:
    docker logs <bot_container> 2>&1 | grep -i "recurrent\|autopayment"
    

Пользователь получает повторные уведомления

Cooldown на уведомления о недостаточном балансе — 24 часа. Если Redis недоступен, cooldown не работает и уведомления могут дублироваться. Проверьте подключение к Redis.