diff --git a/src/main/java/com/acquirex/approval/service/CardAcquisitionApprovalService.java b/src/main/java/com/acquirex/approval/service/CardAcquisitionApprovalService.java new file mode 100644 index 0000000..c2e4bb2 --- /dev/null +++ b/src/main/java/com/acquirex/approval/service/CardAcquisitionApprovalService.java @@ -0,0 +1,118 @@ +package com.acquirex.approval.service; + +import com.acquirex.approval.domain.ApprovalStatus; +import com.acquirex.approval.domain.CardAcquisitionApproval; +import com.acquirex.approval.dto.ApprovalRequest; +import com.acquirex.approval.dto.ApprovalResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; + +/** + * Service for card acquisition approval processing. + * Migrated to Spring Boot 3 with Jakarta EE 9+ compatibility. + */ +@Service +public class CardAcquisitionApprovalService { + + private static final Logger log = LoggerFactory.getLogger(CardAcquisitionApprovalService.class); + private static final BigDecimal MAX_APPROVAL_AMOUNT = new BigDecimal("1000000.00"); + private static final BigDecimal HIGH_RISK_THRESHOLD = new BigDecimal("500000.00"); + + /** + * Process a card acquisition approval request. + * @param request the approval request + * @return the approval response + */ + public ApprovalResponse processApproval(ApprovalRequest request) { + log.info("Processing approval request: {}", request.getRequestId()); + validateRequest(request); + ApprovalStatus status = determineApprovalStatus(request.getAmount()); + String approvedBy = determineApprover(status); + CardAcquisitionApproval approval = createApproval(request, status, approvedBy); + log.info("Approval processed: requestId={}, status={}, amount={}", + request.getRequestId(), status, request.getAmount()); + return buildResponse(approval, status); + } + + /** + * Validate the approval request. + * @param request the request to validate + * @throws IllegalArgumentException if validation fails + */ + void validateRequest(ApprovalRequest request) { + if (request.getCardNumber() == null || request.getCardNumber().length() < 13) { + throw new IllegalArgumentException("Invalid card number format"); + } + if (request.getMerchantId() == null || request.getMerchantId().isBlank()) { + throw new IllegalArgumentException("Merchant ID is required"); + } + } + + /** + * Determine approval status based on amount. + * @param amount the transaction amount + * @return the determined status + */ + ApprovalStatus determineApprovalStatus(BigDecimal amount) { + if (amount.compareTo(MAX_APPROVAL_AMOUNT) > 0) return ApprovalStatus.REJECTED; + if (amount.compareTo(HIGH_RISK_THRESHOLD) > 0) return ApprovalStatus.PENDING; + return ApprovalStatus.APPROVED; + } + + /** + * Determine the approver based on status. + * @param status the approval status + * @return the approver identifier + */ + String determineApprover(ApprovalStatus status) { + return switch (status) { + case APPROVED, REJECTED -> "SYSTEM"; + case PENDING -> "MANUAL_REVIEW"; + case CANCELLED -> "CANCELLED"; + }; + } + + /** + * Create the approval record. + */ + CardAcquisitionApproval createApproval(ApprovalRequest request, ApprovalStatus status, String approvedBy) { + LocalDateTime now = LocalDateTime.now(); + return new CardAcquisitionApproval( + generateId(), request.getRequestId(), maskCardNumber(request.getCardNumber()), + request.getMerchantId(), request.getAmount(), status, now, now, approvedBy); + } + + /** + * Build the approval response. + */ + ApprovalResponse buildResponse(CardAcquisitionApproval approval, ApprovalStatus status) { + String message = switch (status) { + case APPROVED -> "Card acquisition approved successfully"; + case REJECTED -> "Card acquisition rejected: amount exceeds limit"; + case PENDING -> "Card acquisition pending manual review"; + case CANCELLED -> "Card acquisition cancelled"; + }; + return new ApprovalResponse(approval.getId(), approval.getRequestId(), status, + approval.getAmount(), approval.getUpdatedAt(), message); + } + + /** + * Mask card number for logging/security. + */ + String maskCardNumber(String cardNumber) { + if (cardNumber == null || cardNumber.length() < 4) return "****"; + return "****-****-****-" + cardNumber.substring(cardNumber.length() - 4); + } + + /** + * Generate unique ID for approval records. + */ + Long generateId() { + return UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE; + } +} \ No newline at end of file