#175 fix(payment): PG confirm/cancel 실패 시 상태 유실 방지 - #176
Merged
Conversation
CB fallback 예외(PaymentGatewayTemporarilyUnavailableException / PaymentGatewayExternalCallFailedException) 발생 시 아무 흔적도 남지 않던 문제를 최소 영속화로 해결. - PaymentTransactionService.createRequested() 추가: PG 호출 전 Payment를 REQUESTED로 선저장, 실패 시 handlePaymentFailure()로 FAILED 전환 - RefundTransactionService.saveRequested() 추가: PG cancel 호출 전 Refund를 REQUESTED로 선저장 - prepareRefund()의 "이미 환불됨" 판단을 상태 기반으로 수정 (REQUESTED는 재시도 대상, 그 외는 완료로 간주) — REQUESTED row 선저장이 재시도 자체를 막아버리는 회귀를 방지 정산 배치, 알림 큐, 재조회 스케줄러는 스코프에서 제외. Resolves: #175
Contributor
There was a problem hiding this comment.
Pull request overview
PG confirm/cancel 호출이 CircuitBreaker fallback 또는 외부 호출 실패로 중단될 때 Payment/Refund row가 생성되지 않아 “재조회 근거”가 유실되던 문제를, PG 호출 전에 REQUESTED 상태를 선저장하는 방식으로 최소 영속화해 복구 가능성을 높이는 PR입니다.
Changes:
- (Payment) PG confirm 전에
PaymentTransactionService.createRequested()로 REQUESTED 선저장, 예외 시 실패 처리 경로 추가 - (Refund) PG cancel 전에
RefundTransactionService.saveRequested()로 REQUESTED 선저장, REQUESTED 상태의 기존 Refund는 재시도 대상으로 처리 - 위 변경을 검증하는 단위 테스트 신규 추가/보강
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| payment/src/main/java/wisoft/nextframe/payment/application/payment/PaymentService.java | PG confirm 전 선저장 + PG 예외 처리 로직 추가 |
| payment/src/main/java/wisoft/nextframe/payment/application/payment/PaymentTransactionService.java | createRequested() 추가 및 handlePaymentFailure() 접근/트랜잭션 변경 |
| payment/src/main/java/wisoft/nextframe/payment/application/refund/RefundService.java | PG cancel 전 선저장 + PG 예외 처리 로직 추가 |
| payment/src/main/java/wisoft/nextframe/payment/application/refund/RefundTransactionService.java | REQUESTED 상태 재시도 처리 + saveRequested() 추가 |
| payment/src/test/java/wisoft/nextframe/payment/application/payment/PaymentServiceTest.java | PaymentService 예외/성공 흐름 단위 테스트 신규 |
| payment/src/test/java/wisoft/nextframe/payment/application/payment/PaymentTransactionServiceTest.java | createRequested() 신규 케이스 추가 |
| payment/src/test/java/wisoft/nextframe/payment/application/refund/RefundServiceTest.java | RefundService 선저장/예외/스킵 흐름 단위 테스트 신규 |
| payment/src/test/java/wisoft/nextframe/payment/application/refund/RefundTransactionServiceTest.java | REQUESTED 재시도 분기 + saveRequested() 테스트 추가 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
PaymentGatewayExternalCallFailedException(타임아웃 등 실제 호출 시도 후 실패)까지 handlePaymentFailure()로 FAILED 확정하면, PG가 실제로는 처리했는데 응답만 유실된 경우에도 예약 취소가 발생하고 이후 재시도 confirm도 Payment.approve()가 막혀 불가능해지는 문제가 있었음. - CB OPEN(PaymentGatewayTemporarilyUnavailableException): PG에 요청 자체가 전달되지 않았으므로 안전하게 FAILED 확정 - 외부 호출 실패(PaymentGatewayExternalCallFailedException): PG 처리 여부가 불확실하므로 REQUESTED로 유지, 재조회/재시도 대상으로만 남김 Resolves: #175
payments.reservation_id는 DB unique 제약이 있어, 동시 confirm 요청 두 개가 findByReservationId()에서 함께 빈 결과를 본 뒤 둘 다 save()로 진입하면 하나는 DataIntegrityViolationException으로 실패할 수 있었음. - createRequested()에서 save() 실패 시 승자 row를 재조회해 반환하도록 처리 - 재조회가 같은 세션에서 깨진 트랜잭션을 다시 쓰지 않도록 메서드의 @transactional을 제거하여 조회/저장 각각이 독립된 트랜잭션을 갖게 함 Resolves: #175
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🛠️ 설명 (Description)
PG(Payment Gateway) 결제 승인 또는 환불 취소 요청 실패 시 애플리케이션의 상태 유실을 방지하고, 데이터 정합성을 유지하기 위한 변경사항입니다.
이전에는 PG 외부 호출 실패 시 결제 또는 환불 상태가 DB에 반영되지 않아 재시도나 복구 시점에서 데이터 불일치가 발생할 수 있었습니다.
이를 개선하기 위해 PG 호출 직전에 결제/환불 상태를 "요청됨(REQUESTED)"으로 미리 저장하고, PG 호출 실패 시 해당 상태를 업데이트하거나 재시도 근거로 활용하도록 변경했습니다.
주요 변경 내용은 다음과 같습니다:
Payment객체를REQUESTED상태로 저장하고, PG 호출이 실패하면 해당Payment를FAILED상태로 업데이트합니다. 이는 네트워크 문제나 PG 일시 중단 등으로 인한 상태 유실을 방지하여 결제 실패 기록을 명확히 남깁니다.Refund객체를REQUESTED상태로 저장합니다. PG 호출이 실패하면Refund는REQUESTED상태로 유지되어, 운영자가 상태를 확인하고 필요에 따라 수동 재시도할 수 있는 근거를 제공합니다. 또한,RefundTransactionService.prepareRefund메서드는 이미REQUESTED상태로 남아있는 환불 요청이 있을 경우, 이를 재시도 대상으로 간주하여 반환하도록 멱등성 로직을 개선했습니다.이러한 변경을 통해 시스템 장애 및 외부 서비스 연동 실패 시에도 결제 및 환불 관련 데이터의 정합성을 높이고, 장애 복구 및 재시도에 필요한 기반을 마련합니다.
📄 설계 문서 (Design Document)
N/A
✅ 테스트 계획 (Test Plan)
PaymentTransactionService.applyConfirmResult로 위임하는지 확인PaymentTransactionService.handlePaymentFailure를 호출하고 예외를 다시 던지는지 확인createRequested메서드가 기존Payment가 없으면REQUESTED상태로 새로 생성 및 저장하고, 기존Payment가 있으면 기존 것을 반환하는지 확인Refund를 선저장(REQUESTED)하고, 성공하면 완료 처리 및 예약 취소를 수행하는지 확인Refund를 선저장(REQUESTED)까지만 하고 완료 처리 없이 예외를 다시 던지는지 확인prepareRefund메서드가REQUESTED상태로 남아있는 환불 요청을 재시도 대상으로 반환하는지 확인saveRequested메서드가 PG 호출 전Refund를REQUESTED상태로 저장하는지 확인📝 변경 사항 요약 (Summary)
PaymentService.confirmPayment메서드에 PG 결제 승인 호출 전 Payment를REQUESTED상태로 선저장하는 로직 추가FAILED상태로 업데이트하는 예외 처리 로직 추가PaymentTransactionService에 Payment를REQUESTED상태로 생성하거나 기존 Payment를 반환하는createRequested메서드 추가PaymentTransactionService.handlePaymentFailure메서드를@Transactional및public으로 변경하여 PaymentService에서 호출 가능하도록 수정RefundService.refund메서드에 PG 환불 호출 전 Refund를REQUESTED상태로 선저장하는 로직 추가REQUESTED상태로 유지하여 재시도 대상이 되도록 처리 (완료 처리 없이 예외를 다시 던짐)RefundTransactionService에 Refund를REQUESTED상태로 저장하는saveRequested메서드 추가RefundTransactionService.prepareRefund메서드에서RefundStatus.REQUESTED상태의 기존 Refund는 재시도 대상으로 간주하여 반환하도록 멱등성 로직 수정PaymentServiceTest,PaymentTransactionServiceTest,RefundServiceTest,RefundTransactionServiceTest에 위 변경 사항에 대한 단위 테스트 코드 추가 및 수정🔗 관련 이슈 (Related Issues)
☑️ 체크리스트 (Checklist)
👀 리뷰어를 위한 참고 사항 (Notes for Reviewers)
REQUESTED상태) 전략이 Payment와 Refund 각각에 적용되었습니다.FAILED로 즉시 업데이트하여 최종 실패 상태를 명확히 합니다.REQUESTED상태로 유지하여 재시도 가능성을 열어두고,prepareRefund로직에서REQUESTED상태의 Refund를 재시도 대상으로 처리합니다. 이 부분의 설계 의도와 적절성에 대해 의견 주시면 감사하겠습니다.➕ 추가 정보 (Additional Information)
N/A