diff --git a/app/services/admin_notification_service.py b/app/services/admin_notification_service.py
new file mode 100644
index 00000000..43a8fa66
--- /dev/null
+++ b/app/services/admin_notification_service.py
@@ -0,0 +1,273 @@
+import logging
+from typing import Optional, Dict, Any
+from datetime import datetime
+from aiogram import Bot
+from aiogram.exceptions import TelegramBadRequest, TelegramForbiddenError
+from sqlalchemy.ext.asyncio import AsyncSession
+
+from app.config import settings
+from app.database.models import User, Subscription, Transaction
+
+logger = logging.getLogger(__name__)
+
+
+class AdminNotificationService:
+
+ def __init__(self, bot: Bot):
+ self.bot = bot
+ self.chat_id = getattr(settings, 'ADMIN_NOTIFICATIONS_CHAT_ID', None)
+ self.topic_id = getattr(settings, 'ADMIN_NOTIFICATIONS_TOPIC_ID', None)
+ self.enabled = getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False)
+
+ async def send_trial_activation_notification(
+ self,
+ user: User,
+ subscription: Subscription
+ ) -> bool:
+ if not self._is_enabled():
+ return False
+
+ try:
+ user_status = "🆕 Новый" if not user.has_had_paid_subscription else "🔄 Существующий"
+
+ message = f"""🎯 АКТИВАЦИЯ ТРИАЛА
+
+👤 Пользователь: {user.full_name}
+🆔 Telegram ID: {user.telegram_id}
+📱 Username: @{user.username or 'отсутствует'}
+👥 Статус: {user_status}
+
+⏰ Параметры триала:
+📅 Период: {settings.TRIAL_DURATION_DAYS} дней
+📊 Трафик: {settings.TRIAL_TRAFFIC_LIMIT_GB} ГБ
+📱 Устройства: {settings.TRIAL_DEVICE_LIMIT}
+🌐 Сервер: {subscription.connected_squads[0] if subscription.connected_squads else 'По умолчанию'}
+
+📆 Действует до: {subscription.end_date.strftime('%d.%m.%Y %H:%M')}
+🔗 Реферер: {f'ID {user.referred_by_id}' if user.referred_by_id else 'Нет'}
+
+⏰ {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}"""
+
+ return await self._send_message(message)
+
+ except Exception as e:
+ logger.error(f"Ошибка отправки уведомления о триале: {e}")
+ return False
+
+ async def send_subscription_purchase_notification(
+ self,
+ user: User,
+ subscription: Subscription,
+ transaction: Transaction,
+ period_days: int,
+ was_trial_conversion: bool = False
+ ) -> bool:
+ if not self._is_enabled():
+ return False
+
+ try:
+ event_type = "🔄 КОНВЕРСИЯ ИЗ ТРИАЛА" if was_trial_conversion else "💎 ПОКУПКА ПОДПИСКИ"
+
+ if was_trial_conversion:
+ user_status = "🎯 Конверсия из триала"
+ elif user.has_had_paid_subscription:
+ user_status = "🔄 Продление/Обновление"
+ else:
+ user_status = "🆕 Первая покупка"
+
+ servers_info = await self._get_servers_info(subscription.connected_squads)
+
+ payment_method = self._get_payment_method_display(transaction.payment_method)
+
+ message = f"""💎 {event_type}
+
+👤 Пользователь: {user.full_name}
+🆔 Telegram ID: {user.telegram_id}
+📱 Username: @{user.username or 'отсутствует'}
+👥 Статус: {user_status}
+
+💰 Платеж:
+💵 Сумма: {settings.format_price(transaction.amount_kopeks)}
+💳 Способ: {payment_method}
+🆔 ID транзакции: {transaction.id}
+
+📱 Параметры подписки:
+📅 Период: {period_days} дней
+📊 Трафик: {self._format_traffic(subscription.traffic_limit_gb)}
+📱 Устройства: {subscription.device_limit}
+🌐 Серверы: {servers_info}
+
+📆 Действует до: {subscription.end_date.strftime('%d.%m.%Y %H:%M')}
+💰 Баланс после покупки: {settings.format_price(user.balance_kopeks)}
+🔗 Реферер: {f'ID {user.referred_by_id}' if user.referred_by_id else 'Нет'}
+
+⏰ {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}"""
+
+ return await self._send_message(message)
+
+ except Exception as e:
+ logger.error(f"Ошибка отправки уведомления о покупке: {e}")
+ return False
+
+ async def send_balance_topup_notification(
+ self,
+ user: User,
+ transaction: Transaction,
+ old_balance: int
+ ) -> bool:
+ if not self._is_enabled():
+ return False
+
+ try:
+ topup_status = "🆕 Первое пополнение" if not user.has_made_first_topup else "🔄 Пополнение"
+
+ payment_method = self._get_payment_method_display(transaction.payment_method)
+
+ balance_change = user.balance_kopeks - old_balance
+
+ message = f"""💰 ПОПОЛНЕНИЕ БАЛАНСА
+
+👤 Пользователь: {user.full_name}
+🆔 Telegram ID: {user.telegram_id}
+📱 Username: @{user.username or 'отсутствует'}
+💳 Статус: {topup_status}
+
+💰 Детали пополнения:
+💵 Сумма: {settings.format_price(transaction.amount_kopeks)}
+💳 Способ: {payment_method}
+🆔 ID транзакции: {transaction.id}
+
+💰 Баланс:
+📉 Было: {settings.format_price(old_balance)}
+📈 Стало: {settings.format_price(user.balance_kopeks)}
+➕ Изменение: +{settings.format_price(balance_change)}
+
+🔗 Реферер: {f'ID {user.referred_by_id}' if user.referred_by_id else 'Нет'}
+📱 Подписка: {self._get_subscription_status(user)}
+
+⏰ {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}"""
+
+ return await self._send_message(message)
+
+ except Exception as e:
+ logger.error(f"Ошибка отправки уведомления о пополнении: {e}")
+ return False
+
+ async def send_subscription_extension_notification(
+ self,
+ user: User,
+ subscription: Subscription,
+ transaction: Transaction,
+ extended_days: int,
+ old_end_date: datetime
+ ) -> bool:
+ if not self._is_enabled():
+ return False
+
+ try:
+ payment_method = self._get_payment_method_display(transaction.payment_method)
+
+ servers_info = await self._get_servers_info(subscription.connected_squads)
+
+ message = f"""⏰ ПРОДЛЕНИЕ ПОДПИСКИ
+
+👤 Пользователь: {user.full_name}
+🆔 Telegram ID: {user.telegram_id}
+📱 Username: @{user.username or 'отсутствует'}
+
+💰 Платеж:
+💵 Сумма: {settings.format_price(transaction.amount_kopeks)}
+💳 Способ: {payment_method}
+🆔 ID транзакции: {transaction.id}
+
+📅 Продление:
+➕ Добавлено дней: {extended_days}
+📆 Было до: {old_end_date.strftime('%d.%m.%Y %H:%M')}
+📆 Стало до: {subscription.end_date.strftime('%d.%m.%Y %H:%M')}
+
+📱 Текущие параметры:
+📊 Трафик: {self._format_traffic(subscription.traffic_limit_gb)}
+📱 Устройства: {subscription.device_limit}
+🌐 Серверы: {servers_info}
+
+💰 Баланс после операции: {settings.format_price(user.balance_kopeks)}
+
+⏰ {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}"""
+
+ return await self._send_message(message)
+
+ except Exception as e:
+ logger.error(f"Ошибка отправки уведомления о продлении: {e}")
+ return False
+
+ async def _send_message(self, text: str) -> bool:
+ if not self.chat_id:
+ logger.warning("ADMIN_NOTIFICATIONS_CHAT_ID не настроен")
+ return False
+
+ try:
+ message_kwargs = {
+ 'chat_id': self.chat_id,
+ 'text': text,
+ 'parse_mode': 'HTML',
+ 'disable_web_page_preview': True
+ }
+
+ if self.topic_id:
+ message_kwargs['message_thread_id'] = self.topic_id
+
+ await self.bot.send_message(**message_kwargs)
+ logger.info(f"Уведомление отправлено в чат {self.chat_id}")
+ return True
+
+ except TelegramForbiddenError:
+ logger.error(f"Бот не имеет прав для отправки в чат {self.chat_id}")
+ return False
+ except TelegramBadRequest as e:
+ logger.error(f"Ошибка отправки уведомления: {e}")
+ return False
+ except Exception as e:
+ logger.error(f"Неожиданная ошибка при отправке уведомления: {e}")
+ return False
+
+ def _is_enabled(self) -> bool:
+ return self.enabled and bool(self.chat_id)
+
+ def _get_payment_method_display(self, payment_method: Optional[str]) -> str:
+ method_names = {
+ 'telegram_stars': '⭐ Telegram Stars',
+ 'yookassa': '💳 YooKassa (карта)',
+ 'tribute': '💎 Tribute (карта)',
+ 'manual': '🛠️ Вручную (админ)',
+ 'balance': '💰 С баланса'
+ }
+ return method_names.get(payment_method, f'❓ {payment_method or "Неизвестно"}')
+
+ def _format_traffic(self, traffic_gb: int) -> str:
+ if traffic_gb == 0:
+ return "∞ Безлимит"
+ return f"{traffic_gb} ГБ"
+
+ def _get_subscription_status(self, user: User) -> str:
+ if not user.subscription:
+ return "❌ Нет подписки"
+
+ sub = user.subscription
+ if sub.is_trial:
+ return f"🎯 Триал (до {sub.end_date.strftime('%d.%m')})"
+ elif sub.is_active:
+ return f"✅ Активна (до {sub.end_date.strftime('%d.%m')})"
+ else:
+ return "❌ Неактивна"
+
+ async def _get_servers_info(self, squad_uuids: list) -> str:
+ if not squad_uuids:
+ return "❌ Нет серверов"
+
+ try:
+ from app.handlers.subscription import get_servers_display_names
+ servers_names = await get_servers_display_names(squad_uuids)
+ return f"{len(squad_uuids)} шт. ({servers_names})"
+ except Exception as e:
+ logger.warning(f"Не удалось получить названия серверов: {e}")
+ return f"{len(squad_uuids)} шт."