Интегрировал биллинг при оплате подписки
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
from decimal import Decimal
|
||||
import json
|
||||
from instance.model import User, Subscription, Transaction
|
||||
from app.services.billing_service import BillingAdapter
|
||||
from app.services.marzban import MarzbanService, MarzbanUser
|
||||
from .postgres_rep import PostgresRepository
|
||||
from instance.model import Transaction,TransactionType, Plan
|
||||
@@ -14,13 +15,14 @@ from uuid import UUID
|
||||
|
||||
|
||||
class DatabaseManager:
|
||||
def __init__(self, session_generator,marzban_username,marzban_password,marzban_url):
|
||||
def __init__(self, session_generator,marzban_username,marzban_password,marzban_url,billing_base_url):
|
||||
"""
|
||||
Инициализация с асинхронным генератором сессий (например, get_postgres_session).
|
||||
"""
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.postgres_repo = PostgresRepository(session_generator, self.logger)
|
||||
self.marzban_service = MarzbanService(marzban_url,marzban_username,marzban_password)
|
||||
self.billing_adapter = BillingAdapter(billing_base_url)
|
||||
|
||||
async def create_user(self, telegram_id: int, invented_by: Optional[int]= None):
|
||||
"""
|
||||
@@ -59,24 +61,24 @@ class DatabaseManager:
|
||||
"""
|
||||
return await self.postgres_repo.get_last_transactions(telegram_id, limit)
|
||||
|
||||
async def update_balance(self, telegram_id: int, amount: float):
|
||||
"""
|
||||
Обновляет баланс пользователя и добавляет транзакцию.
|
||||
"""
|
||||
self.logger.info(f"Попытка обновления баланса: telegram_id={telegram_id}, amount={amount}")
|
||||
user = await self.get_user_by_telegram_id(telegram_id)
|
||||
if not user:
|
||||
self.logger.warning(f"Пользователь с Telegram ID {telegram_id} не найден.")
|
||||
return "ERROR"
|
||||
# async def update_balance(self, telegram_id: int, amount: float):
|
||||
# """
|
||||
# Обновляет баланс пользователя и добавляет транзакцию.
|
||||
# """
|
||||
# self.logger.info(f"Попытка обновления баланса: telegram_id={telegram_id}, amount={amount}")
|
||||
# user = await self.get_user_by_telegram_id(telegram_id)
|
||||
# if not user:
|
||||
# self.logger.warning(f"Пользователь с Telegram ID {telegram_id} не найден.")
|
||||
# return "ERROR"
|
||||
|
||||
updated = await self.postgres_repo.update_balance(user, amount)
|
||||
if not updated:
|
||||
self.logger.error(f"Не удалось обновить баланс пользователя {telegram_id}")
|
||||
return "ERROR"
|
||||
# updated = await self.postgres_repo.update_balance(user, amount)
|
||||
# if not updated:
|
||||
# self.logger.error(f"Не удалось обновить баланс пользователя {telegram_id}")
|
||||
# return "ERROR"
|
||||
|
||||
self.logger.info(f"Баланс пользователя {telegram_id} обновлен на {amount}, добавление транзакции")
|
||||
await self.add_transaction(user.telegram_id, amount)
|
||||
return "OK"
|
||||
# self.logger.info(f"Баланс пользователя {telegram_id} обновлен на {amount}, добавление транзакции")
|
||||
# await self.add_transaction(user.telegram_id, amount)
|
||||
# return "OK"
|
||||
|
||||
|
||||
async def get_active_subscription(self, telegram_id: int):
|
||||
@@ -105,67 +107,59 @@ class DatabaseManager:
|
||||
Возвращает список последних подписок.
|
||||
"""
|
||||
return await self.postgres_repo.get_last_subscription_by_user_id(telegram_id)
|
||||
|
||||
|
||||
async def buy_sub(self, telegram_id: int, plan_name: str):
|
||||
"""
|
||||
Покупает подписку.
|
||||
Покупка подписки: сначала создаем подписку, потом списываем деньги
|
||||
"""
|
||||
try:
|
||||
self.logger.info(f"Начало покупки подписки для пользователя {telegram_id}, план: {plan_name}")
|
||||
active_subscription = await self.get_active_subscription(telegram_id)
|
||||
self.logger.info(f"Активная подписка: {active_subscription}")
|
||||
|
||||
if active_subscription:
|
||||
self.logger.error(f"Пользователь {telegram_id} уже имеет активную подписку.")
|
||||
self.logger.info(f"Покупка подписки: user={telegram_id}, plan={plan_name}")
|
||||
|
||||
# 1. Проверка активной подписки
|
||||
if await self.get_active_subscription(telegram_id):
|
||||
return "ACTIVE_SUBSCRIPTION_EXISTS"
|
||||
|
||||
result = await self._initialize_user_and_plan(telegram_id, plan_name)
|
||||
if isinstance(result, str):
|
||||
return result # Возвращает "ERROR", "TARIFF_NOT_FOUND" или "INSUFFICIENT_FUNDS"
|
||||
|
||||
user, plan = result
|
||||
self.logger.info(f"Пользователь и план найдены: user_id={user.telegram_id}, plan_price={plan.price}")
|
||||
|
||||
new_subscription = await self._create_subscription_and_add_client(user, plan)
|
||||
if not new_subscription:
|
||||
self.logger.error(f"Не удалось создать подписку для пользователя {telegram_id}")
|
||||
return "ERROR"
|
||||
|
||||
updated = await self.postgres_repo.update_balance(user,-plan.price)
|
||||
if updated == False:
|
||||
self.logger.error(f"Не удалось обновить баланс для пользователя {telegram_id}")
|
||||
return "ERROR"
|
||||
|
||||
self.logger.info(f"Подписка успешно оформлена для пользователя {telegram_id}.")
|
||||
return {"status": "OK", "subscription_id": str(new_subscription.id)}
|
||||
except Exception as e:
|
||||
self.logger.error(f"Неожиданная ошибка в buy_sub: {str(e)}")
|
||||
return "ERROR"
|
||||
|
||||
async def _initialize_user_and_plan(self, telegram_id, plan_name):
|
||||
"""
|
||||
Инициализирует пользователя и план подписки.
|
||||
"""
|
||||
try:
|
||||
|
||||
user = await self.get_user_by_telegram_id(telegram_id)
|
||||
if not user:
|
||||
self.logger.error(f"Пользователь с Telegram ID {telegram_id} не найден.")
|
||||
return "ERROR"
|
||||
|
||||
# 2. Получаем план
|
||||
plan = await self.postgres_repo.get_subscription_plan(plan_name)
|
||||
if not plan:
|
||||
self.logger.error(f"Тарифный план {plan_name} не найден.")
|
||||
return "TARIFF_NOT_FOUND"
|
||||
|
||||
cost = plan.price
|
||||
if user.balance < cost:
|
||||
self.logger.error(f"Недостаточно средств у пользователя {telegram_id} для покупки плана {plan_name}.")
|
||||
# 3. Проверяем пользователя
|
||||
user = await self.get_user_by_telegram_id(telegram_id)
|
||||
if not user:
|
||||
return "USER_NOT_FOUND"
|
||||
|
||||
# 4. Проверяем баланс (только для информации)
|
||||
balance_result = await self.billing_adapter.get_balance(telegram_id)
|
||||
if balance_result["status"] == "error":
|
||||
return "BILLING_SERVICE_ERROR"
|
||||
|
||||
if balance_result["balance"] < plan.price:
|
||||
return "INSUFFICIENT_FUNDS"
|
||||
|
||||
return user, plan
|
||||
# 5. СОЗДАЕМ ПОДПИСКУ (самое важное - сначала!)
|
||||
new_subscription = await self._create_subscription_and_add_client(user, plan)
|
||||
if not new_subscription:
|
||||
return "SUBSCRIPTION_CREATION_FAILED"
|
||||
|
||||
# 6. ТОЛЬКО ПОСЛЕ УСПЕШНОГО СОЗДАНИЯ ПОДПИСКИ - списываем деньги
|
||||
withdraw_result = await self.billing_adapter.withdraw_funds(
|
||||
telegram_id,
|
||||
float(plan.price),
|
||||
f"Оплата подписки {plan_name}"
|
||||
)
|
||||
|
||||
if withdraw_result["status"] == "error":
|
||||
await self.postgres_repo.delete_subscription(new_subscription.id)
|
||||
self.logger.error(f"Payment failed but subscription created: {new_subscription.id}")
|
||||
return "PAYMENT_FAILED_AFTER_SUBSCRIPTION"
|
||||
|
||||
# 7. ВСЕ УСПЕШНО
|
||||
self.logger.info(f"Подписка успешно создана и оплачена: {new_subscription.id}")
|
||||
return {"status": "OK", "subscription_id": str(new_subscription.id)}
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Неожиданная ошибка в _initialize_user_and_plan: {str(e)}")
|
||||
self.logger.error(f"Ошибка в buy_sub: {str(e)}")
|
||||
return "ERROR"
|
||||
|
||||
async def _create_subscription_and_add_client(self, user: User, plan: Plan):
|
||||
|
||||
Reference in New Issue
Block a user