Compare commits

...
Sign in to create a new pull request.

8 commits

Author SHA1 Message Date
48411757a1 카드 매입 승인 경로 Spring Boot 3 전환 (ACX-E2E-1785394564819)
All checks were successful
Verify / source-contract (push) Successful in 10s
Verify / source-contract (pull_request) Successful in 12s
2026-07-30 11:34:50 +00:00
382d65ce08 카드 매입 승인 경로 Spring Boot 3 전환 (ACX-E2E-1785394564819)
Some checks are pending
Verify / source-contract (push) Waiting to run
2026-07-30 11:34:49 +00:00
7781110287 카드 매입 승인 경로 Spring Boot 3 전환 (ACX-E2E-1785394564819)
Some checks are pending
Verify / source-contract (push) Waiting to run
2026-07-30 11:34:47 +00:00
8b8e860a75 카드 매입 승인 경로 Spring Boot 3 전환 (ACX-E2E-1785394564819)
Some checks are pending
Verify / source-contract (push) Waiting to run
2026-07-30 11:34:46 +00:00
f006ec0d0c 카드 매입 승인 경로 Spring Boot 3 전환 (ACX-E2E-1785394564819)
Some checks failed
Verify / source-contract (push) Has been cancelled
2026-07-30 11:34:45 +00:00
d057364307 카드 매입 승인 경로 Spring Boot 3 전환 (ACX-E2E-1785394564819)
Some checks are pending
Verify / source-contract (push) Waiting to run
2026-07-30 11:34:43 +00:00
6eab7fe2f4 카드 매입 승인 경로 Spring Boot 3 전환 (ACX-E2E-1785394564819)
Some checks are pending
Verify / source-contract (push) Waiting to run
2026-07-30 11:34:42 +00:00
db5e3d135d 카드 매입 승인 경로 Spring Boot 3 전환 (ACX-E2E-1785394564819)
Some checks failed
Verify / source-contract (push) Has been cancelled
2026-07-30 11:34:41 +00:00
8 changed files with 323 additions and 0 deletions

29
proj-acquirex/README.md Normal file
View file

@ -0,0 +1,29 @@
# proj-acquirex
## 카드 매입 승인 경로 Spring Boot 3 전환
### 전환 범위
- Spring Boot 3.2.5 (Jakarta EE 9+)
- Java 17+
- Spring Framework 6.x
- Hibernate 6.x
### 검증 절차
1. `./mvnw clean compile` - 컴파일 검증
2. `./mvnw test` - 단위 테스트 실행
3. `./mvnw verify` - 통합 검증
### API 엔드포인트
- `POST /api/v1/approvals/card-acquisition` - 카드 매입 승인 요청
- `GET /api/v1/approvals/card-acquisition/{approvalId}` - 승인 상태 조회
### 프로젝트 구조
```
src/main/java/com/acquirex/
├── AcquirexApplication.java # 메인 애플리케이션
├── CardAcquisitionApprovalStatus.java # 승인 상태 enum
├── CardAcquisitionApprovalRequest.java # 요청 DTO
├── CardAcquisitionApprovalResponse.java # 응답 DTO
├── CardAcquisitionApprovalService.java # 비즈니스 로직
└── CardAcquisitionApprovalController.java # REST 컨트롤러
```

66
proj-acquirex/pom.xml Normal file
View file

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/>
</parent>
<groupId>com.acquirex</groupId>
<artifactId>proj-acquirex</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>proj-acquirex</name>
<description>카드 매입 승인 경로 - Spring Boot 3 전환</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,11 @@
package com.acquirex;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AcquirexApplication {
public static void main(String[] args) {
SpringApplication.run(AcquirexApplication.class, args);
}
}

View file

@ -0,0 +1,34 @@
package com.acquirex.approval;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CardAcquisitionApprovalRequest {
@NotBlank(message = "가맹점 ID는 필수입니다")
private String merchantId;
@NotBlank(message = "카드사 코드는 필수입니다")
private String cardCompanyCode;
@NotNull(message = "거래 금액은 필수입니다")
@Positive(message = "거래 금액은 양수여야 합니다")
private BigDecimal transactionAmount;
@NotBlank(message = "거래 유형은 필수입니다")
private String transactionType;
private String merchantName;
private LocalDateTime requestDateTime;
}

View file

@ -0,0 +1,25 @@
package com.acquirex.approval;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CardAcquisitionApprovalResponse {
private String approvalId;
private String merchantId;
private String cardCompanyCode;
private BigDecimal transactionAmount;
private CardAcquisitionApprovalStatus status;
private String statusMessage;
private LocalDateTime approvalDateTime;
private String responseCode;
private String responseMessage;
}

View file

@ -0,0 +1,59 @@
package com.acquirex.approval;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Service
public class CardAcquisitionApprovalService {
private final Map<String, CardAcquisitionApprovalResponse> approvalStore = new ConcurrentHashMap<>();
public CardAcquisitionApprovalResponse processApproval(CardAcquisitionApprovalRequest request) {
log.info("카드 매입 승인 요청 처리: merchantId={}, amount={}",
request.getMerchantId(), request.getTransactionAmount());
String approvalId = generateApprovalId();
CardAcquisitionApprovalStatus status = determineApprovalStatus(request);
CardAcquisitionApprovalResponse response = CardAcquisitionApprovalResponse.builder()
.approvalId(approvalId)
.merchantId(request.getMerchantId())
.cardCompanyCode(request.getCardCompanyCode())
.transactionAmount(request.getTransactionAmount())
.status(status)
.statusMessage(status.getDescription())
.approvalDateTime(LocalDateTime.now())
.responseCode(status == CardAcquisitionApprovalStatus.APPROVED ? "0000" : "9999")
.responseMessage(status == CardAcquisitionApprovalStatus.APPROVED ? "승인 완료" : "승인 실패")
.build();
approvalStore.put(approvalId, response);
log.info("카드 매입 승인 완료: approvalId={}, status={}", approvalId, status);
return response;
}
public CardAcquisitionApprovalResponse getApprovalStatus(String approvalId) {
log.info("승인 상태 조회: approvalId={}", approvalId);
return approvalStore.get(approvalId);
}
private CardAcquisitionApprovalStatus determineApprovalStatus(CardAcquisitionApprovalRequest request) {
if (request.getTransactionAmount() == null) {
return CardAcquisitionApprovalStatus.REJECTED;
}
if (request.getTransactionAmount().compareTo(new BigDecimal("100000")) >= 0) {
return CardAcquisitionApprovalStatus.PROCESSING;
}
return CardAcquisitionApprovalStatus.APPROVED;
}
private String generateApprovalId() {
return "APR-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
}
}

View file

@ -0,0 +1,19 @@
package com.acquirex.approval;
public enum CardAcquisitionApprovalStatus {
PENDING("승인 대기"),
APPROVED("승인 완료"),
REJECTED("승인 거절"),
CANCELLED("승인 취소"),
PROCESSING("처리 중");
private final String description;
CardAcquisitionApprovalStatus(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}

View file

@ -0,0 +1,80 @@
package com.acquirex.approval;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import static org.junit.jupiter.api.Assertions.*;
@DisplayName("카드 매입 승인 서비스 테스트")
class CardAcquisitionApprovalServiceTest {
private CardAcquisitionApprovalService approvalService;
@BeforeEach
void setUp() {
approvalService = new CardAcquisitionApprovalService();
}
@Test
@DisplayName("소액 거래 승인 처리 성공")
void testSmallAmountApproval() {
CardAcquisitionApprovalRequest request = CardAcquisitionApprovalRequest.builder()
.merchantId("MERCHANT001")
.cardCompanyCode("CARD001")
.transactionAmount(new BigDecimal("50000"))
.transactionType("PURCHASE")
.build();
CardAcquisitionApprovalResponse response = approvalService.processApproval(request);
assertNotNull(response);
assertNotNull(response.getApprovalId());
assertTrue(response.getApprovalId().startsWith("APR-"));
assertEquals("MERCHANT001", response.getMerchantId());
assertEquals(CardAcquisitionApprovalStatus.APPROVED, response.getStatus());
assertEquals("0000", response.getResponseCode());
}
@Test
@DisplayName("대액 거래 처리 중 상태")
void testLargeAmountProcessing() {
CardAcquisitionApprovalRequest request = CardAcquisitionApprovalRequest.builder()
.merchantId("MERCHANT002")
.cardCompanyCode("CARD002")
.transactionAmount(new BigDecimal("150000"))
.transactionType("PURCHASE")
.build();
CardAcquisitionApprovalResponse response = approvalService.processApproval(request);
assertNotNull(response);
assertEquals(CardAcquisitionApprovalStatus.PROCESSING, response.getStatus());
}
@Test
@DisplayName("승인 상태 조회 성공")
void testGetApprovalStatus() {
CardAcquisitionApprovalRequest request = CardAcquisitionApprovalRequest.builder()
.merchantId("MERCHANT003")
.cardCompanyCode("CARD003")
.transactionAmount(new BigDecimal("30000"))
.transactionType("PURCHASE")
.build();
CardAcquisitionApprovalResponse created = approvalService.processApproval(request);
CardAcquisitionApprovalResponse found = approvalService.getApprovalStatus(created.getApprovalId());
assertNotNull(found);
assertEquals(created.getApprovalId(), found.getApprovalId());
}
@Test
@DisplayName("존재하지 않는 승인 ID 조회 시 null 반환")
void testGetNonExistentApproval() {
CardAcquisitionApprovalResponse response = approvalService.getApprovalStatus("APR-NONEXIST");
assertNull(response);
}
}