From 9b4dab84addc390165d743bb8ca93cf5e89c1afc Mon Sep 17 00:00:00 2001 From: machka-pasla Date: Wed, 3 Sep 2025 10:44:20 +0300 Subject: [PATCH] Enhance auto-renewal messaging and payment processing logic - Introduced a new flag for auto-renew subscriptions to streamline messaging and avoid redundant configuration links. - Updated localization files to include a new message for auto-renewal notifications in both English and Russian. - Improved error handling and logging in the payment processing flow to ensure clarity and reliability during subscription renewals. --- bot/handlers/user/payment.py | 90 +++++++++++++++------------ bot/services/panel_webhook_service.py | 2 + locales/en.json | 1 + locales/ru.json | 1 + 4 files changed, 54 insertions(+), 40 deletions(-) diff --git a/bot/handlers/user/payment.py b/bot/handlers/user/payment.py index 54b097c..b4a4ba1 100644 --- a/bot/handlers/user/payment.py +++ b/bot/handlers/user/payment.py @@ -58,6 +58,7 @@ async def process_successful_payment(session: AsyncSession, bot: Bot, subscription_months = int(subscription_months_str) payment_db_id = int( payment_db_id_str) if payment_db_id_str and payment_db_id_str.isdigit() else None + is_auto_renew = bool(auto_renew_subscription_id_str and not payment_db_id) promo_code_id = int( promo_code_id_str ) if promo_code_id_str and promo_code_id_str.isdigit() else None @@ -199,53 +200,62 @@ async def process_successful_payment(session: AsyncSession, bot: Bot, user_lang = db_user.language_code if db_user and db_user.language_code else settings.DEFAULT_LANGUAGE _ = lambda key, **kwargs: i18n.gettext(user_lang, key, **kwargs) - config_link = activation_details.get("subscription_url") or _( - "config_link_not_available" - ) - - if applied_referee_bonus_days_from_referral and final_end_date_for_user: - inviter_name_display = _("friend_placeholder") - if db_user and db_user.referred_by_id: - inviter = await user_dal.get_user_by_id( - session, db_user.referred_by_id) - if inviter and inviter.first_name: - inviter_name_display = inviter.first_name - elif inviter and inviter.username: - inviter_name_display = f"@{inviter.username}" - + # For auto-renew charges, avoid re-sending config link; send concise message + if is_auto_renew and final_end_date_for_user: details_message = _( - "payment_successful_with_referral_bonus_full", - months=subscription_months, - base_end_date=base_subscription_end_date.strftime('%Y-%m-%d'), - bonus_days=applied_referee_bonus_days_from_referral, - final_end_date=final_end_date_for_user.strftime('%Y-%m-%d'), - inviter_name=inviter_name_display, - config_link=config_link, - ) - elif applied_promo_bonus_days > 0 and final_end_date_for_user: - details_message = _( - "payment_successful_with_promo_full", - months=subscription_months, - bonus_days=applied_promo_bonus_days, - end_date=final_end_date_for_user.strftime('%Y-%m-%d'), - config_link=config_link, - ) - elif final_end_date_for_user: - details_message = _( - "payment_successful_full", + "yookassa_auto_renewal", months=subscription_months, end_date=final_end_date_for_user.strftime('%Y-%m-%d'), - config_link=config_link, ) + details_markup = None else: - logging.error( - f"Critical error: final_end_date_for_user is None for user {user_id} after successful payment logic." + config_link = activation_details.get("subscription_url") or _( + "config_link_not_available" ) - details_message = _("payment_successful_error_details") - details_markup = get_connect_and_main_keyboard( - user_lang, i18n, settings, config_link - ) + if applied_referee_bonus_days_from_referral and final_end_date_for_user: + inviter_name_display = _("friend_placeholder") + if db_user and db_user.referred_by_id: + inviter = await user_dal.get_user_by_id( + session, db_user.referred_by_id) + if inviter and inviter.first_name: + inviter_name_display = inviter.first_name + elif inviter and inviter.username: + inviter_name_display = f"@{inviter.username}" + + details_message = _( + "payment_successful_with_referral_bonus_full", + months=subscription_months, + base_end_date=base_subscription_end_date.strftime('%Y-%m-%d'), + bonus_days=applied_referee_bonus_days_from_referral, + final_end_date=final_end_date_for_user.strftime('%Y-%m-%d'), + inviter_name=inviter_name_display, + config_link=config_link, + ) + elif applied_promo_bonus_days > 0 and final_end_date_for_user: + details_message = _( + "payment_successful_with_promo_full", + months=subscription_months, + bonus_days=applied_promo_bonus_days, + end_date=final_end_date_for_user.strftime('%Y-%m-%d'), + config_link=config_link, + ) + elif final_end_date_for_user: + details_message = _( + "payment_successful_full", + months=subscription_months, + end_date=final_end_date_for_user.strftime('%Y-%m-%d'), + config_link=config_link, + ) + else: + logging.error( + f"Critical error: final_end_date_for_user is None for user {user_id} after successful payment logic." + ) + details_message = _("payment_successful_error_details") + + details_markup = get_connect_and_main_keyboard( + user_lang, i18n, settings, config_link + ) try: await bot.send_message( user_id, diff --git a/bot/services/panel_webhook_service.py b/bot/services/panel_webhook_service.py index fcef89a..c2ec807 100644 --- a/bot/services/panel_webhook_service.py +++ b/bot/services/panel_webhook_service.py @@ -196,8 +196,10 @@ class PanelWebhookService: if sub and sub.auto_renew_enabled and sub.provider != 'tribute': try: ok = await subscription_service.charge_subscription_renewal(session, sub) + # If initiation succeeded, suppress the 24h reminder by returning early if ok: await session.commit() + return else: await session.rollback() except Exception: diff --git a/locales/en.json b/locales/en.json index cec81e0..53a63f4 100644 --- a/locales/en.json +++ b/locales/en.json @@ -211,6 +211,7 @@ "subscription_expired_yesterday_notification": "πŸ‘‹ Hi, {user_name}!\n\n⏳ Your VPN subscription expired yesterday ({end_date}).\n\nPlease renew it using the button below.", "tribute_subscription_cancelled": "🚨 Subscription Cancelled\n\nYour Tribute subscription has been cancelled. You have 24 hours to restore access, after which the subscription will be blocked.\n\nTo renew your subscription, press the button below.", "tribute_auto_renewal": "πŸ”„ Subscription Auto-Renewed\n\nYour Tribute subscription has been automatically renewed for {months} months.\nNew expiration date: {end_date}", + "yookassa_auto_renewal": "πŸ”„ Subscription Auto-Renewed\n\nYour subscription was automatically renewed for {months} month(s).\nNew expiration date: {end_date}", "admin_user_management_prompt": "πŸ‘€ User Management\n\nEnter user ID or @username to search:", "admin_user_subscription_info": "Subscription Information:", "admin_user_reset_trial_button": "πŸ”„ Reset Trial", diff --git a/locales/ru.json b/locales/ru.json index fe28c90..651b26d 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -140,6 +140,7 @@ "subscription_expired_notification": "πŸ‘‹ ΠŸΡ€ΠΈΠ²Π΅Ρ‚, {user_name}!\n\nβ›” Π‘Ρ€ΠΎΠΊ вашСй подписки Π½Π° VPN истСк ({end_date}).\n\nΠŸΡ€ΠΎΠ΄Π»ΠΈΡ‚Π΅ Π΅Ρ‘ ΠΏΠΎ ΠΊΠ½ΠΎΠΏΠΊΠ΅ Π½ΠΈΠΆΠ΅.", "subscription_expired_yesterday_notification": "πŸ‘‹ ΠŸΡ€ΠΈΠ²Π΅Ρ‚, {user_name}!\n\n⏳ Π’Π°ΡˆΠ° подписка Π½Π° VPN истСкла сутки Π½Π°Π·Π°Π΄ ({end_date}).\n\nΠŸΡ€ΠΎΠ΄Π»ΠΈΡ‚Π΅ Π΅Ρ‘ ΠΏΠΎ ΠΊΠ½ΠΎΠΏΠΊΠ΅ Π½ΠΈΠΆΠ΅.", "tribute_subscription_cancelled": "🚨 Подписка ΠΎΡ‚ΠΌΠ΅Π½Π΅Π½Π°\n\nΠ’Π°ΡˆΠ° подписка Tribute Π±Ρ‹Π»Π° ΠΎΡ‚ΠΌΠ΅Π½Π΅Π½Π°. Π£ вас Π΅ΡΡ‚ΡŒ 24 часа для восстановлСния доступа, послС Ρ‡Π΅Π³ΠΎ подписка Π±ΡƒΠ΄Π΅Ρ‚ Π·Π°Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²Π°Π½Π°.\n\nДля продлСния подписки Π½Π°ΠΆΠΌΠΈΡ‚Π΅ ΠΊΠ½ΠΎΠΏΠΊΡƒ Π½ΠΈΠΆΠ΅.", + "yookassa_auto_renewal": "πŸ”„ Подписка автоматичСски ΠΏΡ€ΠΎΠ΄Π»Π΅Π½Π°\n\nΠ’Π°ΡˆΠ° подписка Π±Ρ‹Π»Π° автоматичСскΠΈ ΠΏΡ€ΠΎΠ΄Π»Π΅Π½Π° Π½Π° {months} мСс.\nНовая Π΄Π°Ρ‚Π° окончания: {end_date}", "admin_promo_set_validity_days": "⏰ Π£ΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ срок (Π΄Π½ΠΈ)", "admin_back_to_panel": "⬅️ Π’ панСль", "admin_promo_unlimited": "♾️ НСограничСнно",