Initial commit
This commit is contained in:
114
src/presentation/routing/order.py
Normal file
114
src/presentation/routing/order.py
Normal file
@@ -0,0 +1,114 @@
|
||||
import json
|
||||
from decimal import Decimal
|
||||
from urllib.parse import parse_qs
|
||||
import aiohttp
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from fastapi.responses import ORJSONResponse
|
||||
from ulid import ULID
|
||||
from src.application.contracts import ILogger
|
||||
from src.application.domain.dto import AuthContext
|
||||
from src.application.domain.exceptions import ApplicationException
|
||||
from src.presentation.decorators import csrf_protect, require_access_token
|
||||
from src.presentation.dependencies.logger import get_logger
|
||||
from src.presentation.schemas.order import CreateOrder
|
||||
|
||||
|
||||
order_router = APIRouter(prefix='/order', tags=['orders'])
|
||||
|
||||
ITPAY_API_BASE = 'https://api.gw.itpay.ru'
|
||||
ITPAY_AUTHORIZATION = 'Token REPLACE_WITH_JWT_FROM_ITPAY_DASHBOARD'
|
||||
HARDCODED_USDT_TO_RUB = Decimal('100')
|
||||
HARDCODED_GAS_RUB = Decimal('15')
|
||||
HARDCODED_OUR_COMMISSION_RUB = Decimal('25')
|
||||
|
||||
|
||||
def _amount_rub_for_itpay(amount_usdt: Decimal) -> Decimal:
|
||||
return (amount_usdt * HARDCODED_USDT_TO_RUB + HARDCODED_GAS_RUB + HARDCODED_OUR_COMMISSION_RUB).quantize(Decimal('0.01'))
|
||||
|
||||
|
||||
|
||||
@order_router.post('/create')
|
||||
#@csrf_protect()
|
||||
async def create_order(
|
||||
request: Request,
|
||||
body: CreateOrder,
|
||||
#auth: AuthContext = Depends(require_access_token),
|
||||
logger: ILogger = Depends(get_logger),
|
||||
) -> ORJSONResponse:
|
||||
amount_rub = _amount_rub_for_itpay(body.amount_usdt)
|
||||
amount_str = str(amount_rub)
|
||||
client_payment_id = str(ULID())
|
||||
payload = {
|
||||
'amount': amount_str,
|
||||
'client_payment_id': client_payment_id,
|
||||
'description': f'USDT {body.amount_usdt}',
|
||||
'metadata': {
|
||||
'user_id': '01KPSYW27JZ26HBDR3QS5J6VMS',
|
||||
'amount_usdt': str(body.amount_usdt),
|
||||
'rate': str(HARDCODED_USDT_TO_RUB),
|
||||
'gas_rub': str(HARDCODED_GAS_RUB),
|
||||
'commission_rub': str(HARDCODED_OUR_COMMISSION_RUB),
|
||||
},
|
||||
}
|
||||
url = f'{ITPAY_API_BASE}/v1/payments'
|
||||
headers = {
|
||||
'Authorization': ITPAY_AUTHORIZATION,
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
try:
|
||||
timeout = aiohttp.ClientTimeout(total=30)
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
async with session.post(url, json=payload, headers=headers) as resp:
|
||||
response_text = await resp.text()
|
||||
try:
|
||||
response_json = json.loads(response_text)
|
||||
except json.JSONDecodeError:
|
||||
response_json = {'raw': response_text}
|
||||
if resp.status >= 400:
|
||||
logger.warning(f'itpay payments POST {resp.status} {response_text}')
|
||||
raise ApplicationException(status_code=502, message='Payment provider error')
|
||||
except ApplicationException:
|
||||
raise
|
||||
except aiohttp.ClientError as e:
|
||||
logger.error(str(e))
|
||||
raise ApplicationException(status_code=502, message='Payment provider unreachable')
|
||||
return ORJSONResponse(
|
||||
content={
|
||||
'itpay': response_json,
|
||||
'client_payment_id': client_payment_id,
|
||||
'amount_usdt': str(body.amount_usdt),
|
||||
'amount_rub': amount_str,
|
||||
'hardcoded': {
|
||||
'usdt_to_rub': str(HARDCODED_USDT_TO_RUB),
|
||||
'gas_rub': str(HARDCODED_GAS_RUB),
|
||||
'commission_rub': str(HARDCODED_OUR_COMMISSION_RUB),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
@order_router.post('/webhook/itpay')
|
||||
async def itpay_webhook(request: Request, logger: ILogger = Depends(get_logger)) -> ORJSONResponse:
|
||||
raw = await request.body()
|
||||
ct = (request.headers.get('content-type') or '').lower()
|
||||
if 'application/json' in ct:
|
||||
try:
|
||||
parsed = json.loads(raw.decode('utf-8'))
|
||||
except (json.JSONDecodeError, UnicodeDecodeError):
|
||||
parsed = raw.decode('utf-8', errors='replace')
|
||||
elif 'application/x-www-form-urlencoded' in ct:
|
||||
decoded = raw.decode('utf-8', errors='replace')
|
||||
qs = parse_qs(decoded, keep_blank_values=True)
|
||||
parsed = {k: (vals[0] if len(vals) == 1 else vals) for k, vals in qs.items()}
|
||||
else:
|
||||
parsed = raw.decode('utf-8', errors='replace')
|
||||
log_payload = {
|
||||
'method': request.method,
|
||||
'url': str(request.url),
|
||||
'headers': {k: v for k, v in request.headers.items()},
|
||||
'body': parsed,
|
||||
}
|
||||
logger.info(json.dumps(log_payload, ensure_ascii=False, default=str))
|
||||
return ORJSONResponse(content={'status': 0})
|
||||
Reference in New Issue
Block a user