feat(promo): Integrate promo code service into subscription options
- Enhanced the display_subscription_options and reshow_subscription_options_callback functions to accept a promo_code_service parameter for improved discount handling. - Updated the logic to calculate and display discounted prices based on active promo codes, including support for both regular and star-based pricing. - Modified relevant locales to include currency symbols in discount notices for better user clarity.
This commit is contained in:
@@ -675,7 +675,7 @@ async def main_action_callback_handler(
|
||||
|
||||
if action == "subscribe":
|
||||
await user_subscription_handlers.display_subscription_options(
|
||||
callback, i18n_data, settings, session)
|
||||
callback, i18n_data, settings, session, promo_code_service=promo_code_service)
|
||||
elif action == "my_subscription":
|
||||
await user_subscription_handlers.my_subscription_command_handler(
|
||||
callback, i18n_data, settings, panel_service, subscription_service,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import hashlib
|
||||
import logging
|
||||
import math
|
||||
from aiogram import Router, F, types, Bot
|
||||
from aiogram.filters import Command
|
||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, WebAppInfo
|
||||
@@ -39,7 +40,13 @@ def _hwid_callback_token(hwid: Optional[str]) -> str:
|
||||
return hashlib.sha256(hwid_str.encode()).hexdigest()[:32]
|
||||
|
||||
|
||||
async def display_subscription_options(event: Union[types.Message, types.CallbackQuery], i18n_data: dict, settings: Settings, session: AsyncSession):
|
||||
async def display_subscription_options(
|
||||
event: Union[types.Message, types.CallbackQuery],
|
||||
i18n_data: dict,
|
||||
settings: Settings,
|
||||
session: AsyncSession,
|
||||
promo_code_service=None,
|
||||
):
|
||||
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
|
||||
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
|
||||
|
||||
@@ -61,21 +68,46 @@ async def display_subscription_options(event: Union[types.Message, types.Callbac
|
||||
stars_traffic_packages = getattr(settings, "stars_traffic_packages", {}) or {}
|
||||
traffic_mode = bool(getattr(settings, "traffic_sale_mode", False) or stars_traffic_packages)
|
||||
|
||||
options_are_stars = False
|
||||
if traffic_mode:
|
||||
if traffic_packages:
|
||||
options = traffic_packages
|
||||
elif stars_traffic_packages:
|
||||
options = stars_traffic_packages
|
||||
currency_symbol_val = "⭐"
|
||||
options_are_stars = True
|
||||
else:
|
||||
options = {}
|
||||
else:
|
||||
options = settings.subscription_options
|
||||
|
||||
if options:
|
||||
display_options = options
|
||||
if options and promo_code_service:
|
||||
try:
|
||||
active_discount_info = await promo_code_service.get_user_active_discount(
|
||||
session, event.from_user.id
|
||||
)
|
||||
except Exception:
|
||||
active_discount_info = None
|
||||
if active_discount_info:
|
||||
discount_pct, _promo_code = active_discount_info
|
||||
discounted_options = {}
|
||||
for period, price in options.items():
|
||||
if price is None:
|
||||
discounted_options[period] = price
|
||||
else:
|
||||
discounted_price, _ = promo_code_service.calculate_discounted_price(
|
||||
price, discount_pct
|
||||
)
|
||||
if options_are_stars:
|
||||
discounted_price = math.ceil(discounted_price)
|
||||
discounted_options[period] = discounted_price
|
||||
display_options = discounted_options
|
||||
|
||||
if display_options:
|
||||
text_content = get_text("select_traffic_package") if traffic_mode else get_text("select_subscription_period")
|
||||
reply_markup = get_subscription_options_keyboard(
|
||||
options, currency_symbol_val, current_lang, i18n, traffic_mode=traffic_mode
|
||||
display_options, currency_symbol_val, current_lang, i18n, traffic_mode=traffic_mode
|
||||
)
|
||||
else:
|
||||
text_content = get_text("no_subscription_options_available")
|
||||
@@ -104,8 +136,16 @@ async def display_subscription_options(event: Union[types.Message, types.Callbac
|
||||
|
||||
|
||||
@router.callback_query(F.data == "main_action:subscribe")
|
||||
async def reshow_subscription_options_callback(callback: types.CallbackQuery, i18n_data: dict, settings: Settings, session: AsyncSession):
|
||||
await display_subscription_options(callback, i18n_data, settings, session)
|
||||
async def reshow_subscription_options_callback(
|
||||
callback: types.CallbackQuery,
|
||||
i18n_data: dict,
|
||||
settings: Settings,
|
||||
session: AsyncSession,
|
||||
promo_code_service=None,
|
||||
):
|
||||
await display_subscription_options(
|
||||
callback, i18n_data, settings, session, promo_code_service=promo_code_service
|
||||
)
|
||||
|
||||
|
||||
async def my_subscription_command_handler(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import logging
|
||||
import math
|
||||
from typing import Optional
|
||||
|
||||
from aiogram import F, Router, types
|
||||
@@ -52,26 +53,45 @@ async def select_subscription_period_callback_handler(
|
||||
|
||||
# Check for active discount and apply if exists
|
||||
discount_text = ""
|
||||
if promo_code_service and price_rub:
|
||||
if promo_code_service and (price_rub is not None or stars_price is not None):
|
||||
active_discount_info = await promo_code_service.get_user_active_discount(
|
||||
session, callback.from_user.id
|
||||
)
|
||||
|
||||
if active_discount_info:
|
||||
discount_pct, promo_code = active_discount_info
|
||||
original_price_rub = price_rub
|
||||
price_rub, discount_amt = promo_code_service.calculate_discounted_price(
|
||||
price_rub, discount_pct
|
||||
)
|
||||
discount_text = get_text(
|
||||
"active_discount_notice",
|
||||
code=promo_code,
|
||||
discount_pct=discount_pct,
|
||||
original_price=original_price_rub,
|
||||
discounted_price=price_rub,
|
||||
discount_amount=discount_amt
|
||||
)
|
||||
# Note: Stars prices typically don't get discounts (can be added if needed)
|
||||
if price_rub is not None:
|
||||
original_price_rub = price_rub
|
||||
price_rub, discount_amt = promo_code_service.calculate_discounted_price(
|
||||
price_rub, discount_pct
|
||||
)
|
||||
discount_text = get_text(
|
||||
"active_discount_notice",
|
||||
code=promo_code,
|
||||
discount_pct=discount_pct,
|
||||
original_price=original_price_rub,
|
||||
discounted_price=price_rub,
|
||||
discount_amount=discount_amt,
|
||||
currency_symbol=currency_symbol_val,
|
||||
)
|
||||
if stars_price is not None:
|
||||
original_stars_price = stars_price
|
||||
discounted_stars_price, _ = promo_code_service.calculate_discounted_price(
|
||||
float(stars_price), discount_pct
|
||||
)
|
||||
discounted_stars_price = math.ceil(discounted_stars_price)
|
||||
stars_price = discounted_stars_price
|
||||
if not discount_text:
|
||||
discount_amt = original_stars_price - discounted_stars_price
|
||||
discount_text = get_text(
|
||||
"active_discount_notice",
|
||||
code=promo_code,
|
||||
discount_pct=discount_pct,
|
||||
original_price=original_stars_price,
|
||||
discounted_price=discounted_stars_price,
|
||||
discount_amount=discount_amt,
|
||||
currency_symbol="⭐",
|
||||
)
|
||||
|
||||
if price_rub is None:
|
||||
if traffic_mode and not price_source and stars_price is not None:
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@
|
||||
"discount_promo_code_applied_success": "✅ Promo code <code>{code}</code> activated!\n\n💰 A {discount}% discount will be applied to your next purchase.\n\nSelect a plan for payment.",
|
||||
"discount_promo_already_active": "❌ You already have an active discount promo code (<code>{code}</code>, -{discount_pct}%). Use it first or wait until the payment is complete.",
|
||||
"promo_code_not_found_or_not_discount": "❌ Promo code <code>{code}</code> not found or is invalid.",
|
||||
"active_discount_notice": "🎁 Active discount: <code>{code}</code> (-{discount_pct}%)\n💵 Price: <s>{original_price}</s> ➔ <b>{discounted_price}</b>\n💰 Savings: {discount_amount}",
|
||||
"active_discount_notice": "🎁 Active discount: <code>{code}</code> (-{discount_pct}%)\n💵 Price: <s>{original_price}{currency_symbol}</s> ➔ <b>{discounted_price}{currency_symbol}</b>\n💰 Savings: {discount_amount}{currency_symbol}",
|
||||
"error_applying_promo_bonus": "Failed to apply promo bonus. Please try again later or contact support.",
|
||||
"promo_input_cancelled_short": "Promo code entry cancelled.",
|
||||
"trial_feature_disabled": "Free trial is currently unavailable.",
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@
|
||||
"discount_promo_code_applied_success": "✅ Промокод <code>{code}</code> активирован!\n\n💰 Скидка {discount}% будет применена к вашей следующей покупке.\n\nВыберите тариф для оплаты.",
|
||||
"discount_promo_already_active": "❌ У вас уже есть активированный промокод на скидку (<code>{code}</code>, -{discount_pct}%). Используйте его сначала или дождитесь окончания платежа.",
|
||||
"promo_code_not_found_or_not_discount": "❌ Промокод <code>{code}</code> не найден или недействителен.",
|
||||
"active_discount_notice": "🎁 Активна скидка: <code>{code}</code> (-{discount_pct}%)\n💵 Цена: <s>{original_price}</s> ➔ <b>{discounted_price}</b>\n💰 Экономия: {discount_amount}",
|
||||
"active_discount_notice": "🎁 Активна скидка: <code>{code}</code> (-{discount_pct}%)\n💵 Цена: <s>{original_price}{currency_symbol}</s> ➔ <b>{discounted_price}{currency_symbol}</b>\n💰 Экономия: {discount_amount}{currency_symbol}",
|
||||
"error_applying_promo_bonus": "Не удалось применить бонус по промокоду. Пожалуйста, попробуйте позже или свяжитесь с поддержкой.",
|
||||
"error_applying_promo_discount": "❌ Ошибка при активации промокода. Попробуйте позже.",
|
||||
"promo_input_cancelled_short": "Ввод промокода отменен.",
|
||||
|
||||
Reference in New Issue
Block a user