fix: restore panel user discovery on admin tariff change, localize cart reminder

Bug 1: Admin tariff change used update_remnawave_user() which returns
early when user has no remnawave_uuid. Restored _sync_subscription_to_panel()
which discovers/creates panel users via telegram_id/email fallback, then
applies traffic reset if RESET_TRAFFIC_ON_TARIFF_SWITCH is enabled.

Bug 2: Post-topup cart reminder in payment/common.py had hardcoded Russian
text sent to all users regardless of language. Replaced with localized
BALANCE_TOPPED_UP_CART_SUFFICIENT/INSUFFICIENT keys and used existing
MY_BALANCE_BUTTON/MAIN_MENU_BUTTON for inline keyboard buttons.
Added new i18n keys to all 5 locales (ru, en, ua, zh, fa).
This commit is contained in:
Fringg
2026-03-02 20:48:02 +03:00
parent 58faf9eaec
commit 1256ddcd1a
7 changed files with 1636 additions and 1966 deletions

View File

@@ -1073,17 +1073,19 @@ async def update_user_subscription(
await db.commit()
await db.refresh(subscription)
# Синхронизируем с RemnaWave (сброс трафика по админ-настройке)
# Синхронизируем с RemnaWave (discovery/create + сброс трафика по админ-настройке)
try:
from app.services.subscription_service import SubscriptionService
result = await _sync_subscription_to_panel(db, user, subscription)
if settings.RESET_TRAFFIC_ON_TARIFF_SWITCH and result.get('action') in ('updated', 'created'):
from app.services.subscription_service import SubscriptionService
subscription_service = SubscriptionService()
await subscription_service.update_remnawave_user(
db,
subscription,
reset_traffic=settings.RESET_TRAFFIC_ON_TARIFF_SWITCH,
reset_reason='смена тарифа (cabinet admin)',
)
subscription_service = SubscriptionService()
await subscription_service.update_remnawave_user(
db,
subscription,
reset_traffic=True,
reset_reason='смена тарифа (cabinet admin)',
)
except Exception as e:
logger.error('Failed to sync tariff switch with RemnaWave', error=e)

View File

@@ -1328,6 +1328,8 @@
"RESET_TRAFFIC_BUTTON": "🔄 Reset traffic",
"NO_SAVED_SUBSCRIPTION_ORDER": "No pending subscription order was found.",
"RETURN_TO_SUBSCRIPTION_CHECKOUT": "⬅️ Return to subscription checkout",
"BALANCE_TOPPED_UP_CART_SUFFICIENT": "✅ Balance topped up by {amount}!\n\n💰 Current balance: {balance}\n\n🛒 You have a saved cart for {cart_total}\nYour balance is sufficient to proceed.",
"BALANCE_TOPPED_UP_CART_INSUFFICIENT": "✅ Balance topped up by {amount}!\n\n💰 Current balance: {balance}\n\n🛒 You have a saved cart for {cart_total}\nStill needed: {missing}",
"RULES_ACCEPT": "✅ I accept the rules",
"RULES_ACCEPTED_PROCESSING": "✅ Rules accepted! Completing registration...",
"RULES_DECLINE": "❌ I do not accept",

View File

@@ -1349,6 +1349,8 @@
"RESET_TRAFFIC_BUTTON": "🔄 بازنشانی ترافیک",
"NO_SAVED_SUBSCRIPTION_ORDER": "❗️ سفارش ذخیره‌شده یافت نشد.",
"RETURN_TO_SUBSCRIPTION_CHECKOUT": "⬅️ بازگشت به خرید اشتراک",
"BALANCE_TOPPED_UP_CART_SUFFICIENT": "✅ موجودی شارژ شد به مبلغ {amount}!\n\n💰 موجودی فعلی: {balance}\n\n🛒 شما یک سبد ذخیره‌شده به مبلغ {cart_total} دارید\nموجودی شما برای ادامه کافی است.",
"BALANCE_TOPPED_UP_CART_INSUFFICIENT": "✅ موجودی شارژ شد به مبلغ {amount}!\n\n💰 موجودی فعلی: {balance}\n\n🛒 شما یک سبد ذخیره‌شده به مبلغ {cart_total} دارید\nمبلغ باقیمانده: {missing}",
"RULES_ACCEPT": "✅ می‌پذیرم",
"RULES_ACCEPTED_PROCESSING": "✅ قوانین پذیرفته شد. در حال پردازش...",
"RULES_DECLINE": "❌ نمی‌پذیرم",

View File

@@ -1349,6 +1349,8 @@
"RESET_TRAFFIC_BUTTON": "🔄 Сбросить трафик",
"NO_SAVED_SUBSCRIPTION_ORDER": "❗️ Сохраненный заказ не найден.",
"RETURN_TO_SUBSCRIPTION_CHECKOUT": "⬅️ Вернуться к оформлению подписки",
"BALANCE_TOPPED_UP_CART_SUFFICIENT": "✅ Баланс пополнен на {amount}!\n\n💰 Текущий баланс: {balance}\n\n🛒 У вас есть сохранённая корзина на {cart_total}\nСредств на балансе достаточно для оформления.",
"BALANCE_TOPPED_UP_CART_INSUFFICIENT": "✅ Баланс пополнен на {amount}!\n\n💰 Текущий баланс: {balance}\n\n🛒 У вас есть сохранённая корзина на {cart_total}\nНе хватает: {missing}",
"RULES_ACCEPT": "✅ Принимаю правила",
"RULES_ACCEPTED_PROCESSING": "✅ Правила приняты! Завершаем регистрацию...",
"RULES_DECLINE": "❌ Не принимаю",

View File

@@ -1265,6 +1265,8 @@
"RESET_TRAFFIC_BUTTON": "🔄 Скинути трафік",
"NO_SAVED_SUBSCRIPTION_ORDER": "❗️ Збережене замовлення не знайдено.",
"RETURN_TO_SUBSCRIPTION_CHECKOUT": "⬅️ Повернутися до оформлення підписки",
"BALANCE_TOPPED_UP_CART_SUFFICIENT": "✅ Баланс поповнено на {amount}!\n\n💰 Поточний баланс: {balance}\n\n🛒 У вас є збережений кошик на {cart_total}\nКоштів на балансі достатньо для оформлення.",
"BALANCE_TOPPED_UP_CART_INSUFFICIENT": "✅ Баланс поповнено на {amount}!\n\n💰 Поточний баланс: {balance}\n\n🛒 У вас є збережений кошик на {cart_total}\nНе вистачає: {missing}",
"RULES_ACCEPT": "✅ Приймаю правила",
"RULES_ACCEPTED_PROCESSING": "✅ Правила прийнято! Завершуємо реєстрацію...",
"RULES_DECLINE": "❌ Не приймаю",

File diff suppressed because it is too large Load Diff

View File

@@ -341,20 +341,15 @@ async def send_cart_notification_after_topup(
texts = get_texts(getattr(user, 'language', 'ru'))
# Build message based on whether balance is sufficient
fmt = settings.format_price
if balance >= cart_total:
message_text = (
f'✅ Баланс пополнен на {settings.format_price(amount_kopeks)}!\n\n'
f'💰 Текущий баланс: {settings.format_price(balance)}\n\n'
f'🛒 У вас есть сохранённая корзина на {settings.format_price(cart_total)}\n'
f'Средств на балансе достаточно для оформления.'
)
template = texts.get('BALANCE_TOPPED_UP_CART_SUFFICIENT', '')
message_text = template.format(amount=fmt(amount_kopeks), balance=fmt(balance), cart_total=fmt(cart_total))
else:
missing = cart_total - balance
message_text = (
f'✅ Баланс пополнен на {settings.format_price(amount_kopeks)}!\n\n'
f'💰 Текущий баланс: {settings.format_price(balance)}\n\n'
f'🛒 У вас есть сохранённая корзина на {settings.format_price(cart_total)}\n'
f'Не хватает: {settings.format_price(missing)}'
template = texts.get('BALANCE_TOPPED_UP_CART_INSUFFICIENT', '')
message_text = template.format(
amount=fmt(amount_kopeks), balance=fmt(balance), cart_total=fmt(cart_total), missing=fmt(missing),
)
keyboard = types.InlineKeyboardMarkup(
@@ -365,8 +360,8 @@ async def send_cart_notification_after_topup(
callback_data='return_to_saved_cart',
)
],
[types.InlineKeyboardButton(text='💰 Мой баланс', callback_data='menu_balance')],
[types.InlineKeyboardButton(text='🏠 Главное меню', callback_data='back_to_menu')],
[types.InlineKeyboardButton(text=texts.MY_BALANCE_BUTTON, callback_data='menu_balance')],
[types.InlineKeyboardButton(text=texts.MAIN_MENU_BUTTON, callback_data='back_to_menu')],
]
)