feat: add withdraw
This commit is contained in:
7
alter_sbp_withdrawals_drop_wallet_addresses.sql
Normal file
7
alter_sbp_withdrawals_drop_wallet_addresses.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE sbp_withdrawals
|
||||
DROP COLUMN IF EXISTS wallet_address,
|
||||
DROP COLUMN IF EXISTS sender_wallet_address;
|
||||
|
||||
COMMIT;
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
from datetime import datetime,timezone
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
import re
|
||||
from ulid import ULID
|
||||
from src.application.abstractions import IUnitOfWork
|
||||
from src.application.contracts import IQueueMessanger,ILogger,IMozenSbpService,SbpBank
|
||||
@@ -48,14 +49,30 @@ class CreateSbpWithdrawalCommand:
|
||||
raise BadRequestException(message='SBP bank not found')
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _normalize_russian_phone(phone: str | None) -> str:
|
||||
digits=re.sub(r'\D','',phone or '')
|
||||
if len(digits)!=11:
|
||||
raise BadRequestException(message='Russian phone number is required for SBP withdrawal')
|
||||
if digits.startswith('8'):
|
||||
digits=f'7{digits[1:]}'
|
||||
if not digits.startswith('7'):
|
||||
raise BadRequestException(message='Russian phone number is required for SBP withdrawal')
|
||||
return f'+{digits}'
|
||||
|
||||
|
||||
@transactional
|
||||
async def __call__(self,*,user_id: str,bank_id: str,phone: str,usdt_amount: Decimal) -> SbpWithdrawalEntity:
|
||||
async def __call__(self,*,user_id: str,bank_id: str,usdt_amount: Decimal) -> SbpWithdrawalEntity:
|
||||
user=await self._unit_of_work.user_repository.get(user_id)
|
||||
if user is None:
|
||||
raise NotFoundException(message='User not found')
|
||||
wallet_address=str(settings.SBP_WITHDRAWAL_WALLET_ADDRESS or '').strip()
|
||||
phone=self._normalize_russian_phone(user.phone)
|
||||
wallet_address=str(settings.CRYPTO_USDT_CONTRACT_ADDRESS or '').strip()
|
||||
if not wallet_address:
|
||||
raise ServiceUnavailableException(message='SBP withdrawal wallet address unavailable')
|
||||
raise ServiceUnavailableException(message='USDT contract address unavailable')
|
||||
sender_wallet_address=str(user.erc20 or '').strip()
|
||||
if not sender_wallet_address:
|
||||
raise BadRequestException(message='User ERC20 wallet address is required for SBP withdrawal')
|
||||
trace_id=self._logger.get_trace_id()
|
||||
withdraw_id=str(ULID())
|
||||
bank_name=await self._bank_name(bank_id)
|
||||
@@ -66,7 +83,6 @@ class CreateSbpWithdrawalCommand:
|
||||
bank_id=bank_id,
|
||||
bank_name=bank_name,
|
||||
phone=phone,
|
||||
wallet_address=wallet_address,
|
||||
usdt_amount=quote.usdt_amount,
|
||||
rub_amount=quote.rub_amount,
|
||||
usdt_exchange_rate=quote.usdt_exchange_rate,
|
||||
@@ -83,6 +99,8 @@ class CreateSbpWithdrawalCommand:
|
||||
'usdt_amount':str(quote.usdt_amount),
|
||||
'trace_id':trace_id,
|
||||
'wallet_address':wallet_address,
|
||||
'sender_wallet_address':sender_wallet_address,
|
||||
'receiver_wallet_address':wallet_address,
|
||||
'withdraw_id':withdraw_id,
|
||||
'message_id':message_id,
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ class SbpWithdrawalEntity:
|
||||
bank_id: str | None = None
|
||||
bank_name: str | None = None
|
||||
phone: str | None = None
|
||||
wallet_address: str | None = None
|
||||
usdt_amount: Decimal | None = None
|
||||
rub_amount: Decimal | None = None
|
||||
usdt_exchange_rate: Decimal | None = None
|
||||
|
||||
@@ -103,7 +103,7 @@ class Settings(BaseSettings):
|
||||
MOZEN_MERCHANT_ID: str = ''
|
||||
MOZEN_PARK_ID: str = ''
|
||||
MOZEN_PARK_TOKEN: str = ''
|
||||
SBP_WITHDRAWAL_WALLET_ADDRESS: str = ''
|
||||
CRYPTO_USDT_CONTRACT_ADDRESS: str = ''
|
||||
|
||||
CLOUD_KASSIR_PUBLIC_ID: str = ''
|
||||
CLOUD_KASSIR_API_SECRET: str = ''
|
||||
@@ -240,8 +240,13 @@ class Settings(BaseSettings):
|
||||
extgw_set('merchant_id', 'MOZEN_MERCHANT_ID')
|
||||
extgw_set('park_id', 'MOZEN_PARK_ID')
|
||||
extgw_set('park_token', 'MOZEN_PARK_TOKEN')
|
||||
extgw_set('wallet_address', 'SBP_WITHDRAWAL_WALLET_ADDRESS')
|
||||
extgw_set('usdt_wallet_address', 'SBP_WITHDRAWAL_WALLET_ADDRESS')
|
||||
|
||||
crypto = read_secret_optional('crypto')
|
||||
if crypto:
|
||||
crypto_ci = {str(k).lower(): v for k, v in crypto.items()}
|
||||
usdt_contract_address = crypto_ci.get('usdt_contract_address')
|
||||
if usdt_contract_address is not None and str(usdt_contract_address).strip():
|
||||
data['CRYPTO_USDT_CONTRACT_ADDRESS'] = str(usdt_contract_address).strip()
|
||||
|
||||
keydb = read_secret('keydb')
|
||||
k_ci = {str(k).lower(): v for k, v in keydb.items()}
|
||||
|
||||
@@ -20,7 +20,6 @@ class SbpWithdrawal(Base,UlidPrimaryKeyMixin,AuditTimestampsMixin):
|
||||
bank_id: Mapped[str]=mapped_column(String(32),nullable=False,index=True)
|
||||
bank_name: Mapped[str]=mapped_column(String(255),nullable=False)
|
||||
phone: Mapped[str]=mapped_column(String(32),nullable=False)
|
||||
wallet_address: Mapped[str]=mapped_column(String(255),nullable=False)
|
||||
usdt_amount: Mapped[Decimal]=mapped_column(Numeric(38,2),nullable=False)
|
||||
rub_amount: Mapped[Decimal]=mapped_column(Numeric(38,2),nullable=False)
|
||||
usdt_exchange_rate: Mapped[Decimal]=mapped_column(Numeric(38,2),nullable=False)
|
||||
|
||||
@@ -26,7 +26,6 @@ class SbpWithdrawalRepository(ISbpWithdrawalRepository):
|
||||
bank_id=model.bank_id,
|
||||
bank_name=model.bank_name,
|
||||
phone=model.phone,
|
||||
wallet_address=model.wallet_address,
|
||||
usdt_amount=model.usdt_amount,
|
||||
rub_amount=model.rub_amount,
|
||||
usdt_exchange_rate=model.usdt_exchange_rate,
|
||||
@@ -48,7 +47,6 @@ class SbpWithdrawalRepository(ISbpWithdrawalRepository):
|
||||
'bank_id':withdrawal.bank_id,
|
||||
'bank_name':withdrawal.bank_name,
|
||||
'phone':withdrawal.phone,
|
||||
'wallet_address':withdrawal.wallet_address,
|
||||
'usdt_amount':withdrawal.usdt_amount,
|
||||
'rub_amount':withdrawal.rub_amount,
|
||||
'usdt_exchange_rate':withdrawal.usdt_exchange_rate,
|
||||
|
||||
@@ -10,7 +10,7 @@ from src.presentation.dependencies.commands import get_crypto_transfer_completed
|
||||
from src.presentation.dependencies.logger import get_logger
|
||||
|
||||
|
||||
crypto_transfer_router=RabbitRouter(settings.RABBIT_URL)
|
||||
crypto_transfer_router=RabbitRouter(settings.RABBIT_URL,schema_url=None)
|
||||
|
||||
|
||||
class CryptoTransferCompletedMessage(BaseModel):
|
||||
|
||||
@@ -11,7 +11,7 @@ from src.presentation.dependencies.commands import get_sbp_withdrawal_wallet_eve
|
||||
from src.presentation.dependencies.logger import get_logger
|
||||
|
||||
|
||||
sbp_withdrawal_messaging_router=RabbitRouter(settings.RABBIT_URL)
|
||||
sbp_withdrawal_messaging_router=RabbitRouter(settings.RABBIT_URL,schema_url=None)
|
||||
|
||||
|
||||
class SbpWithdrawalWalletEventMessage(BaseModel):
|
||||
|
||||
@@ -119,7 +119,6 @@ def _withdrawal_operation_response(withdrawal: SbpWithdrawalEntity) -> SbpWithdr
|
||||
bank_id=withdrawal.bank_id,
|
||||
bank_name=withdrawal.bank_name,
|
||||
phone=withdrawal.phone,
|
||||
wallet_address=withdrawal.wallet_address,
|
||||
usdt_amount=str(withdrawal.usdt_amount) if withdrawal.usdt_amount is not None else None,
|
||||
rub_amount=str(withdrawal.rub_amount) if withdrawal.rub_amount is not None else None,
|
||||
usdt_exchange_rate=str(withdrawal.usdt_exchange_rate) if withdrawal.usdt_exchange_rate is not None else None,
|
||||
|
||||
@@ -34,7 +34,6 @@ def _withdrawal_response(withdrawal: SbpWithdrawalEntity) -> SbpWithdrawalRespon
|
||||
bank_id=withdrawal.bank_id,
|
||||
bank_name=withdrawal.bank_name,
|
||||
phone=withdrawal.phone,
|
||||
wallet_address=withdrawal.wallet_address,
|
||||
usdt_amount=str(withdrawal.usdt_amount) if withdrawal.usdt_amount is not None else None,
|
||||
rub_amount=str(withdrawal.rub_amount) if withdrawal.rub_amount is not None else None,
|
||||
usdt_exchange_rate=str(withdrawal.usdt_exchange_rate) if withdrawal.usdt_exchange_rate is not None else None,
|
||||
@@ -84,7 +83,6 @@ async def create_sbp_withdrawal(
|
||||
withdrawal=await command(
|
||||
user_id=auth.user_id,
|
||||
bank_id=payload.bank_id,
|
||||
phone=payload.phone,
|
||||
usdt_amount=payload.usdt_amount,
|
||||
)
|
||||
return CreateSbpWithdrawalResponse(status_code=201,withdrawal=_withdrawal_response(withdrawal))
|
||||
|
||||
@@ -59,7 +59,6 @@ class SbpWithdrawalOperationResponse(BaseModel):
|
||||
bank_id: str | None = None
|
||||
bank_name: str | None = None
|
||||
phone: str | None = None
|
||||
wallet_address: str | None = None
|
||||
usdt_amount: str | None = None
|
||||
rub_amount: str | None = None
|
||||
usdt_exchange_rate: str | None = None
|
||||
|
||||
@@ -15,7 +15,6 @@ class SbpBanksResponse(BaseModel):
|
||||
|
||||
class CreateSbpWithdrawal(BaseModel):
|
||||
bank_id: str = Field(min_length=1,max_length=32)
|
||||
phone: str = Field(min_length=5,max_length=32)
|
||||
usdt_amount: Decimal = Field(ge=Decimal('100'),le=Decimal('5000'),decimal_places=2,max_digits=20)
|
||||
|
||||
|
||||
@@ -27,7 +26,6 @@ class SbpWithdrawalResponse(BaseModel):
|
||||
bank_id: str | None = None
|
||||
bank_name: str | None = None
|
||||
phone: str | None = None
|
||||
wallet_address: str | None = None
|
||||
usdt_amount: str | None = None
|
||||
rub_amount: str | None = None
|
||||
usdt_exchange_rate: str | None = None
|
||||
|
||||
Reference in New Issue
Block a user