feat: update for b2b

This commit is contained in:
2026-06-02 23:45:32 +03:00
parent 31d097c3c2
commit c06a01259d
7 changed files with 33 additions and 3 deletions

View File

@@ -20,6 +20,11 @@ class IUserRepository(ABC):
raise NotImplementedError
@abstractmethod
async def get_user_by_id(self, user_id: str) -> UserEntity:
raise NotImplementedError
@abstractmethod
async def exists_verified_by_passport(self,*,passport_data: str,exclude_user_id: str) -> bool:
raise NotImplementedError

View File

@@ -6,7 +6,7 @@ from src.application.abstractions import IUnitOfWork
from src.application.contracts import IBeorgService,ILogger
from src.application.domain.dto import BeorgKycCreateResponse,BeorgKycResultResponse,KycPersonalData,KycSessionResponse
from src.application.domain.entities import KycEntity
from src.application.domain.exceptions import ApplicationException,KycFailedException,KycNotCompletedException,KycPassportAlreadyVerifiedException,KycPersonalDataIncompleteException,KycSessionExpiredException,KycSessionMissingUserTokenException
from src.application.domain.exceptions import ApplicationException,KycFailedException,KycLegalEntityForbiddenException,KycNotCompletedException,KycPassportAlreadyVerifiedException,KycPersonalDataIncompleteException,KycSessionExpiredException,KycSessionMissingUserTokenException
from src.application.services import ensure_adult,extract_personal_data,normalize_passport_data,parse_birth_date
@@ -41,6 +41,9 @@ class PassKycCommand:
now = _utc_now()
self._logger.info(f'KYC session creation started for user {user_id}')
async with self._unit_of_work as unit_of_work:
user = await unit_of_work.user_repository.get_user_by_id(user_id)
if user.account_type == 'legal_entity':
raise KycLegalEntityForbiddenException()
await unit_of_work.kyc_repository.expire_started_sessions(user_id=user_id,now=now)
existing = await unit_of_work.kyc_repository.get_active_session(user_id=user_id,now=now)
if existing is not None:

View File

@@ -29,3 +29,5 @@ class UserEntity:
created_at: datetime | None = None
updated_at: datetime | None = None
kyc_verified_at: datetime | None = None
account_type: str = 'individual'

View File

@@ -1,2 +1,2 @@
from src.application.domain.exceptions.application_exceptions import ApplicationException
from src.application.domain.exceptions.kyc_exceptions import BeorgConfigException,BeorgRejectedException,BeorgUnavailableException,CsrfRequestRequiredException,InvalidTokenException,JwtDecodeFailedException,KycAgeRestrictedException,KycBirthDateInvalidException,KycFailedException,KycNotCompletedException,KycPassportAlreadyVerifiedException,KycPersonalDataIncompleteException,KycSessionExpiredException,KycSessionMissingUserTokenException,NotAuthenticatedException,UnauthorizedException,UserNotFoundException
from src.application.domain.exceptions.kyc_exceptions import BeorgConfigException,BeorgRejectedException,BeorgUnavailableException,CsrfRequestRequiredException,InvalidTokenException,JwtDecodeFailedException,KycAgeRestrictedException,KycBirthDateInvalidException,KycFailedException,KycLegalEntityForbiddenException,KycNotCompletedException,KycPassportAlreadyVerifiedException,KycPersonalDataIncompleteException,KycSessionExpiredException,KycSessionMissingUserTokenException,NotAuthenticatedException,UnauthorizedException,UserNotFoundException

View File

@@ -106,3 +106,13 @@ class BeorgConfigException(ApplicationException):
def __init__(self) -> None:
super().__init__(status_code=500,message='Beorg service is not configured',error_code='beorg_not_configured')
class KycLegalEntityForbiddenException(ApplicationException):
def __init__(self) -> None:
super().__init__(
status_code=403,
message='KYC via Beorg is not available for legal entity accounts',
error_code='kyc_legal_entity_forbidden',
)

View File

@@ -28,3 +28,5 @@ class UserModel(Base, UlidPrimaryKeyMixin, AuditTimestampsMixin, SoftDeleteMixin
kyc_verified: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default='false', default=False)
kyc_verified_at: Mapped[DateTime | None] = mapped_column(DateTime(timezone=True), nullable=True)
account_type: Mapped[str] = mapped_column(String(20), nullable=False, server_default='individual', default='individual')

View File

@@ -19,7 +19,7 @@ class UserRepository(IUserRepository):
async def create_user(self,email: str,password_hash: str) -> UserEntity:
user = UserModel(email=email,password_hash=password_hash)
user = UserModel(email=email, password_hash=password_hash, account_type='individual')
self._session.add(user)
await self._session.flush()
return self._to_entity(user)
@@ -38,6 +38,13 @@ class UserRepository(IUserRepository):
return result.scalar_one_or_none() is not None
async def get_user_by_id(self, user_id: str) -> UserEntity:
user = await self._session.get(UserModel, user_id)
if user is None or user.is_deleted:
raise UserNotFoundException()
return self._to_entity(user)
async def exists_verified_by_passport(self,*,passport_data: str,exclude_user_id: str) -> bool:
passport_digits = func.regexp_replace(UserModel.passport_data,'[^0-9]','','g')
result = await self._session.execute(
@@ -104,6 +111,7 @@ class UserRepository(IUserRepository):
created_at=user.created_at,
updated_at=user.updated_at,
kyc_verified_at=user.kyc_verified_at,
account_type=user.account_type,
)