diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..fa81484
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,90 @@
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.2.5
+
+
+
+ com.acquirex
+ proj-acquirex
+ 1.0.0-SNAPSHOT
+ proj-acquirex
+ 카드 매입 승인 경로 - Spring Boot 3 Migration
+
+
+ 17
+ 17
+ 17
+ UTF-8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ jakarta.persistence
+ jakarta.persistence-api
+
+
+ jakarta.validation
+ jakarta.validation-api
+
+
+ com.h2database
+ h2
+ runtime
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.2.5
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/acquirex/AcquirexApplication.java b/src/main/java/com/acquirex/AcquirexApplication.java
new file mode 100644
index 0000000..489bcaa
--- /dev/null
+++ b/src/main/java/com/acquirex/AcquirexApplication.java
@@ -0,0 +1,20 @@
+package com.acquirex;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * 카드 매입 승인 경로 Spring Boot 3 애플리케이션
+ *
+ * Spring Boot 3.x 마이그레이션:
+ * - Java 17+ 필수
+ * - Jakarta EE 9+ (javax.* → jakarta.*)
+ * - Spring Framework 6.x
+ */
+@SpringBootApplication
+public class AcquirexApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(AcquirexApplication.class, args);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/acquirex/controller/AcquisitionController.java b/src/main/java/com/acquirex/controller/AcquisitionController.java
new file mode 100644
index 0000000..5e776bc
--- /dev/null
+++ b/src/main/java/com/acquirex/controller/AcquisitionController.java
@@ -0,0 +1,59 @@
+package com.acquirex.controller;
+
+import com.acquirex.service.AcquisitionRequest;
+import com.acquirex.service.AcquisitionResponse;
+import com.acquirex.service.AcquisitionService;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 카드 매입 승인 REST 컨트롤러 - Spring Boot 3 (jakarta.validation)
+ */
+@RestController
+@RequestMapping("/api/v1/acquisitions")
+@RequiredArgsConstructor
+@Slf4j
+public class AcquisitionController {
+
+ private final AcquisitionService acquisitionService;
+
+ @PostMapping
+ public ResponseEntity registerAcquisition(@Valid @RequestBody AcquisitionRequest request) {
+ log.info("매입 요청 수신: {}", request);
+ return ResponseEntity.status(HttpStatus.CREATED).body(acquisitionService.registerAcquisition(request));
+ }
+
+ @PostMapping("/{transactionId}/approve")
+ public ResponseEntity approveAcquisition(@PathVariable String transactionId) {
+ log.info("매입 승인 요청: transactionId={}", transactionId);
+ return ResponseEntity.ok(acquisitionService.approveAcquisition(transactionId));
+ }
+
+ @PostMapping("/{transactionId}/reject")
+ public ResponseEntity rejectAcquisition(@PathVariable String transactionId, @RequestBody Map body) {
+ log.info("매입 거절 요청: transactionId={}", transactionId);
+ return ResponseEntity.ok(acquisitionService.rejectAcquisition(transactionId, body.getOrDefault("reason", "사유 미지정")));
+ }
+
+ @GetMapping("/{transactionId}")
+ public ResponseEntity getAcquisition(@PathVariable String transactionId) {
+ log.info("매입 조회 요청: transactionId={}", transactionId);
+ return ResponseEntity.ok(acquisitionService.getAcquisition(transactionId));
+ }
+
+ @GetMapping
+ public ResponseEntity> getAcquisitions(@RequestParam(required = false) String merchantId) {
+ log.info("매입 목록 조회: merchantId={}", merchantId);
+ List responses = (merchantId != null && !merchantId.isBlank())
+ ? acquisitionService.getAcquisitionsByMerchant(merchantId)
+ : acquisitionService.getPendingAcquisitions();
+ return ResponseEntity.ok(responses);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/acquirex/domain/Acquisition.java b/src/main/java/com/acquirex/domain/Acquisition.java
new file mode 100644
index 0000000..2e6ffa8
--- /dev/null
+++ b/src/main/java/com/acquirex/domain/Acquisition.java
@@ -0,0 +1,87 @@
+package com.acquirex.domain;
+
+import jakarta.persistence.*;
+import jakarta.validation.constraints.*;
+import lombok.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 카드 매입 정보 도메인 엔티티
+ * Jakarta EE 9+ 어노테이션 사용 (Spring Boot 3)
+ */
+@Entity
+@Table(name = "acquisitions")
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class Acquisition {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @NotBlank(message = "가맹점 번호는 필수입니다")
+ @Column(name = "merchant_id", nullable = false, length = 20)
+ private String merchantId;
+
+ @NotBlank(message = "카드번호는 필수입니다")
+ @Column(name = "card_number", nullable = false, length = 19)
+ private String cardNumber;
+
+ @NotNull(message = "매입 금액은 필수입니다")
+ @Positive(message = "매입 금액은 양수여야 합니다")
+ @Column(name = "amount", nullable = false, precision = 15, scale = 2)
+ private BigDecimal amount;
+
+ @NotBlank(message = "거래 번호는 필수입니다")
+ @Column(name = "transaction_id", nullable = false, unique = true, length = 30)
+ private String transactionId;
+
+ @Enumerated(EnumType.STRING)
+ @Column(name = "status", nullable = false, length = 20)
+ private AcquisitionStatus status;
+
+ @Column(name = "approval_code", length = 10)
+ private String approvalCode;
+
+ @Column(name = "approved_at")
+ private LocalDateTime approvedAt;
+
+ @Column(name = "created_at", nullable = false, updatable = false)
+ private LocalDateTime createdAt;
+
+ @Column(name = "updated_at")
+ private LocalDateTime updatedAt;
+
+ @PrePersist
+ protected void onCreate() {
+ createdAt = LocalDateTime.now();
+ updatedAt = LocalDateTime.now();
+ }
+
+ @PreUpdate
+ protected void onUpdate() {
+ updatedAt = LocalDateTime.now();
+ }
+
+ public void approve(String approvalCode) {
+ this.status = AcquisitionStatus.APPROVED;
+ this.approvalCode = approvalCode;
+ this.approvedAt = LocalDateTime.now();
+ }
+
+ public void reject(String reason) {
+ this.status = AcquisitionStatus.REJECTED;
+ }
+}
+
+/**
+ * 카드 매입 승인 상태 열거형
+ */
+enum AcquisitionStatus {
+ PENDING, APPROVED, REJECTED, CANCELLED
+}
\ No newline at end of file
diff --git a/src/main/java/com/acquirex/exception/GlobalExceptionHandler.java b/src/main/java/com/acquirex/exception/GlobalExceptionHandler.java
new file mode 100644
index 0000000..d25d1b1
--- /dev/null
+++ b/src/main/java/com/acquirex/exception/GlobalExceptionHandler.java
@@ -0,0 +1,74 @@
+package com.acquirex.exception;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 전역 예외 처리 핸들러 - Spring Boot 3 (jakarta.validation)
+ */
+@RestControllerAdvice
+@Slf4j
+public class GlobalExceptionHandler {
+
+ @ExceptionHandler(AcquisitionNotFoundException.class)
+ public ResponseEntity