fix: block legacy subscription renewal bypass in tariff mode

When switching from configurator to tariff mode, users with old
subscriptions (tariff_id=NULL) could still renew them through
unguarded paths, bypassing tariff pricing entirely.

Vulnerable paths fixed:
- MiniApp POST /subscription/renewal/options: returns empty list
  for classic subscriptions in tariff mode
- MiniApp POST /subscription/renewal: raises 400 with
  classic_subscription_blocked error code
- Bot confirm_extend_subscription: blocks stale extend_period_
  callbacks with tariff mode check
- Monitoring _process_autopayments: skips classic subscriptions
  (tariff_id=NULL) in autopay loop when tariff mode active

Already protected (no changes needed):
- Cabinet GET/POST renewal endpoints (renewal.py:51,117)
- Auto-purchase service (_prepare_auto_extend_context:244)
- Bot handle_extend_subscription menu (purchase.py:1657)
- Tariff extend flow (tariff_purchase.py:2047)
This commit is contained in:
Fringg
2026-03-28 19:27:08 +03:00
parent cddb8d6332
commit 78209c8623
3 changed files with 35 additions and 0 deletions
+8
View File
@@ -1817,6 +1817,14 @@ async def confirm_extend_subscription(
days = int(callback.data.split('_')[2])
texts = get_texts(db_user.language)
# Block classic subscription renewal when tariff mode is active
if settings.is_tariffs_mode():
await callback.answer(
texts.t('TARIFF_MODE_RENEWAL_BLOCKED', '❌ Продление в этом режиме недоступно. Выберите тариф.'),
show_alert=True,
)
return
# Валидация что период доступен для продления
available_renewal_periods = settings.get_available_renewal_periods()
if days not in available_renewal_periods:
+9
View File
@@ -1075,6 +1075,15 @@ class MonitoringService:
)
continue
# Skip classic subscriptions (tariff_id=NULL) when tariff mode is active
if settings.is_tariffs_mode() and not sub.tariff_id:
logger.debug(
'Пропускаем классическую подписку без тарифа в autopay (tariff mode)',
sub_id=sub.id,
user_id=sub.user_id,
)
continue
days_before_expiry = (sub.end_date - current_time).days
if days_before_expiry <= min(sub.autopay_days_before or 3, 3):
autopay_subscriptions.append(sub)
+18
View File
@@ -5106,6 +5106,14 @@ async def get_subscription_renewal_options_endpoint(
)
_validate_subscription_id(payload.subscription_id, subscription)
# Block classic subscription renewal when tariff mode is active
if settings.is_tariffs_mode() and not subscription.tariff_id:
return MiniAppSubscriptionRenewalOptionsResponse(
periods=[],
balance_kopeks=getattr(user, 'balance_kopeks', 0),
balance_currency=(getattr(user, 'balance_currency', None) or 'RUB').upper(),
)
periods, pricing_map, default_period_id = await _prepare_subscription_renewal_options(
db,
user,
@@ -5197,6 +5205,16 @@ async def submit_subscription_renewal_endpoint(
)
_validate_subscription_id(payload.subscription_id, subscription)
# Block classic subscription renewal when tariff mode is active
if settings.is_tariffs_mode() and not subscription.tariff_id:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail={
'code': 'classic_subscription_blocked',
'message': 'Classic subscriptions cannot be renewed. Please purchase a tariff.',
},
)
period_days: int | None = None
if payload.period_days is not None:
try: