feat: add mpre endpoints
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
from src.application.commands.create_order_command import CreateOrderCommand
|
||||
from src.application.commands.create_payment_command import CreatePaymentCommand
|
||||
from src.application.commands.create_crypto_transfer_completed_command import CreateCryptoTransferCompletedCommand
|
||||
from src.application.commands.create_crypto_transfer_completed_command import CreateCryptoTransferCompletedCommand
|
||||
from src.application.commands.payment_read_commands import GetOrderCommand,GetOrderStatusCommand,GetPaymentConfigCommand,GetPaymentQuoteCommand,ListOrdersCommand,ListPaymentsCommand,OrderPaymentResult
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
from decimal import Decimal
|
||||
from src.application.abstractions import IUnitOfWork
|
||||
from src.application.contracts import ILogger,IReceipt
|
||||
from src.application.domain.enums import PaymentStatus
|
||||
from src.application.domain.exceptions import ApplicationException
|
||||
from src.infrastructure.database.decorators import transactional
|
||||
|
||||
@@ -62,18 +63,26 @@ class CreateCryptoTransferCompletedCommand:
|
||||
if principal_amount < 0:
|
||||
raise ApplicationException(status_code=400, message='Invalid receipt amounts: principal negative')
|
||||
|
||||
receipt_response = await self._receipt.create_receipt(
|
||||
order_id=order_id,
|
||||
user_id=user_id,
|
||||
email=email,
|
||||
total_amount=total_amount,
|
||||
principal_amount=principal_amount,
|
||||
service_fee=service_fee,
|
||||
customer_info=customer_info,
|
||||
customer_inn=customer_inn,
|
||||
customer_birthday=customer_birthday,
|
||||
request_id=self._logger.get_trace_id(),
|
||||
)
|
||||
try:
|
||||
receipt_response = await self._receipt.create_receipt(
|
||||
order_id=order_id,
|
||||
user_id=user_id,
|
||||
email=email,
|
||||
total_amount=total_amount,
|
||||
principal_amount=principal_amount,
|
||||
service_fee=service_fee,
|
||||
customer_info=customer_info,
|
||||
customer_inn=customer_inn,
|
||||
customer_birthday=customer_birthday,
|
||||
request_id=self._logger.get_trace_id(),
|
||||
)
|
||||
except ApplicationException as exception:
|
||||
self._logger.error({'event':'receipt_create_failed','order_id':order_id,'error':exception.message})
|
||||
await self._unit_of_work.payment_repository.update_status(
|
||||
order_id=order_id,
|
||||
status=PaymentStatus.RECEIPT_ERROR,
|
||||
)
|
||||
return
|
||||
receipt_model = receipt_response.get('Model')
|
||||
if not isinstance(receipt_model, dict):
|
||||
receipt_model = {}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
from __future__ import annotations
|
||||
from datetime import datetime, timezone
|
||||
from decimal import Decimal, ROUND_UP
|
||||
from decimal import Decimal
|
||||
from ulid import ULID
|
||||
from src.application.abstractions import IUnitOfWork
|
||||
from src.application.contracts import ICache,ILogger
|
||||
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,ServiceUnavailableException
|
||||
from src.infrastructure.config import settings
|
||||
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
|
||||
|
||||
@@ -20,12 +20,12 @@ class CreateOrderCommand:
|
||||
*,
|
||||
unit_of_work: IUnitOfWork,
|
||||
logger: ILogger,
|
||||
remote_cache: ICache,
|
||||
payment_quote_service: PaymentQuoteService,
|
||||
itpay_service: IItPayService,
|
||||
) -> None:
|
||||
self._unit_of_work = unit_of_work
|
||||
self._logger = logger
|
||||
self._remote_cache = remote_cache
|
||||
self._payment_quote_service = payment_quote_service
|
||||
self._itpay_service = itpay_service
|
||||
|
||||
|
||||
@@ -33,22 +33,11 @@ class CreateOrderCommand:
|
||||
async def __call__(self, payment_data: CreateOrder, user_id: str) -> OrderEntity:
|
||||
client_payment_id = str(ULID())
|
||||
|
||||
rate_raw = await self._remote_cache.hget('tradex:rub:rate','value')
|
||||
gas_raw = await self._remote_cache.hget('gwei:eth:last','normal_rub')
|
||||
self._logger.info(f'Actual market values: rate={rate_raw}, gas={gas_raw}')
|
||||
|
||||
if rate_raw is None:
|
||||
self._logger.error('Exchange rate unavailable')
|
||||
raise ServiceUnavailableException(message='Exchange rate unavailable')
|
||||
|
||||
if gas_raw is None:
|
||||
self._logger.error('Exchange gas unavailable')
|
||||
raise ServiceUnavailableException(message='Exchange gas unavailable')
|
||||
|
||||
actual_gas_fee = Decimal(gas_raw).quantize(Decimal('0.00'), rounding=ROUND_UP)
|
||||
actual_usdt_exchange_rate = Decimal(rate_raw).quantize(Decimal('0.00'), rounding=ROUND_UP)
|
||||
actual_service_fee = (payment_data.usdt_amount * actual_usdt_exchange_rate * settings.PAYMENT_SERVICE_FEE_RATE).quantize(Decimal('0.01'))
|
||||
actual_total_price = (payment_data.usdt_amount * actual_usdt_exchange_rate + actual_service_fee + actual_gas_fee).quantize(Decimal('0.01'))
|
||||
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')
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
from ulid import ULID
|
||||
from src.application.abstractions import IUnitOfWork
|
||||
from src.application.contracts import ILogger,IQueueMessanger
|
||||
from src.application.domain.enums import PaymentStatus
|
||||
from src.application.domain.exceptions import ApplicationException
|
||||
from src.infrastructure.config import settings
|
||||
from src.infrastructure.database.decorators import transactional
|
||||
@@ -52,3 +53,7 @@ class CreatePaymentCommand:
|
||||
correlation_id=message['trace_id'],
|
||||
headers={'trace_id': trace_id},
|
||||
)
|
||||
await self._unit_of_work.payment_repository.update_status(
|
||||
order_id=order_id,
|
||||
status=PaymentStatus.WEB3_PROCESSING,
|
||||
)
|
||||
|
||||
111
src/application/commands/payment_read_commands.py
Normal file
111
src/application/commands/payment_read_commands.py
Normal file
@@ -0,0 +1,111 @@
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from decimal import Decimal
|
||||
from src.application.abstractions import IUnitOfWork
|
||||
from src.application.contracts import ILogger
|
||||
from src.application.domain.entities import OrderEntity,PaymentEntity
|
||||
from src.application.domain.exceptions import NotFoundException
|
||||
from src.application.services import PaymentQuote,PaymentQuoteService
|
||||
from src.infrastructure.database.decorators import transactional
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class OrderPaymentResult:
|
||||
order: OrderEntity
|
||||
payment: PaymentEntity | None
|
||||
|
||||
|
||||
class GetPaymentConfigCommand:
|
||||
def __init__(self, *, payment_quote_service: PaymentQuoteService, logger: ILogger):
|
||||
self._payment_quote_service = payment_quote_service
|
||||
self._logger = logger
|
||||
|
||||
|
||||
async def __call__(self) -> PaymentQuote:
|
||||
quote = await self._payment_quote_service.get_quote(Decimal('1.00'))
|
||||
self._logger.info({'event':'payment_config_requested'})
|
||||
return quote
|
||||
|
||||
|
||||
class GetPaymentQuoteCommand:
|
||||
def __init__(self, *, payment_quote_service: PaymentQuoteService, logger: ILogger):
|
||||
self._payment_quote_service = payment_quote_service
|
||||
self._logger = logger
|
||||
|
||||
|
||||
async def __call__(self, usdt_amount: Decimal) -> PaymentQuote:
|
||||
quote = await self._payment_quote_service.get_quote(usdt_amount)
|
||||
self._logger.info({'event':'payment_quote_requested','usdt_amount':str(usdt_amount)})
|
||||
return quote
|
||||
|
||||
|
||||
class ListOrdersCommand:
|
||||
def __init__(self, *, unit_of_work: IUnitOfWork, logger: ILogger):
|
||||
self._unit_of_work = unit_of_work
|
||||
self._logger = logger
|
||||
|
||||
|
||||
@transactional
|
||||
async def __call__(self, *, user_id: str, limit: int, offset: int) -> list[OrderPaymentResult]:
|
||||
orders = await self._unit_of_work.order_repository.list_by_user_id(
|
||||
user_id=user_id,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
)
|
||||
items: list[OrderPaymentResult] = []
|
||||
for order in orders:
|
||||
if order.id is None:
|
||||
continue
|
||||
payment = await self._unit_of_work.payment_repository.get_by_order_id(order.id)
|
||||
items.append(OrderPaymentResult(order=order,payment=payment))
|
||||
self._logger.info({'event':'orders_list_requested','user_id':user_id,'limit':limit,'offset':offset})
|
||||
return items
|
||||
|
||||
|
||||
class ListPaymentsCommand:
|
||||
def __init__(self, *, unit_of_work: IUnitOfWork, logger: ILogger):
|
||||
self._unit_of_work = unit_of_work
|
||||
self._logger = logger
|
||||
|
||||
|
||||
@transactional
|
||||
async def __call__(self, *, user_id: str, limit: int, offset: int) -> list[PaymentEntity]:
|
||||
payments = await self._unit_of_work.payment_repository.list_by_user_id(
|
||||
user_id=user_id,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
)
|
||||
self._logger.info({'event':'payments_list_requested','user_id':user_id,'limit':limit,'offset':offset})
|
||||
return payments
|
||||
|
||||
|
||||
class GetOrderCommand:
|
||||
def __init__(self, *, unit_of_work: IUnitOfWork, logger: ILogger):
|
||||
self._unit_of_work = unit_of_work
|
||||
self._logger = logger
|
||||
|
||||
|
||||
@transactional
|
||||
async def __call__(self, *, order_id: str, user_id: str) -> OrderPaymentResult:
|
||||
order = await self._unit_of_work.order_repository.get_by_id_for_user(order_id=order_id,user_id=user_id)
|
||||
if order is None:
|
||||
raise NotFoundException(message='Order not found')
|
||||
payment = await self._unit_of_work.payment_repository.get_by_order_id(order_id)
|
||||
self._logger.info({'event':'order_detail_requested','order_id':order_id,'user_id':user_id})
|
||||
return OrderPaymentResult(order=order,payment=payment)
|
||||
|
||||
|
||||
class GetOrderStatusCommand:
|
||||
def __init__(self, *, unit_of_work: IUnitOfWork, logger: ILogger):
|
||||
self._unit_of_work = unit_of_work
|
||||
self._logger = logger
|
||||
|
||||
|
||||
@transactional
|
||||
async def __call__(self, *, order_id: str, user_id: str) -> OrderPaymentResult:
|
||||
order = await self._unit_of_work.order_repository.get_by_id_for_user(order_id=order_id,user_id=user_id)
|
||||
if order is None:
|
||||
raise NotFoundException(message='Order not found')
|
||||
payment = await self._unit_of_work.payment_repository.get_by_order_id(order_id)
|
||||
self._logger.info({'event':'order_status_requested','order_id':order_id,'user_id':user_id})
|
||||
return OrderPaymentResult(order=order,payment=payment)
|
||||
Reference in New Issue
Block a user