kyc
unknown
java
2 years ago
11 kB
11
Indexable
package com.breakwater.kycservice.request.service;
import com.breakwater.common.core.util.UuidGenerator;
import com.breakwater.common.exception.exception.EntityNotFoundException;
import com.breakwater.common.exception.exception.InvalidStateException;
import com.breakwater.kycservice.common.mapper.DocumentResultMapper;
import com.breakwater.kycservice.common.model.*;
import com.breakwater.kycservice.common.repository.PaymentAccountRepository;
import com.breakwater.kycservice.common.repository.PlayerRepository;
import com.breakwater.kycservice.documenttype.model.DocumentType;
import com.breakwater.kycservice.documenttype.repository.DocumentTypeRepository;
import com.breakwater.kycservice.external.hooyu.service.HooyuApiService;
import com.breakwater.kycservice.playerverification.service.PlayerVerificationService;
import com.breakwater.kycservice.providerconfiguration.model.ProviderConfiguration;
import com.breakwater.kycservice.providerconfiguration.service.ProviderConfigurationService;
import com.breakwater.kycservice.request.dto.query.RequestCollectionQuery;
import com.breakwater.kycservice.request.model.KycRequest;
import com.breakwater.kycservice.request.repository.KycRequestRepository;
import com.breakwater.paymentservice.paymentaccount.model.PaymentAccountStatus;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Clock;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static com.breakwater.kycservice.common.error.KycServiceErrorCode.*;
import static com.breakwater.kycservice.request.model.KycStatus.REQUESTED;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
import static reactor.function.TupleUtils.function;
@Service
@RequiredArgsConstructor
public class KycRequestService {
private static final String DOCUMENT_GROUP = DocumentType.Fields.documentGroup;
private static final String EXTERNAL_TYPE = DocumentRequest.Fields.externalType;
private static final String EXTERNAL_ACCOUNT_ID = DocumentRequest.Fields.externalAccountId;
private static final String METHOD_TYPE = DocumentRequest.Fields.methodType;
private final KycRequestRepository kycRequestRepository;
private final ProviderConfigurationService providerConfigurationService;
private final DocumentTypeRepository documentTypeRepository;
private final PlayerRepository playerRepository;
private final PaymentAccountRepository paymentAccountRepository;
private final HooyuApiService hooyuApiService;
private final UuidGenerator uuidGenerator;
private final DocumentResultMapper documentResultMapper;
private final KycRequestPersistenceService kycRequestPersistenceService;
private final PlayerVerificationService playerVerificationService;
private final Clock clock;
@Transactional
@PreAuthorize("@kycRequestPermissionEvaluator.checkDocumentsRequestEdit(authentication, #request)")
public Mono<KycRequest> create(KycRequest request) {
request.setCreated(clock.instant());
return validateAndSendRequest(request.setId(uuidGenerator.get()))
.flatMap(documentTypeMap -> toDocumentResults(request, documentTypeMap))
.map(request::setDocumentResults)
.flatMap(kycRequestPersistenceService::create)
.delayUntil(playerVerificationService::processKycRequest);
}
@PreAuthorize("@kycRequestPermissionEvaluator.checkDocumentsRequestView(authentication, #query)")
public Mono<Page<KycRequest>> readCollection(RequestCollectionQuery query, Pageable pageable) {
return kycRequestRepository.findByCollectionQuery(query, pageable);
}
@PreAuthorize("@kycRequestPermissionEvaluator.checkDocumentsRequestView(authentication, #id)")
public Mono<KycRequest> read(UUID id) {
return kycRequestRepository.findById(id);
}
private Mono<Map<String, DocumentType>> validateAndSendRequest(KycRequest request) {
return Mono.zip(providerConfigurationService.readByBusinessUnitId(request.getBusinessUnitId()),
tryGetPlayer(request.getPlayerId()),
Mono.just(request))
.flatMap(function(this::validateRequestedDocumentsAndSendRequest));
}
private Mono<Player> tryGetPlayer(UUID playerId) {
return playerRepository.findById(playerId)
.switchIfEmpty(Mono.error(() -> new EntityNotFoundException(
PLAYER_NOT_FOUND, Map.of("playerId", playerId))));
}
/**
* Currently there is only one HOOYU
*/
private Mono<Map<String, DocumentType>> validateRequestedDocumentsAndSendRequest(ProviderConfiguration conf,
Player player, KycRequest request) {
request.setProvider(conf.getProvider())
.setStatus(REQUESTED);
var externalTypeToDocumentTypeMap = getExternalTypeToDocumentTypeMap(request);
var activePaymentAccounts = getActivePaymentAccountsForPlayer(request).collectList();
return Mono.zip(Mono.just(request), externalTypeToDocumentTypeMap, activePaymentAccounts)
.flatMap(function(this::validateRequestedDocuments))
.then(Mono.defer(() -> hooyuApiService.submitRequest(conf, player, request)))
.flatMap(ignored -> externalTypeToDocumentTypeMap);
}
private Mono<Map<String, DocumentType>> getExternalTypeToDocumentTypeMap(KycRequest request) {
return documentTypeRepository.findByProviderType(request.getProvider())
.collect(toMap(DocumentType::getExternalType, identity()));
}
private Flux<PaymentAccount> getActivePaymentAccountsForPlayer(KycRequest request) {
boolean noPaymentAccounts = request.getDocuments().stream()
.noneMatch(d -> d.getExternalAccountId() != null && d.getMethodType() != null);
if (noPaymentAccounts) {
return Flux.empty();
}
return paymentAccountRepository.findByPlayerIdAndAccountStatus(
request.getPlayerId(), PaymentAccountStatus.ACTIVE);
}
private Mono<Void> validateRequestedDocuments(KycRequest request, Map<String, DocumentType> documentTypes,
List<PaymentAccount> paymentAccounts) {
return Flux.fromIterable(request.getDocuments())
.flatMap(d -> validateRequestedDocuments(d, documentTypes, paymentAccounts)).then();
}
private Mono<Void> validateRequestedDocuments(DocumentRequest document, Map<String, DocumentType> documentTypes,
List<PaymentAccount> paymentAccounts) {
String externalType = document.getExternalType();
var type = documentTypes.get(externalType);
if (type == null) {
return Mono.error(new EntityNotFoundException(
EXTERNAL_TYPE_NOT_FOUND,
Map.of(EXTERNAL_TYPE, externalType)));
}
var documentGroup = type.getDocumentGroup();
if (documentGroup == DocumentGroup.PROOF_OF_PAYMENT) {
return validatePaymentDocuments(document, type, paymentAccounts);
}
return validateNonPaymentDocuments(document, documentGroup);
}
private Mono<Void> validatePaymentDocuments(DocumentRequest document,
DocumentType type, List<PaymentAccount> paymentAccounts) {
String externalType = document.getExternalType();
String externalAccountId = document.getExternalAccountId();
if (externalAccountId == null) {
return Mono.error(new InvalidStateException(
EXTERNAL_ACCOUNT_ID_MISSING_FOR_EXTERNAL_TYPE,
Map.of(EXTERNAL_TYPE, externalType)));
}
var methodType = document.getMethodType();
if (methodType == null) {
return Mono.error(new InvalidStateException(
METHOD_TYPE_MISSING_FOR_EXTERNAL_TYPE,
Map.of(EXTERNAL_TYPE, externalType)));
}
if (!type.getMethodTypes().contains(methodType)) {
return Mono.error(new InvalidStateException(
METHOD_TYPE_DOES_NOT_MATCH_EXTERNAL_TYPE,
Map.of(EXTERNAL_TYPE, externalType,
METHOD_TYPE, methodType)));
}
return Flux.fromIterable(paymentAccounts)
.filter(a -> a.getMethodType() == methodType
&& a.getExternalAccountId().equals(externalAccountId))
.switchIfEmpty(Mono.error(() -> new EntityNotFoundException(
EXTERNAL_ACCOUNT_NOT_FOUND,
Map.of(EXTERNAL_ACCOUNT_ID, externalAccountId,
METHOD_TYPE, methodType))))
.then();
}
private Mono<Void> validateNonPaymentDocuments(DocumentRequest document, DocumentGroup documentGroup) {
String externalType = document.getExternalType();
if (document.getExternalAccountId() != null) {
return Mono.error(new InvalidStateException(
EXTERNAL_ACCOUNT_ID_IS_NOT_EXPECTED_FOR_DOCUMENT_GROUP,
Map.of(EXTERNAL_TYPE, externalType,
DOCUMENT_GROUP, documentGroup)));
}
var methodType2 = document.getMethodType();
if (methodType2 != null) {
return Mono.error(new InvalidStateException(
METHOD_TYPE_IS_NOT_EXPECTED_FOR_DOCUMENT_GROUP,
Map.of(EXTERNAL_TYPE, externalType,
DOCUMENT_GROUP, documentGroup)));
}
return Mono.empty();
}
private Mono<List<DocumentResult>> toDocumentResults(KycRequest kycRequest,
Map<String, DocumentType> documentTypeMap) {
return Flux.zip(Mono.just(kycRequest.getCreated()).repeat(),
Mono.just(DocumentStatus.REQUESTED).repeat(),
Flux.fromIterable(kycRequest.getDocuments()),
generateExternalTypeToDisplayNameMap(documentTypeMap).cache().repeat())
.map(function(documentResultMapper::toDocumentResult))
.collectList();
}
private Mono<Map<String, String>> generateExternalTypeToDisplayNameMap(Map<String, DocumentType> documentTypeMap) {
return Mono.just(documentTypeMap.entrySet())
.map(entries -> entries.stream()
.collect(toMap(Map.Entry::getKey, entry -> entry.getValue().getDisplayName())));
}
}
Editor is loading...