@@ -34,6 +34,7 @@ import { KycStep } from 'src/subdomains/generic/kyc/entities/kyc-step.entity';
3434import { KycStepName } from 'src/subdomains/generic/kyc/enums/kyc-step-name.enum' ;
3535import { ReviewStatus } from 'src/subdomains/generic/kyc/enums/review-status.enum' ;
3636import { KycService } from 'src/subdomains/generic/kyc/services/kyc.service' ;
37+ import { AccountMergeService } from 'src/subdomains/generic/user/models/account-merge/account-merge.service' ;
3738import { AccountType } from 'src/subdomains/generic/user/models/user-data/account-type.enum' ;
3839import { UserData } from 'src/subdomains/generic/user/models/user-data/user-data.entity' ;
3940import { KycLevel } from 'src/subdomains/generic/user/models/user-data/user-data.enum' ;
@@ -52,7 +53,14 @@ import {
5253 TokenInfoClientResponse ,
5354} from './dto/client.dto' ;
5455import { RealUnitDtoMapper } from './dto/realunit-dto.mapper' ;
55- import { AktionariatRegistrationDto , RealUnitRegistrationDto , RealUnitUserType } from './dto/realunit-registration.dto' ;
56+ import {
57+ AktionariatRegistrationDto ,
58+ RealUnitEmailRegistrationDto ,
59+ RealUnitEmailRegistrationStatus ,
60+ RealUnitRegistrationDto ,
61+ RealUnitRegistrationStatus ,
62+ RealUnitUserType ,
63+ } from './dto/realunit-registration.dto' ;
5664import { RealUnitSellConfirmDto , RealUnitSellDto , RealUnitSellPaymentInfoDto } from './dto/realunit-sell.dto' ;
5765import {
5866 AccountHistoryDto ,
@@ -98,6 +106,7 @@ export class RealUnitService {
98106 private readonly sellService : SellService ,
99107 private readonly eip7702DelegationService : Eip7702DelegationService ,
100108 private readonly transactionRequestService : TransactionRequestService ,
109+ private readonly accountMergeService : AccountMergeService ,
101110 ) {
102111 this . ponderUrl = GetConfig ( ) . blockchain . realunit . graphUrl ;
103112 }
@@ -341,6 +350,91 @@ export class RealUnitService {
341350 return ! success ;
342351 }
343352
353+ async registerEmail ( userDataId : number , dto : RealUnitEmailRegistrationDto ) : Promise < RealUnitEmailRegistrationStatus > {
354+ const userData = await this . userDataService . getUserData ( userDataId , { users : true } ) ;
355+ if ( ! userData ) throw new NotFoundException ( 'User not found' ) ;
356+
357+ if ( ! userData . mail ) {
358+ try {
359+ await this . userDataService . trySetUserMail ( userData , dto . email ) ;
360+ } catch ( e ) {
361+ if ( e instanceof ConflictException ) {
362+ if ( e . message . includes ( 'account merge request sent' ) ) {
363+ return RealUnitEmailRegistrationStatus . MERGE_REQUESTED ;
364+ }
365+ }
366+ throw e ;
367+ }
368+ } else if ( ! Util . equalsIgnoreCase ( dto . email , userData . mail ) ) {
369+ throw new BadRequestException ( 'Email does not match verified email' ) ;
370+ }
371+
372+ if ( userData . kycLevel < KycLevel . LEVEL_10 ) {
373+ await this . kycService . initializeProcess ( userData ) ;
374+ }
375+
376+ return RealUnitEmailRegistrationStatus . EMAIL_REGISTERED ;
377+ }
378+
379+ async completeRegistration ( userDataId : number , dto : RealUnitRegistrationDto ) : Promise < RealUnitRegistrationStatus > {
380+ await this . validateRegistrationDto ( dto ) ;
381+
382+ // get and validate user
383+ const userData = await this . userService
384+ . getUserByAddress ( dto . walletAddress , {
385+ userData : { kycSteps : true , users : true , country : true , organizationCountry : true } ,
386+ } )
387+ . then ( ( u ) => u ?. userData ) ;
388+
389+ if ( ! userData ) throw new NotFoundException ( 'User not found' ) ;
390+ if ( userData . id !== userDataId ) throw new BadRequestException ( 'Wallet address does not belong to user' ) ;
391+
392+ if ( userData . kycLevel < KycLevel . LEVEL_10 || ! userData . mail ) {
393+ throw new BadRequestException ( 'Email registration must be completed first' ) ;
394+ }
395+ if ( ! Util . equalsIgnoreCase ( dto . email , userData . mail ) ) {
396+ throw new BadRequestException ( 'Email does not match registered email' ) ;
397+ }
398+
399+ // duplicate check
400+ if ( userData . getNonFailedStepWith ( KycStepName . REALUNIT_REGISTRATION ) ) {
401+ throw new BadRequestException ( 'RealUnit registration already exists' ) ;
402+ }
403+
404+ // store data with internal review
405+ const kycStep = await this . kycService . createCustomKycStep (
406+ userData ,
407+ KycStepName . REALUNIT_REGISTRATION ,
408+ ReviewStatus . INTERNAL_REVIEW ,
409+ dto ,
410+ ) ;
411+
412+ const hasExistingData = userData . firstname != null ;
413+ if ( hasExistingData ) {
414+ const dataMatches = this . isPersonalDataMatching ( userData , dto ) ;
415+ if ( ! dataMatches ) {
416+ await this . kycService . saveKycStepUpdate ( kycStep . manualReview ( 'Existing KYC data does not match' ) ) ;
417+ return RealUnitRegistrationStatus . MANUAL_REVIEW_DATA_MISMATCH ;
418+ }
419+ } else {
420+ await this . userDataService . updatePersonalData ( userData , dto . kycData ) ;
421+ }
422+
423+ // forward to Aktionariat
424+ const success = await this . forwardRegistration ( kycStep , dto ) ;
425+ if ( ! success ) return RealUnitRegistrationStatus . FORWARDING_FAILED ;
426+
427+ // only update after successful forward
428+ await this . userDataService . updateUserDataInternal ( userData , {
429+ nationality : await this . countryService . getCountryWithSymbol ( dto . nationality ) ,
430+ birthday : new Date ( dto . birthday ) ,
431+ language : dto . lang && ( await this . languageService . getLanguageBySymbol ( dto . lang ) ) ,
432+ tin : dto . countryAndTINs ?. length ? JSON . stringify ( dto . countryAndTINs ) : undefined ,
433+ } ) ;
434+
435+ return RealUnitRegistrationStatus . COMPLETED ;
436+ }
437+
344438 private async validateRegistrationDto ( dto : RealUnitRegistrationDto ) : Promise < void > {
345439 // signature validation
346440 if ( ! this . verifyRealUnitRegistrationSignature ( dto ) ) {
0 commit comments