from __future__ import annotations from datetime import datetime, timezone from decimal import Decimal from ulid import ULID from src.application.abstractions import IUnitOfWork from src.application.contracts import ILogger from src.application.contracts import IItPayService from src.application.domain.entities.order import OrderEntity from src.application.domain.enums import OrderStatus from src.application.domain.exceptions import ApplicationException from src.application.services import PaymentQuoteService from src.infrastructure.database.decorators import transactional from src.presentation.schemas.order import CreateOrder class CreateOrderCommand: def __init__( self, *, unit_of_work: IUnitOfWork, logger: ILogger, payment_quote_service: PaymentQuoteService, itpay_service: IItPayService, ) -> None: self._unit_of_work = unit_of_work self._logger = logger self._payment_quote_service = payment_quote_service self._itpay_service = itpay_service @transactional async def __call__(self, payment_data: CreateOrder, user_id: str) -> OrderEntity: client_payment_id = str(ULID()) quote = await self._payment_quote_service.get_quote(payment_data.usdt_amount) actual_gas_fee = quote.gas_fee actual_usdt_exchange_rate = quote.usdt_exchange_rate actual_service_fee = quote.service_fee actual_total_price = quote.total_price if actual_total_price > payment_data.total_price * Decimal('1.01'): self._logger.error('Price has changed, please refresh and try again') raise ApplicationException(status_code=409, message='Price has changed, please refresh and try again') order = OrderEntity( user_id=user_id, usdt_amount=payment_data.usdt_amount, usdt_exchange_rate=actual_usdt_exchange_rate, gas_fee=actual_gas_fee, service_fee=actual_service_fee, total_price=actual_total_price, status=OrderStatus.PENDING, created_at=datetime.now(timezone.utc), client_payment_id=client_payment_id, ) saved = await self._unit_of_work.order_repository.create(order) with_itpay = await self._itpay_service.create_payment(saved,self._logger.get_trace_id()) if with_itpay.status in ( OrderStatus.CANCELLED, OrderStatus.REJECTED, OrderStatus.ERROR, ): await self._unit_of_work.order_repository.update_after_itpay_failure(with_itpay) else: await self._unit_of_work.order_repository.update_after_itpay_payment_created(with_itpay) return with_itpay