from datetime import datetime, timezone from loguru import logger from app.models.payment import Payment, PaymentStatus from app.repositories.payment import PaymentRepository from app.repositories.tariff import TariffRepository from app.repositories.user import UserRepository class PaymentService: def __init__( self, payment_repo: PaymentRepository, user_repo: UserRepository, tariff_repo: TariffRepository, ): self.payment_repo = payment_repo self.user_repo = user_repo self.tariff_repo = tariff_repo async def create( self, user_id: int, tariff_id: int, provider: str, amount: float, currency: str = "RUB", external_id: str | None = None, ) -> Payment: user = await self.user_repo.get(user_id) if user is None: raise ValueError(f"User not found: {user_id}") tariff = await self.tariff_repo.get(tariff_id) if tariff is None: raise ValueError(f"Tariff not found: {tariff_id}") payment = await self.payment_repo.create( user_id=user_id, tariff_id=tariff_id, amount=amount, currency=currency, status=PaymentStatus.PENDING, provider=provider, external_id=external_id, ) logger.info("Payment created: id={} user={} amount={}", payment.id, user_id, amount) return payment async def confirm( self, payment_id: int, external_id: str | None = None ) -> Payment: payment = await self.payment_repo.get(payment_id) if payment is None: raise ValueError(f"Payment not found: {payment_id}") if payment.status != PaymentStatus.PENDING: raise ValueError( f"Cannot confirm payment {payment_id}: " f"current status is {payment.status.value}" ) payment = await self.payment_repo.update( payment_id, status=PaymentStatus.CONFIRMED, paid_at=datetime.now(timezone.utc), external_id=external_id or payment.external_id, ) logger.info("Payment confirmed: id={} user={}", payment_id, payment.user_id) return payment async def fail(self, payment_id: int) -> Payment: payment = await self.payment_repo.get(payment_id) if payment is None: raise ValueError(f"Payment not found: {payment_id}") payment = await self.payment_repo.update( payment_id, status=PaymentStatus.FAILED ) logger.info("Payment failed: id={} user={}", payment_id, payment.user_id) return payment async def refund(self, payment_id: int) -> Payment: payment = await self.payment_repo.get(payment_id) if payment is None: raise ValueError(f"Payment not found: {payment_id}") payment = await self.payment_repo.update( payment_id, status=PaymentStatus.REFUNDED ) logger.info("Payment refunded: id={} user={}", payment_id, payment.user_id) return payment async def get_by_id(self, payment_id: int) -> Payment | None: return await self.payment_repo.get(payment_id) async def get_by_external_id(self, external_id: str) -> Payment | None: return await self.payment_repo.get_by_external_id(external_id) async def get_user_payments(self, user_id: int) -> list[Payment]: return await self.payment_repo.get_by_user_id(user_id) async def get_pending(self) -> list[Payment]: return await self.payment_repo.get_pending_payments()