package ru.soune.nocopy.service.payout; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.soune.nocopy.dto.payout.AddBankTransferRequest; import ru.soune.nocopy.dto.payout.AddCardRequest; import ru.soune.nocopy.dto.payout.PayoutMethodDTO; import ru.soune.nocopy.entity.payout.*; import ru.soune.nocopy.entity.user.User; import ru.soune.nocopy.exception.DuplicateMethodException; import ru.soune.nocopy.repository.PayoutMethodRepository; import ru.soune.nocopy.repository.UserRepository; import ru.soune.nocopy.service.user.UserService; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @Slf4j @Service @RequiredArgsConstructor public class PayoutMethodService { private final PayoutMethodRepository payoutMethodRepository; private final UserRepository userRepository; @Transactional public void initiatedStandartPayoutMethods(User user) { createInternalMethod(user); } @Transactional public InternalPayoutMethod createInternalMethod(User user) { if (payoutMethodRepository.findByUserAndType(user.getId(), InternalPayoutMethod.class).isPresent()) { return (InternalPayoutMethod) payoutMethodRepository.findByUserAndType( user.getId(), InternalPayoutMethod.class).get(); } InternalPayoutMethod internalMethod = new InternalPayoutMethod(); internalMethod.setUser(user); internalMethod.setDefault(true); return payoutMethodRepository.save(internalMethod); } public List getUserMethods(long userId) { return payoutMethodRepository.findByUserId(userId) .stream() .map(this::convertToDto) .collect(Collectors.toList()); } @Transactional public void setDefaultMethod(Long userId, Long methodId) { payoutMethodRepository.clearOtherDefault(userId, methodId); PayoutMethod method = payoutMethodRepository.findByIdAndUserId(methodId, userId) .orElseThrow(() -> new IllegalArgumentException("Not found method")); method.setDefault(true); payoutMethodRepository.save(method); } @Transactional public BankTransferPayoutMethod addBankTransferMethod(Long userId, AddBankTransferRequest request) { User user = userRepository.findById(userId).orElseThrow(); BankTransferPayoutMethod bankMethod = new BankTransferPayoutMethod(); bankMethod.setUser(user); bankMethod.setBankName(request.getBankName()); bankMethod.setBic(request.getBic()); bankMethod.setCorrespondentAccount(request.getCorrespondentAccount()); bankMethod.setAccountNumber(request.getAccountNumber()); bankMethod.setAccountHolder(request.getAccountHolder()); bankMethod.setInn(request.getInn()); bankMethod.setKpp(request.getKpp()); bankMethod.setDefault(payoutMethodRepository.countByUserId(user.getId()) == 0); return payoutMethodRepository.save(bankMethod); } @Transactional public CardPayoutMethod addCardMethod(Long userId, AddCardRequest request) { List byUserId = payoutMethodRepository.findByUserId(userId); if (!byUserId.isEmpty()) { for (PayoutMethod payoutMethod : byUserId) { if (payoutMethod.getMethodType().equals(PayoutType.CARD.getDisplayName().toUpperCase())) { payoutMethodRepository.delete(payoutMethod); } } } String cardHash = generateCardHash(request.getCardNumber()); if (payoutMethodRepository.existsByCardHash(cardHash)) { throw new DuplicateMethodException("Card was added"); } User user = userRepository.findById(userId).orElseThrow(); CardPayoutMethod cardMethod = new CardPayoutMethod(); cardMethod.setUser(user); cardMethod.setCardNumber(request.getCardNumber()); cardMethod.setCardHash(cardHash); cardMethod.setDefault(payoutMethodRepository.countByUserId(user.getId()) == 0); CardPayoutMethod saved = payoutMethodRepository.save(cardMethod); log.info("Added user card: {}", user.getId()); return saved; } public PayoutMethod getDefaultMethod(User user) { return payoutMethodRepository.findByUserIdAndIsDefaultTrue(user.getId()) .orElseGet(() -> { List methods = payoutMethodRepository.findByUserId(user.getId()); if (methods.isEmpty()) { return createInternalMethod(user); } return methods.get(0); }); } public PayoutMethodDTO convertToDto(PayoutMethod method) { PayoutMethodDTO payoutMethodDTO = PayoutMethodDTO.builder() .id(method.getId()) .type(method.getClass().getSimpleName().replace("PayoutMethod", "")) .displayName(method.getDisplayName()) .maskedDetails(method.getMaskedDetails()) .isDefault(method.isDefault()) .build(); if (method instanceof CardPayoutMethod cardMethod) { payoutMethodDTO.setCardNumber(cardMethod.getCardNumber()); } return payoutMethodDTO; } private String generateCardHash(String cardNumber) { try { MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] hash = md.digest(cardNumber.getBytes()); return Base64.getEncoder().encodeToString(hash); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Failed generate hash", e); } } }