From 2fbdbf5ab0ec0c13100e330f60a2249ed866c12e Mon Sep 17 00:00:00 2001 From: Fringg Date: Wed, 29 Apr 2026 08:16:39 +0300 Subject: [PATCH] fix: autopay renewing trial subscriptions at classic-mode pricing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three bugs caused trial subscriptions to be auto-renewed without a tariff at arbitrary prices: 1. try_auto_extend_expired_after_topup: is_trial guard used truthiness check — NULL (legacy rows) passed as falsy. Changed to `is_trial is not False` (NULL-safe). 2. Multi-tariff branch: `not s.is_trial` treated NULL as not-trial. Changed to `s.is_trial is False`. 3. Telegram bot autopay toggle: no is_trial guard — users could enable autopay on trial subscriptions. Added trial check before enabling. --- app/handlers/subscription/autopay.py | 12 ++++++++++++ app/services/subscription_auto_purchase_service.py | 5 +++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/app/handlers/subscription/autopay.py b/app/handlers/subscription/autopay.py index 25f8bab6..7f78274b 100644 --- a/app/handlers/subscription/autopay.py +++ b/app/handlers/subscription/autopay.py @@ -101,6 +101,18 @@ async def toggle_autopay(callback: types.CallbackQuery, db_user: User, db: Async enable = callback.data.startswith('autopay_enable') if enable: + # Trial subscriptions cannot use autopay + if subscription.is_trial or subscription.is_trial is None: + texts = get_texts(db_user.language) + await callback.answer( + texts.t( + 'AUTOPAY_NOT_AVAILABLE_TRIAL', + 'Автоплатеж недоступен для пробных подписок.', + ), + show_alert=True, + ) + return + # Classic subscriptions cannot use autopay when tariff mode is enabled if settings.is_tariffs_mode() and not subscription.tariff_id: texts = get_texts(db_user.language) diff --git a/app/services/subscription_auto_purchase_service.py b/app/services/subscription_auto_purchase_service.py index 9a5ecef8..7866fc7a 100644 --- a/app/services/subscription_auto_purchase_service.py +++ b/app/services/subscription_auto_purchase_service.py @@ -2138,7 +2138,7 @@ async def try_auto_extend_expired_after_topup( from app.database.crud.subscription import get_all_subscriptions_by_user_id all_subs = await get_all_subscriptions_by_user_id(db, user.id) - expired_subs = [s for s in all_subs if s.status == SubscriptionStatus.EXPIRED.value and not s.is_trial] + expired_subs = [s for s in all_subs if s.status == SubscriptionStatus.EXPIRED.value and s.is_trial is False] if not expired_subs: subscription = None else: @@ -2154,9 +2154,10 @@ async def try_auto_extend_expired_after_topup( return False # Only process expired subscriptions (not trial, not disabled) + # NULL-safe: is_trial can be None in legacy rows — treat as trial if subscription.status != SubscriptionStatus.EXPIRED.value: return False - if subscription.is_trial: + if subscription.is_trial is not False: return False # Only process subscriptions expired within the last 30 days