From 669cdbb6c2384dc0e31b636a7057ebba634f7e30 Mon Sep 17 00:00:00 2001 From: forge-bot Date: Thu, 30 Jul 2026 11:07:08 +0000 Subject: [PATCH] =?UTF-8?q?=EC=B9=B4=EB=93=9C=20=EB=A7=A4=EC=9E=85=20?= =?UTF-8?q?=EC=8A=B9=EC=9D=B8=20=EA=B2=BD=EB=A1=9C=20Spring=20Boot=203=20?= =?UTF-8?q?=EC=A0=84=ED=99=98=20(ACX-E2E-1785394564819)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/CardAcquisitionService.java | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/main/java/com/acquirex/service/CardAcquisitionService.java diff --git a/src/main/java/com/acquirex/service/CardAcquisitionService.java b/src/main/java/com/acquirex/service/CardAcquisitionService.java new file mode 100644 index 0000000..0d8493f --- /dev/null +++ b/src/main/java/com/acquirex/service/CardAcquisitionService.java @@ -0,0 +1,116 @@ +package com.acquirex.service; + +import com.acquirex.dto.AcquisitionDto.Request; +import com.acquirex.dto.AcquisitionDto.Response; +import com.acquirex.entity.CardAcquisition; +import com.acquirex.entity.CardAcquisition.AcquisitionStatus; +import com.acquirex.entity.CardAcquisition.CardAcquisitionRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Random; + +@Service +public class CardAcquisitionService { + + private static final BigDecimal MAX_APPROVAL_AMOUNT = new BigDecimal("10000000"); + private static final BigDecimal SUSPICIOUS_AMOUNT_THRESHOLD = new BigDecimal("5000000"); + + private final CardAcquisitionRepository repository; + private final Random approvalCodeGenerator = new Random(); + + public CardAcquisitionService(CardAcquisitionRepository repository) { + this.repository = repository; + } + + @Transactional + public Response processAcquisition(Request request) { + CardAcquisition acquisition = createAcquisition(request); + CardAcquisition saved = repository.save(acquisition); + + if (!validateCardNumber(request.getCardNumber())) { + return rejectAcquisition(saved, "유효하지 않은 카드번호입니다"); + } + + if (request.getAmount().compareTo(MAX_APPROVAL_AMOUNT) > 0) { + return rejectAcquisition(saved, "승인 한도를 초과했습니다"); + } + + if (isSuspiciousTransaction(request)) { + return rejectAcquisition(saved, "의심스러운 거래로 거부되었습니다"); + } + + return approveAcquisition(saved); + } + + private CardAcquisition createAcquisition(Request request) { + CardAcquisition acquisition = new CardAcquisition(); + acquisition.setCardNumber(maskCardNumber(request.getCardNumber())); + acquisition.setMerchantId(request.getMerchantId()); + acquisition.setAmount(request.getAmount()); + acquisition.setCurrency(request.getCurrency()); + acquisition.setStatus(AcquisitionStatus.PENDING); + return acquisition; + } + + private boolean validateCardNumber(String cardNumber) { + if (cardNumber == null || cardNumber.length() < 13) return false; + int sum = 0; + boolean alternate = false; + for (int i = cardNumber.length() - 1; i >= 0; i--) { + int digit = Character.getNumericValue(cardNumber.charAt(i)); + if (alternate) { + digit *= 2; + if (digit > 9) digit -= 9; + } + sum += digit; + alternate = !alternate; + } + return sum % 10 == 0; + } + + private boolean isSuspiciousTransaction(Request request) { + return request.getAmount().compareTo(SUSPICIOUS_AMOUNT_THRESHOLD) >= 0 + && request.getCardNumber().startsWith("9999"); + } + + private String generateApprovalCode() { + return String.format("%06d", approvalCodeGenerator.nextInt(999999)); + } + + private String maskCardNumber(String cardNumber) { + if (cardNumber == null || cardNumber.length() < 8) return cardNumber; + return cardNumber.substring(0, 4) + "****" + cardNumber.substring(cardNumber.length() - 4); + } + + private Response approveAcquisition(CardAcquisition acquisition) { + String approvalCode = generateApprovalCode(); + acquisition.setStatus(AcquisitionStatus.APPROVED); + acquisition.setApprovalCode(approvalCode); + acquisition.setApprovalTime(LocalDateTime.now()); + repository.save(acquisition); + return Response.success(acquisition.getId(), approvalCode, acquisition.getAmount(), acquisition.getCurrency()); + } + + private Response rejectAcquisition(CardAcquisition acquisition, String reason) { + acquisition.setStatus(AcquisitionStatus.REJECTED); + acquisition.setRejectReason(reason); + acquisition.setApprovalTime(LocalDateTime.now()); + repository.save(acquisition); + return Response.rejected(acquisition.getId(), reason); + } + + @Transactional(readOnly = true) + public CardAcquisition findById(Long id) { + return repository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("매입 정보를 찾을 수 없습니다: " + id)); + } + + @Transactional(readOnly = true) + public CardAcquisition findByApprovalCode(String approvalCode) { + return repository.findByApprovalCode(approvalCode) + .orElseThrow(() -> new IllegalArgumentException("승인코드를 찾을 수 없습니다: " + approvalCode)); + } +} \ No newline at end of file