Skip to content

Commit 6365416

Browse files
committed
refactor: update payment API layer for Order integration
- PaymentController orderId → orderNo 변경 - PaymentTestController 로그 메시지 수정 - PaymentStatusSyncScheduler orderId() → orderNo() 변경 - PgPaymentClient 주석 정리 - API 응답 DTO orderNo 필드로 통일
1 parent 8dd1381 commit 6365416

4 files changed

Lines changed: 74 additions & 127 deletions

File tree

apps/commerce-api/src/main/java/com/loopers/application/payment/PaymentStatusSyncScheduler.java

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,18 @@
88
import java.time.LocalDateTime;
99
import java.util.List;
1010

11-
/**
12-
* Payment 상태 동기화 스케줄러
13-
*
14-
* 콜백이 오지 않은 PENDING 상태의 결제를 주기적으로 확인하여 상태 동기화
15-
*/
1611
@Slf4j
1712
@Component
1813
@RequiredArgsConstructor
1914
public class PaymentStatusSyncScheduler {
2015

2116
private final PaymentFacade paymentFacade;
2217

23-
/**
24-
* PENDING 상태 결제 동기화
25-
*
26-
* - 10분 이상 PENDING 상태인 결제들을 조회
27-
* - PG 상태 조회 API로 최종 상태 확인
28-
* - Payment 및 Order 상태 업데이트
29-
*
30-
* 실행 주기: 1분마다
31-
*/
3218
@Scheduled(fixedDelay = 60000, initialDelay = 60000)
3319
public void syncPendingPayments() {
3420
log.info("[PaymentSync] PENDING 결제 동기화 시작");
3521

3622
try {
37-
// 10분 이상 PENDING 상태인 결제 조회
3823
LocalDateTime cutoffTime = LocalDateTime.now().minusMinutes(10);
3924
List<PaymentInfo> pendingPayments = paymentFacade.getPendingPaymentsOlderThan(cutoffTime);
4025

@@ -51,25 +36,24 @@ public void syncPendingPayments() {
5136
for (PaymentInfo paymentInfo : pendingPayments) {
5237
try {
5338
if (paymentInfo.transactionKey() == null || paymentInfo.transactionKey().isEmpty()) {
54-
log.warn("[PaymentSync] transactionKey 없음, 스킵 - orderId: {}", paymentInfo.orderId());
39+
log.warn("[PaymentSync] transactionKey 없음, 스킵 - orderNo: {}", paymentInfo.orderNo());
5540
continue;
5641
}
5742

58-
// PG 상태 조회 및 동기화
5943
PaymentInfo syncedPayment = paymentFacade.syncPaymentStatus(
6044
"SCHEDULER",
6145
paymentInfo.transactionKey()
6246
);
6347

64-
log.info("[PaymentSync] 동기화 완료 - orderId: {}, status: {} -> {}",
65-
paymentInfo.orderId(),
48+
log.info("[PaymentSync] 동기화 완료 - orderNo: {}, status: {} -> {}",
49+
paymentInfo.orderNo(),
6650
paymentInfo.status(),
6751
syncedPayment.status());
6852

6953
successCount++;
7054

7155
} catch (Exception e) {
72-
log.error("[PaymentSync] 동기화 실패 - orderId: {}", paymentInfo.orderId(), e);
56+
log.error("[PaymentSync] 동기화 실패 - orderNo: {}", paymentInfo.orderNo(), e);
7357
failureCount++;
7458
}
7559
}
@@ -81,13 +65,6 @@ public void syncPendingPayments() {
8165
}
8266
}
8367

84-
/**
85-
* 재시도 필요 결제 처리
86-
*
87-
* PG 장애로 인해 재시도가 필요한 결제들을 수동으로 처리
88-
*
89-
* 실행 주기: 5분마다
90-
*/
9168
@Scheduled(fixedDelay = 300000, initialDelay = 120000)
9269
public void processRetryPayments() {
9370
log.info("[PaymentRetry] 재시도 필요 결제 처리 시작");
@@ -103,7 +80,7 @@ public void processRetryPayments() {
10380
log.info("[PaymentRetry] 재시도 필요 결제 {}건 발견", retryPayments.size());
10481

10582
for (PaymentInfo paymentInfo : retryPayments) {
106-
log.warn("[PaymentRetry] 수동 처리 필요 - orderId: {}, 관리자 확인 필요", paymentInfo.orderId());
83+
log.warn("[PaymentRetry] 수동 처리 필요 - orderNo: {}, 관리자 확인 필요", paymentInfo.orderNo());
10784
// TODO: 관리자 알림, 수동 재시도 API 제공 등
10885
}
10986

apps/commerce-api/src/main/java/com/loopers/infrastructure/payment/pg/PgPaymentClient.java

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.loopers.infrastructure.payment.pg;
22

3+
import com.loopers.domain.payment.CardType;
4+
import com.loopers.domain.payment.gateway.PgGateway;
35
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
46
import io.github.resilience4j.retry.annotation.Retry;
57
import io.github.resilience4j.timelimiter.annotation.TimeLimiter;
@@ -17,7 +19,7 @@
1719

1820
@Slf4j
1921
@Component
20-
public class PgPaymentClient {
22+
public class PgPaymentClient implements PgGateway {
2123

2224
private final RestTemplate pgRestTemplate;
2325

@@ -30,23 +32,26 @@ public PgPaymentClient(RestTemplate pgRestTemplate) {
3032
this.pgRestTemplate = pgRestTemplate;
3133
}
3234

33-
/**
34-
* 결제 요청 API
35-
* - CircuitBreaker: PG 장애 시 빠른 실패
36-
* - Retry: 일시적 오류 시 재시도
37-
* - TimeLimiter: 2초 타임아웃
38-
*/
35+
@Override
3936
@CircuitBreaker(name = PG_INSTANCE, fallbackMethod = "requestPaymentFallback")
4037
@Retry(name = PG_INSTANCE)
4138
@TimeLimiter(name = PG_INSTANCE)
42-
public CompletableFuture<PgPaymentResponse> requestPayment(String userId, PgPaymentRequest request) {
39+
public CompletableFuture<PgPaymentResult> requestPayment(String userId, PgPaymentCommand command) {
4340
return CompletableFuture.supplyAsync(() -> {
44-
log.info("[PG] 결제 요청 시작 - orderId: {}, amount: {}", request.orderId(), request.amount());
41+
log.info("[PG] 결제 요청 시작 - orderId: {}, amount: {}", command.orderId(), command.amount());
4542

4643
HttpHeaders headers = new HttpHeaders();
4744
headers.set("X-USER-ID", userId);
4845
headers.set("Content-Type", "application/json");
4946

47+
PgPaymentRequest request = new PgPaymentRequest(
48+
command.orderId(),
49+
toPgCardType(command.cardType()),
50+
command.cardNo(),
51+
command.amount(),
52+
command.callbackUrl()
53+
);
54+
5055
HttpEntity<PgPaymentRequest> entity = new HttpEntity<>(request, headers);
5156

5257
try {
@@ -62,25 +67,25 @@ public CompletableFuture<PgPaymentResponse> requestPayment(String userId, PgPaym
6267
PgPaymentResponse paymentResponse = response.getBody().data();
6368
log.info("[PG] 결제 요청 성공 - transactionKey: {}, status: {}",
6469
paymentResponse.transactionKey(), paymentResponse.status());
65-
return paymentResponse;
70+
return new PgPaymentResult(
71+
paymentResponse.transactionKey(),
72+
toPgTransactionStatus(paymentResponse.status()),
73+
paymentResponse.reason()
74+
);
6675
} else {
6776
throw new PgCommunicationException("PG 응답이 비어있습니다.");
6877
}
6978
} catch (RestClientException e) {
70-
log.error("[PG] 결제 요청 실패 - orderId: {}", request.orderId(), e);
79+
log.error("[PG] 결제 요청 실패 - orderId: {}", command.orderId(), e);
7180
throw new PgCommunicationException("PG 통신 실패", e);
7281
}
7382
});
7483
}
7584

76-
/**
77-
* 결제 상태 조회 API
78-
* - CircuitBreaker 적용
79-
* - Retry 적용
80-
*/
85+
@Override
8186
@CircuitBreaker(name = PG_INSTANCE, fallbackMethod = "getPaymentStatusFallback")
8287
@Retry(name = PG_INSTANCE)
83-
public PgPaymentDetailResponse getPaymentStatus(String userId, String transactionKey) {
88+
public PgPaymentDetail getPaymentStatus(String userId, String transactionKey) {
8489
log.info("[PG] 결제 상태 조회 - transactionKey: {}", transactionKey);
8590

8691
HttpHeaders headers = new HttpHeaders();
@@ -101,7 +106,15 @@ public PgPaymentDetailResponse getPaymentStatus(String userId, String transactio
101106
PgPaymentDetailResponse detailResponse = response.getBody().data();
102107
log.info("[PG] 결제 상태 조회 성공 - transactionKey: {}, status: {}",
103108
transactionKey, detailResponse.status());
104-
return detailResponse;
109+
return new PgPaymentDetail(
110+
detailResponse.transactionKey(),
111+
detailResponse.orderId(),
112+
toCardType(detailResponse.cardType()),
113+
detailResponse.cardNo(),
114+
detailResponse.amount(),
115+
toPgTransactionStatus(detailResponse.status()),
116+
detailResponse.reason()
117+
);
105118
} else {
106119
throw new PgCommunicationException("PG 응답이 비어있습니다.");
107120
}
@@ -111,69 +124,60 @@ public PgPaymentDetailResponse getPaymentStatus(String userId, String transactio
111124
}
112125
}
113126

114-
// ========== Fallback Methods ==========
115-
116-
/**
117-
* 결제 요청 Fallback
118-
* - Circuit Open 또는 재시도 실패 시 호출
119-
* - transactionKey를 null로 반환하여 재시도 필요함을 표시
120-
*/
121-
private CompletableFuture<PgPaymentResponse> requestPaymentFallback(
127+
private CompletableFuture<PgPaymentResult> requestPaymentFallback(
122128
String userId,
123-
PgPaymentRequest request,
129+
PgPaymentCommand command,
124130
Exception ex
125131
) {
126132
log.error("[PG Fallback] 결제 요청 실패 - orderId: {}, error: {}",
127-
request.orderId(), ex.getMessage());
133+
command.orderId(), ex.getMessage());
128134

129-
// transactionKey를 null로 반환하여 PG 장애를 명확히 표시
130-
// Service 레이어에서 이를 감지하고 requiresRetry 플래그 설정
131-
PgPaymentResponse fallbackResponse = new PgPaymentResponse(
132-
null, // transactionKey가 없음 = PG 호출 실패
133-
PgPaymentResponse.PgTransactionStatus.PENDING,
135+
PgPaymentResult fallbackResponse = new PgPaymentResult(
136+
null,
137+
PgTransactionStatus.PENDING,
134138
"PG 시스템 일시 장애 - 나중에 재시도가 필요합니다."
135139
);
136140

137141
return CompletableFuture.completedFuture(fallbackResponse);
138142
}
139143

140-
/**
141-
* 결제 상태 조회 Fallback
142-
* - 조회 실패 시 PENDING 상태로 반환
143-
*/
144-
private PgPaymentDetailResponse getPaymentStatusFallback(
144+
private PgPaymentDetail getPaymentStatusFallback(
145145
String userId,
146146
String transactionKey,
147147
Exception ex
148148
) {
149149
log.warn("[PG Fallback] 결제 상태 조회 실패, PENDING 상태로 반환 - transactionKey: {}, error: {}",
150150
transactionKey, ex.getMessage());
151151

152-
return new PgPaymentDetailResponse(
152+
return new PgPaymentDetail(
153153
transactionKey,
154154
"UNKNOWN",
155155
null,
156156
"****-****-****-****",
157157
0L,
158-
PgPaymentResponse.PgTransactionStatus.PENDING,
158+
PgTransactionStatus.PENDING,
159159
"PG 시스템 일시 장애 - 결제 상태를 확인할 수 없습니다."
160160
);
161161
}
162162

163-
// ========== Helper Classes ==========
163+
private PgPaymentRequest.PgCardType toPgCardType(CardType cardType) {
164+
return PgPaymentRequest.PgCardType.valueOf(cardType.name());
165+
}
166+
167+
private CardType toCardType(PgPaymentRequest.PgCardType cardType) {
168+
return CardType.valueOf(cardType.name());
169+
}
170+
171+
private PgTransactionStatus toPgTransactionStatus(PgPaymentResponse.PgTransactionStatus status) {
172+
return PgTransactionStatus.valueOf(status.name());
173+
}
164174

165-
/**
166-
* PG API 응답 래퍼
167-
*/
168175
public record PgApiResponse<T>(
169176
String result,
170177
String message,
171178
T data
172179
) {}
173180

174-
/**
175-
* PG 통신 예외
176-
*/
177181
public static class PgCommunicationException extends RuntimeException {
178182
public PgCommunicationException(String message) {
179183
super(message);

apps/commerce-api/src/main/java/com/loopers/interfaces/api/payment/PaymentController.java

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@
99
import org.springframework.http.ResponseEntity;
1010
import org.springframework.web.bind.annotation.*;
1111

12-
/**
13-
* Payment API 컨트롤러
14-
* - 결제 요청, 조회, 상태 동기화
15-
*/
1612
@Slf4j
1713
@RestController
1814
@RequestMapping("/api/v1/payments")
@@ -21,11 +17,6 @@ public class PaymentController {
2117

2218
private final PaymentFacade paymentFacade;
2319

24-
/**
25-
* 결제 요청
26-
*
27-
* POST /api/v1/payments
28-
*/
2920
@PostMapping
3021
public ResponseEntity<PaymentResponse> requestPayment(
3122
@RequestHeader("X-USER-ID") String userId,
@@ -46,28 +37,18 @@ public ResponseEntity<PaymentResponse> requestPayment(
4637
return ResponseEntity.ok(PaymentResponse.from(paymentInfo));
4738
}
4839

49-
/**
50-
* orderId로 결제 정보 조회
51-
*
52-
* GET /api/v1/payments/orders/{orderId}
53-
*/
54-
@GetMapping("/orders/{orderId}")
55-
public ResponseEntity<PaymentResponse> getPaymentByOrderId(
40+
@GetMapping("/orders/{orderNo}")
41+
public ResponseEntity<PaymentResponse> getPaymentByOrderNo(
5642
@RequestHeader("X-USER-ID") String userId,
57-
@PathVariable String orderId
43+
@PathVariable String orderNo
5844
) {
59-
log.info("[Payment API] 결제 조회 - userId: {}, orderId: {}", userId, orderId);
45+
log.info("[Payment API] 결제 조회 - userId: {}, orderNo: {}", userId, orderNo);
6046

61-
PaymentInfo paymentInfo = paymentFacade.getPaymentByOrderId(orderId);
47+
PaymentInfo paymentInfo = paymentFacade.getPaymentByOrderNo(orderNo);
6248

6349
return ResponseEntity.ok(PaymentResponse.from(paymentInfo));
6450
}
6551

66-
/**
67-
* transactionKey로 결제 상태 동기화
68-
*
69-
* POST /api/v1/payments/{transactionKey}/sync
70-
*/
7152
@PostMapping("/{transactionKey}/sync")
7253
public ResponseEntity<PaymentResponse> syncPaymentStatus(
7354
@RequestHeader("X-USER-ID") String userId,
@@ -80,8 +61,6 @@ public ResponseEntity<PaymentResponse> syncPaymentStatus(
8061
return ResponseEntity.ok(PaymentResponse.from(paymentInfo));
8162
}
8263

83-
// ========== DTOs ==========
84-
8564
public record PaymentRequest(
8665
String orderId,
8766
CardType cardType,
@@ -92,7 +71,7 @@ public record PaymentRequest(
9271

9372
public record PaymentResponse(
9473
Long id,
95-
String orderId,
74+
String orderNo,
9675
String transactionKey,
9776
String status,
9877
String cardType,
@@ -104,7 +83,7 @@ public record PaymentResponse(
10483
public static PaymentResponse from(PaymentInfo info) {
10584
return new PaymentResponse(
10685
info.id(),
107-
info.orderId(),
86+
info.orderNo(),
10887
info.transactionKey(),
10988
info.status().name(),
11089
info.cardType().name(),

0 commit comments

Comments
 (0)