From 39979f9976683d832bfc8c46036c581b2f45308c Mon Sep 17 00:00:00 2001 From: Noloquideus Date: Sun, 21 Jun 2026 15:00:59 +0300 Subject: [PATCH] feat: add withdraw --- ..._sbp_withdrawals_drop_wallet_addresses.sql | 7 +++++ .../commands/sbp_withdrawal_commands.py | 26 ++++++++++++++++--- .../domain/entities/sbp_withdrawal.py | 1 - src/infrastructure/config/settings.py | 11 +++++--- .../database/models/sbp_withdrawal.py | 1 - .../repositories/sbp_withdrawal_repository.py | 2 -- src/presentation/messaging/crypto_transfer.py | 2 +- src/presentation/messaging/sbp_withdrawal.py | 2 +- src/presentation/routing/order.py | 1 - src/presentation/routing/sbp_withdrawal.py | 2 -- src/presentation/schemas/order.py | 1 - src/presentation/schemas/sbp_withdrawal.py | 2 -- 12 files changed, 39 insertions(+), 19 deletions(-) create mode 100644 alter_sbp_withdrawals_drop_wallet_addresses.sql diff --git a/alter_sbp_withdrawals_drop_wallet_addresses.sql b/alter_sbp_withdrawals_drop_wallet_addresses.sql new file mode 100644 index 0000000..d5dd639 --- /dev/null +++ b/alter_sbp_withdrawals_drop_wallet_addresses.sql @@ -0,0 +1,7 @@ +BEGIN; + +ALTER TABLE sbp_withdrawals + DROP COLUMN IF EXISTS wallet_address, + DROP COLUMN IF EXISTS sender_wallet_address; + +COMMIT; diff --git a/src/application/commands/sbp_withdrawal_commands.py b/src/application/commands/sbp_withdrawal_commands.py index c932d09..d607cbd 100644 --- a/src/application/commands/sbp_withdrawal_commands.py +++ b/src/application/commands/sbp_withdrawal_commands.py @@ -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, } diff --git a/src/application/domain/entities/sbp_withdrawal.py b/src/application/domain/entities/sbp_withdrawal.py index c8d64b6..e14c5de 100644 --- a/src/application/domain/entities/sbp_withdrawal.py +++ b/src/application/domain/entities/sbp_withdrawal.py @@ -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 diff --git a/src/infrastructure/config/settings.py b/src/infrastructure/config/settings.py index 062d24d..7961b9d 100644 --- a/src/infrastructure/config/settings.py +++ b/src/infrastructure/config/settings.py @@ -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()} diff --git a/src/infrastructure/database/models/sbp_withdrawal.py b/src/infrastructure/database/models/sbp_withdrawal.py index a41c890..60789b5 100644 --- a/src/infrastructure/database/models/sbp_withdrawal.py +++ b/src/infrastructure/database/models/sbp_withdrawal.py @@ -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) diff --git a/src/infrastructure/database/repositories/sbp_withdrawal_repository.py b/src/infrastructure/database/repositories/sbp_withdrawal_repository.py index f357f9e..d9b6940 100644 --- a/src/infrastructure/database/repositories/sbp_withdrawal_repository.py +++ b/src/infrastructure/database/repositories/sbp_withdrawal_repository.py @@ -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, diff --git a/src/presentation/messaging/crypto_transfer.py b/src/presentation/messaging/crypto_transfer.py index f23a7e4..9d774bc 100644 --- a/src/presentation/messaging/crypto_transfer.py +++ b/src/presentation/messaging/crypto_transfer.py @@ -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): diff --git a/src/presentation/messaging/sbp_withdrawal.py b/src/presentation/messaging/sbp_withdrawal.py index 985c336..53fa1af 100644 --- a/src/presentation/messaging/sbp_withdrawal.py +++ b/src/presentation/messaging/sbp_withdrawal.py @@ -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): diff --git a/src/presentation/routing/order.py b/src/presentation/routing/order.py index 938bec3..aa283c0 100644 --- a/src/presentation/routing/order.py +++ b/src/presentation/routing/order.py @@ -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, diff --git a/src/presentation/routing/sbp_withdrawal.py b/src/presentation/routing/sbp_withdrawal.py index b75a58a..843145a 100644 --- a/src/presentation/routing/sbp_withdrawal.py +++ b/src/presentation/routing/sbp_withdrawal.py @@ -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)) diff --git a/src/presentation/schemas/order.py b/src/presentation/schemas/order.py index 691b972..94d17cf 100644 --- a/src/presentation/schemas/order.py +++ b/src/presentation/schemas/order.py @@ -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 diff --git a/src/presentation/schemas/sbp_withdrawal.py b/src/presentation/schemas/sbp_withdrawal.py index 281863b..8c028ad 100644 --- a/src/presentation/schemas/sbp_withdrawal.py +++ b/src/presentation/schemas/sbp_withdrawal.py @@ -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