feat(websocket): add real-time notifications for subscription and balance events
- Import and call notify_user_subscription_renewed in auto-extend flows - Import and call notify_user_subscription_activated for new subscriptions - Add WebSocket notifications to _auto_purchase_tariff and _auto_purchase_daily_tariff - Add WebSocket notifications to auto_activate_subscription_after_topup - Add notify_user_balance_topup call in payment common mixin
This commit is contained in:
@@ -14,6 +14,8 @@ from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from sqlalchemy.exc import MissingGreenlet
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
# WebSocket notifications for cabinet
|
||||
from app.cabinet.routes.websocket import notify_user_balance_topup
|
||||
from app.config import settings
|
||||
from app.database.crud.user import get_user_by_telegram_id
|
||||
from app.database.database import get_db
|
||||
@@ -132,6 +134,25 @@ class PaymentCommonMixin:
|
||||
payment_method_title: str | None = None,
|
||||
) -> None:
|
||||
"""Отправляет пользователю уведомление об успешном платеже."""
|
||||
# Send WebSocket notification to cabinet frontend (works for both Telegram and email-only users)
|
||||
user_id = getattr(user, 'id', None) if user else None
|
||||
if user_id:
|
||||
try:
|
||||
# Get new balance from user
|
||||
new_balance = getattr(user, 'balance_kopeks', 0)
|
||||
await notify_user_balance_topup(
|
||||
user_id=user_id,
|
||||
amount_kopeks=amount_kopeks,
|
||||
new_balance_kopeks=new_balance,
|
||||
description=payment_method_title or '',
|
||||
)
|
||||
except Exception as ws_error:
|
||||
logger.warning(
|
||||
'Не удалось отправить WS уведомление о пополнении баланса для user_id=%s: %s',
|
||||
user_id,
|
||||
ws_error,
|
||||
)
|
||||
|
||||
if not getattr(self, 'bot', None):
|
||||
# Если бот не передан (например, внутри фоновых задач), уведомление пропускаем.
|
||||
return
|
||||
|
||||
@@ -9,6 +9,11 @@ from aiogram import Bot
|
||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
# WebSocket notifications for cabinet
|
||||
from app.cabinet.routes.websocket import (
|
||||
notify_user_subscription_activated,
|
||||
notify_user_subscription_renewed,
|
||||
)
|
||||
from app.config import settings
|
||||
from app.database.crud.subscription import extend_subscription
|
||||
from app.database.crud.transaction import create_transaction
|
||||
@@ -559,6 +564,20 @@ async def _auto_extend_subscription(
|
||||
_format_user_id(user),
|
||||
)
|
||||
|
||||
# Send WebSocket notification to cabinet frontend
|
||||
try:
|
||||
await notify_user_subscription_renewed(
|
||||
user_id=user.id,
|
||||
new_expires_at=new_end_date.isoformat() if new_end_date else '',
|
||||
amount_kopeks=prepared.price_kopeks,
|
||||
)
|
||||
except Exception as ws_error:
|
||||
logger.warning(
|
||||
'⚠️ Автопокупка: не удалось отправить WS уведомление о продлении для %s: %s',
|
||||
_format_user_id(user),
|
||||
ws_error,
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -814,6 +833,29 @@ async def _auto_purchase_tariff(
|
||||
_format_user_id(user),
|
||||
)
|
||||
|
||||
# Send WebSocket notification to cabinet frontend
|
||||
try:
|
||||
if existing_subscription:
|
||||
# Renewal of existing subscription
|
||||
await notify_user_subscription_renewed(
|
||||
user_id=user.id,
|
||||
new_expires_at=subscription.end_date.isoformat() if subscription.end_date else '',
|
||||
amount_kopeks=final_price,
|
||||
)
|
||||
else:
|
||||
# New subscription activation
|
||||
await notify_user_subscription_activated(
|
||||
user_id=user.id,
|
||||
expires_at=subscription.end_date.isoformat() if subscription.end_date else '',
|
||||
tariff_name=tariff.name,
|
||||
)
|
||||
except Exception as ws_error:
|
||||
logger.warning(
|
||||
'⚠️ Автопокупка тарифа: не удалось отправить WS уведомление для %s: %s',
|
||||
_format_user_id(user),
|
||||
ws_error,
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -1051,6 +1093,29 @@ async def _auto_purchase_daily_tariff(
|
||||
_format_user_id(user),
|
||||
)
|
||||
|
||||
# Send WebSocket notification to cabinet frontend
|
||||
try:
|
||||
if existing_subscription:
|
||||
# Renewal/upgrade of existing subscription
|
||||
await notify_user_subscription_renewed(
|
||||
user_id=user.id,
|
||||
new_expires_at=subscription.end_date.isoformat() if subscription.end_date else '',
|
||||
amount_kopeks=daily_price,
|
||||
)
|
||||
else:
|
||||
# New subscription activation
|
||||
await notify_user_subscription_activated(
|
||||
user_id=user.id,
|
||||
expires_at=subscription.end_date.isoformat() if subscription.end_date else '',
|
||||
tariff_name=tariff.name,
|
||||
)
|
||||
except Exception as ws_error:
|
||||
logger.warning(
|
||||
'⚠️ Автопокупка суточного тарифа: не удалось отправить WS уведомление для %s: %s',
|
||||
_format_user_id(user),
|
||||
ws_error,
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -1243,6 +1308,29 @@ async def auto_purchase_saved_cart_after_topup(
|
||||
_format_user_id(user),
|
||||
)
|
||||
|
||||
# Send WebSocket notification to cabinet frontend
|
||||
try:
|
||||
if was_trial_conversion:
|
||||
# Trial conversion = activation
|
||||
await notify_user_subscription_activated(
|
||||
user_id=user.id,
|
||||
expires_at=subscription.end_date.isoformat() if subscription and subscription.end_date else '',
|
||||
tariff_name='',
|
||||
)
|
||||
else:
|
||||
# Regular purchase = renewal or new activation
|
||||
await notify_user_subscription_renewed(
|
||||
user_id=user.id,
|
||||
new_expires_at=subscription.end_date.isoformat() if subscription and subscription.end_date else '',
|
||||
amount_kopeks=pricing.final_total,
|
||||
)
|
||||
except Exception as ws_error:
|
||||
logger.warning(
|
||||
'⚠️ Автопокупка: не удалось отправить WS уведомление для %s: %s',
|
||||
_format_user_id(user),
|
||||
ws_error,
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -1397,6 +1485,20 @@ async def auto_activate_subscription_after_topup(
|
||||
best_price,
|
||||
)
|
||||
|
||||
# Send WebSocket notification to cabinet frontend
|
||||
try:
|
||||
await notify_user_subscription_renewed(
|
||||
user_id=user.id,
|
||||
new_expires_at=result.subscription.end_date.isoformat() if result.subscription.end_date else '',
|
||||
amount_kopeks=best_price,
|
||||
)
|
||||
except Exception as ws_error:
|
||||
logger.warning(
|
||||
'⚠️ Автоактивация: не удалось отправить WS уведомление о продлении для %s: %s',
|
||||
_format_user_id(user),
|
||||
ws_error,
|
||||
)
|
||||
|
||||
# Уведомление пользователю (только для Telegram-пользователей)
|
||||
if bot and user.telegram_id:
|
||||
try:
|
||||
@@ -1475,6 +1577,20 @@ async def auto_activate_subscription_after_topup(
|
||||
best_price,
|
||||
)
|
||||
|
||||
# Send WebSocket notification to cabinet frontend
|
||||
try:
|
||||
await notify_user_subscription_activated(
|
||||
user_id=user.id,
|
||||
expires_at=new_subscription.end_date.isoformat() if new_subscription.end_date else '',
|
||||
tariff_name='',
|
||||
)
|
||||
except Exception as ws_error:
|
||||
logger.warning(
|
||||
'⚠️ Автоактивация: не удалось отправить WS уведомление об активации для %s: %s',
|
||||
_format_user_id(user),
|
||||
ws_error,
|
||||
)
|
||||
|
||||
# Уведомление пользователю (только для Telegram-пользователей)
|
||||
if bot and user.telegram_id:
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user