[PR] 결제 verify api 구현 - #606
Conversation
…oFighters/BACK-Fighters into feature/sooyoung0927-payment
📝 WalkthroughWalkthrough결제 검증 API가 PortOne 결제 조회·취소, 결제 상태 전환, Redis 락, 멤버십 갱신 및 결제 예외 응답 처리와 연결되었다. Changes결제 검증 흐름
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant PaymentController
participant PaymentCommandService
participant PortOnePaymentAdapter
participant SetUserMembershipAdapter
Client->>PaymentController: POST /verify
PaymentController->>PaymentCommandService: paymentVerify(command)
PaymentCommandService->>PortOnePaymentAdapter: verifyPayment(paymentId)
PortOnePaymentAdapter-->>PaymentCommandService: PortOnePaymentDetail
PaymentCommandService->>SetUserMembershipAdapter: updateMembership(...)
SetUserMembershipAdapter-->>PaymentCommandService: membership updated
PaymentCommandService-->>PaymentController: PaymentVerifyResult
PaymentController-->>Client: PaymentVerifyResponse
Possibly related PRs
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
legend-momo-city/src/main/java/com/wanted/momocity/payment/presentation/api/PaymentController.java (1)
37-42: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winSwagger 요약에 오타가 있습니다.
"본 결제 전 확인 단게"→"본 결제 전 확인 단계"로 수정해 주세요. 사용자에게 노출되는 API 문서 문자열입니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@legend-momo-city/src/main/java/com/wanted/momocity/payment/presentation/api/PaymentController.java` around lines 37 - 42, Update the summary value in PaymentController’s `@Operation` for the /prepare endpoint, correcting the typo from “단게” to “단계” while leaving the description unchanged.
🧹 Nitpick comments (3)
legend-momo-city/src/main/java/com/wanted/momocity/payment/application/service/PaymentCommandService.java (1)
60-114: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift외부 API 호출이
@Transactional범위 내에서 수행됩니다.
paymentVerify는 클래스 레벨@Transactional로 인해 DB 트랜잭션이 열린 상태에서 PortOne API 호출(verifyPayment,cancelPayment)을 수행합니다. 외부 HTTP 호출 지연 시 DB 커넥션이 장시간 점유되어 커넥션 풀 고갈 위험이 있습니다.외부 호출 전후로 트랜잭션을 분리하거나, 외부 호출 결과를 바탕으로 별도 트랜잭션에서 DB 갱신을 수행하는 구조를 권장합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@legend-momo-city/src/main/java/com/wanted/momocity/payment/application/service/PaymentCommandService.java` around lines 60 - 114, Refactor paymentVerify so the PortOne calls verifyPayment and cancelPayment do not execute within the class-level database transaction. Separate external API interaction from persistence, then perform payment state changes and membership updates through a distinct transactional method or service using the verified result, preserving the existing success, mismatch, and cancellation-failure outcomes.legend-momo-city/src/main/java/com/wanted/momocity/payment/presentation/api/PaymentController.java (1)
89-99: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value주석 처리된 dead code를 제거해 주세요.
//@PatchMapping("/cancle")등 사용하지 않는 코드가 주석으로 남아 있습니다. 향후 환불 기능은 별도 PR에서 구현할 때 추가하고, 현재는 제거하는 것이 가독성에 좋습니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@legend-momo-city/src/main/java/com/wanted/momocity/payment/presentation/api/PaymentController.java` around lines 89 - 99, PaymentController 주변에 남아 있는 주석 처리된 환불 엔드포인트와 관련 설명을 제거하세요. 향후 별도 PR에서 구현할 예정이므로 `@PatchMapping`("/cancle") 및 해당 주석 블록만 삭제하고, 인접한 실제 컨트롤러 코드는 유지하세요.legend-momo-city/src/main/java/com/wanted/momocity/payment/domain/exception/PaymentExceptionHandler.java (1)
46-122: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value에러 코드 문자열이 하드코딩되어 있습니다.
"PAYMENT_NOT_FOUND","PORTONE_API_ERROR"등 에러 코드가 문자열 리터럴로 직접 작성되어 있습니다. 기존 핸들러(13-45)도 동일한 패턴이므로 일관성은 맞지만, 오타 발생 시 컴파일 타임에 잡을 수 없어 유지보수 리스크가 있습니다.PaymentResponseCode처럼 상수 클래스(예:PaymentErrorCode)를 도입해 참조하는 것을 권장합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@legend-momo-city/src/main/java/com/wanted/momocity/payment/domain/exception/PaymentExceptionHandler.java` around lines 46 - 122, PaymentExceptionHandler의 각 핸들러에 하드코딩된 에러 코드 문자열을 제거하고 PaymentResponseCode 또는 새 PaymentErrorCode 상수 클래스의 정의된 상수를 참조하도록 변경하세요. PaymentNotFoundException, PortOneApiException 및 나머지 결제 예외 핸들러의 코드 값과 응답 구조는 기존 의미를 그대로 유지하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/application/service/PaymentCommandService.java`:
- Around line 63-64: PaymentCommandService의 결제 조회 직후 command.userId()와
payment.getUserId()가 일치하는지 검증하도록 수정하세요. 불일치하면 기존 도메인 예외 처리 방식에 맞는 예외를 발생시키고,
일치하는 경우에만 이후 결제 검증 및 멤버십 갱신 로직을 진행하세요. Payment에 getUserId()가 없다면 해당 접근 메서드를
추가하세요.
- Around line 82-114: 분기 내부의 paymentRepository.save 호출은 이후 RuntimeException으로
롤백되므로 실패 상태가 보존되지 않습니다. PaymentCommandService의 실패 처리 흐름에서 FAILED 및 CANCEL_FAILED
저장을 별도 REQUIRES_NEW 트랜잭션 서비스나 메서드로 분리하고, PaymentAmountMismatchException 및
PaymentCancelFailedException을 던지기 전에 해당 저장을 호출하도록 수정하세요.
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/domain/model/Payment.java`:
- Around line 43-68: Payment.markSuccess의 불완전한 주석을 “포트원 실결제금액과 DB 금액이 일치할 때”처럼
완전한 문장으로 수정하세요. Payment.markFailed 주변 주석의 pgTxId 언급은 현재 필드가 없으므로 제거하고, 실제 구현과
일치하는 금액 불일치·결제 실패 설명만 남기세요.
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PortOnePaymentAdapter.java`:
- Around line 19-51: PortOnePaymentAdapter의 verifyPayment 및 cancelPayment에서
WebClient의 .block() 호출에 명시적인 Duration 타임아웃을 적용하세요. 필요한 java.time.Duration을
import하고, PortOne API 응답이 지연될 때 무기한 대기하지 않도록 두 메서드의 blocking 호출 모두 동일한 적절한 타임아웃을
사용하게 수정하세요.
- Around line 44-48: Update the amount parsing in PortOnePaymentAdapter so a
non-null amountMap with a null amountMap.get("total") produces a null total
instead of calling Long.valueOf on "null". Preserve numeric total conversion
when the field is present and avoid the resulting NumberFormatException.
- Around line 53-62: Update PortOnePaymentAdapter.cancelPayment to catch
WebClientResponseException from the PortOne cancellation request and convert it
to the existing PortOneApiException type, matching the error-handling behavior
of verifyPayment so PaymentCommandService can persist CANCEL_FAILED without
transaction rollback.
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/persistence/PaymentRepositoryAdapter.java`:
- Around line 28-32: Update findPendingByUserIdAndPlan to apply the targetPlan
filter when querying pending payments, using the corresponding Spring Data
repository method and preserving the existing domain mapping. If plan filtering
is intentionally removed instead, rename this method and update
PaymentRepository plus PaymentCommandService.paymentPrepare to remove the
obsolete parameter and reflect the broader lookup.
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/persistence/SpringDataPaymentRepository.java`:
- Line 11: Restore the Plan predicate in
SpringDataPaymentRepository.findFirstByUserIdAndPlanAndStatus and update
PaymentRepositoryAdapter.findPendingByUserIdAndPlan to pass targetPlan through,
ensuring /prepare retrieves the latest PENDING payment for the requested plan
rather than any plan.
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/presentation/api/PaymentController.java`:
- Around line 65-87: PaymentController의 paymentVerify 흐름에서 결제 소유자 검증을 추가하세요.
paymentId로 조회한 결제의 payment.getUserId()와 PaymentVerifyCommand의 userId()를 비교하고,
불일치하면 검증 및 멤버십 갱신을 중단하도록 paymentCommandUseCase의 관련 검증 로직을 수정하세요.
---
Outside diff comments:
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/presentation/api/PaymentController.java`:
- Around line 37-42: Update the summary value in PaymentController’s `@Operation`
for the /prepare endpoint, correcting the typo from “단게” to “단계” while leaving
the description unchanged.
---
Nitpick comments:
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/application/service/PaymentCommandService.java`:
- Around line 60-114: Refactor paymentVerify so the PortOne calls verifyPayment
and cancelPayment do not execute within the class-level database transaction.
Separate external API interaction from persistence, then perform payment state
changes and membership updates through a distinct transactional method or
service using the verified result, preserving the existing success, mismatch,
and cancellation-failure outcomes.
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/domain/exception/PaymentExceptionHandler.java`:
- Around line 46-122: PaymentExceptionHandler의 각 핸들러에 하드코딩된 에러 코드 문자열을 제거하고
PaymentResponseCode 또는 새 PaymentErrorCode 상수 클래스의 정의된 상수를 참조하도록 변경하세요.
PaymentNotFoundException, PortOneApiException 및 나머지 결제 예외 핸들러의 코드 값과 응답 구조는 기존
의미를 그대로 유지하세요.
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/presentation/api/PaymentController.java`:
- Around line 89-99: PaymentController 주변에 남아 있는 주석 처리된 환불 엔드포인트와 관련 설명을 제거하세요.
향후 별도 PR에서 구현할 예정이므로 `@PatchMapping`("/cancle") 및 해당 주석 블록만 삭제하고, 인접한 실제 컨트롤러 코드는
유지하세요.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e29b8e6a-49da-4dea-9740-4df18c375b66
📒 Files selected for processing (27)
legend-momo-city/src/main/java/com/wanted/momocity/payment/application/command/PaymentVerifyCommand.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/application/port/PortOnePaymentPort.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/application/port/SetUserMembershipPort.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/application/service/PaymentCommandService.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/application/usecase/PaymentCommandUseCase.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/exception/PaymentAlreadyVerifiedException.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/exception/PaymentAmountMismatchException.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/exception/PaymentCancelFailedException.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/exception/PaymentExceptionHandler.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/exception/PaymentNotAttemptedException.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/exception/PaymentNotFoundException.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/exception/PortOneApiException.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/model/Payment.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/model/PaymentVerifyResult.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/model/PortOnePaymentDetail.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/model/Status.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/repository/PaymentRepository.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PortOnePaymentAdapter.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/persistence/PaymentRepositoryAdapter.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/persistence/SpringDataPaymentRepository.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/presentation/api/PaymentController.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/presentation/api/common/PaymentResponseCode.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/presentation/api/common/PaymentResponseMessage.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/presentation/api/request/PaymentVerifyRequest.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/presentation/api/response/PaymentVerifyResponse.javalegend-momo-city/src/main/java/com/wanted/momocity/user/infrastructure/paymentadapter/SetUserMembershipAdapter.javalegend-momo-city/src/main/java/com/wanted/momocity/user/infrastructure/persistence/SpringDataUserRepository.java
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
legend-momo-city/src/main/java/com/wanted/momocity/payment/application/service/PaymentCommandService.java (1)
66-84: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winearly exit 경로에서 락이 해제되지 않습니다.
paymentPrepare에서tryLock로 획득한 락이paymentVerify의 다음 예외 경로에서 해제되지 않습니다:
PaymentNotFoundException(line 69)PaymentAccessDeniedException(line 73)PaymentAlreadyVerifiedException(line 79)portOnePaymentPort.verifyPayment실패 (line 84)이 경우 TTL(5분) 만료까지 사용자가 새 결제를 시도할 수 없습니다. 메서드 전체를 try-finally로 감싸거나, 각 예외 발생 지점에서 unlock를 호출해야 합니다.
🔒️ Proposed fix
`@Override` public PaymentVerifyResult paymentVerify(PaymentVerifyCommand command) { + Payment payment = null; + try { // 결제 건 조회 - Payment payment = paymentRepository.findByPaymentId(command.paymentId()) + payment = paymentRepository.findByPaymentId(command.paymentId()) .orElseThrow(() -> new PaymentNotFoundException("결제 정보를 찾을 수 없습니다.")); // 정말 본인이 맞는지 확인 if (!payment.getUserId().equals(command.userId())) { throw new PaymentAccessDeniedException("결제 소유자가 아닙니다."); } // 이미 검증 처리가 완료된 결제건 if (payment.isFinalized()) { throw new PaymentAlreadyVerifiedException( "이미 처리된 결제 건입니다. status=" + payment.getStatus() ); } // 포트원에서 실제 결제 내역 조회 PortOnePaymentDetail detail = portOnePaymentPort.verifyPayment(command.paymentId()); // 검증 boolean amountMatches = detail.isPaid() && payment.getPrice().equals(detail.amount()); // 검증 성송 if (amountMatches) { Payment result = payment.markSuccess(); LocalDateTime membershipStart = LocalDateTime.now(); setUserMembershipPort.updateMembership(command.userId(), payment.getPlan(), membershipStart); LocalDateTime membershipUntil = membershipStart.plusDays(30); paymentRepository.save(result); - paymentLockPort.unlock(command.userId(), payment.getPlan()); // 여기 + return new PaymentVerifyResult(result.getPaymentId(), result.getStatus(), membershipUntil); + } // 금액 불일치 - 여기서부터는 전부 실패 if (detail.isPaid()) { try { portOnePaymentPort.cancelPayment(command.paymentId(), "결제 금액 불일치로 인한 자동 취소"); Payment failed = payment.markFailed(); paymentStatusUpdater.saveFailed(failed); throw new PaymentAmountMismatchException( "결제 금액이 일치하지 않아 취소 처리되었습니다. 예상 결제 금액 =" + payment.getPrice() + ", 실제 결제 금액=" + detail.amount() ); } catch (PortOneApiException e) { Payment cancelFailed = payment.markCancelFailed(); paymentStatusUpdater.saveCancelFailed(cancelFailed); throw new PaymentCancelFailedException( "취소 처리 실패 paymentId=" + command.paymentId() ); - }finally { - paymentLockPort.unlock(command.userId(), payment.getPlan()); } } else { Payment failed = payment.markFailed(); paymentStatusUpdater.saveFailed(failed); - paymentLockPort.unlock(command.userId(), payment.getPlan()); throw new PaymentAmountMismatchException("결제가 완료되지 않았습니다."); } + } finally { + if (payment != null) { + paymentLockPort.unlock(payment.getUserId(), payment.getPlan()); + } + } }
unlock에command.userId()대신payment.getUserId()를 사용하는 것이 더 안전합니다.PaymentAccessDeniedException경로에서command.userId()는 결제 소유자가 아닐 수 있기 때문입니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@legend-momo-city/src/main/java/com/wanted/momocity/payment/application/service/PaymentCommandService.java` around lines 66 - 84, Update paymentVerify so every path after paymentPrepare’s lock acquisition releases the lock, including payment lookup failure, ownership denial, already-finalized payments, and verifyPayment failures. Wrap the full flow in try-finally and unlock using the payment owner’s identifier when available, while safely handling the PaymentNotFoundException path before a Payment instance exists.
🧹 Nitpick comments (2)
legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PortOnePaymentAdapter.java (1)
70-91: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win
cancelPayment에서 네트워크 예외(WebClientRequestException) 처리 누락
WebClientResponseException만 catch하므로, PortOne 서버가 응답하지 않거나 연결이 거부된 경우WebClientRequestException이 그대로 전파됩니다. 이 경우PaymentCommandService의catch (PortOneApiException e)에서 잡히지 않아CANCEL_FAILED상태가 저장되지 않습니다.필요하다면
WebClientRequestException도PortOneApiException으로 래핑하는 것을 고려해 보세요. 단, 현재 PR의 범위에서는 필수 사항은 아닙니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PortOnePaymentAdapter.java` around lines 70 - 91, Update cancelPayment in PortOnePaymentAdapter to catch WebClientRequestException in addition to WebClientResponseException, wrapping network failures in PortOneApiException so PaymentCommandService can persist the CANCEL_FAILED state. Preserve the existing response-status and response-body details for WebClientResponseException.legend-momo-city/src/main/java/com/wanted/momocity/payment/application/service/PaymentCommandService.java (1)
105-122: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win
catch (PortOneApiException e)가PaymentAmountMismatchException을 놓칠 수 있습니다.
try블록에서cancelPayment성공 후paymentStatusUpdater.saveFailed(failed)가 실패하면PaymentAmountMismatchException이 아니라 다른RuntimeException이 발생합니다. 이 경우catch (PortOneApiException e)에 잡히지 않고finally만 실행된 후 예외가 전파되어CANCEL_FAILED상태가 저장되지 않습니다.
saveFailed호출을catch블록 밖으로 이동하거나, catch 범위를Exception으로 넓히는 것을 고려하세요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@legend-momo-city/src/main/java/com/wanted/momocity/payment/application/service/PaymentCommandService.java` around lines 105 - 122, Update the payment failure handling around cancelPayment, payment.markFailed, and paymentStatusUpdater.saveFailed so any exception after cancellation, including saveFailed failures, records CANCEL_FAILED through paymentStatusUpdater.saveCancelFailed before propagating the appropriate error. Preserve the existing PaymentAmountMismatchException for successful cancellation and ensure paymentLockPort.unlock remains in finally.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PaymentLockAdapter.java`:
- Around line 19-30: PaymentLockAdapter의 tryLock과 unlock이 서로 다른 Redis 키를 사용하고
있습니다. 두 메서드에서 동일한 키 형식을 사용하도록 수정해, unlock이 tryLock에서 설정한 락을 삭제하도록 하세요.
---
Outside diff comments:
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/application/service/PaymentCommandService.java`:
- Around line 66-84: Update paymentVerify so every path after paymentPrepare’s
lock acquisition releases the lock, including payment lookup failure, ownership
denial, already-finalized payments, and verifyPayment failures. Wrap the full
flow in try-finally and unlock using the payment owner’s identifier when
available, while safely handling the PaymentNotFoundException path before a
Payment instance exists.
---
Nitpick comments:
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/application/service/PaymentCommandService.java`:
- Around line 105-122: Update the payment failure handling around cancelPayment,
payment.markFailed, and paymentStatusUpdater.saveFailed so any exception after
cancellation, including saveFailed failures, records CANCEL_FAILED through
paymentStatusUpdater.saveCancelFailed before propagating the appropriate error.
Preserve the existing PaymentAmountMismatchException for successful cancellation
and ensure paymentLockPort.unlock remains in finally.
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PortOnePaymentAdapter.java`:
- Around line 70-91: Update cancelPayment in PortOnePaymentAdapter to catch
WebClientRequestException in addition to WebClientResponseException, wrapping
network failures in PortOneApiException so PaymentCommandService can persist the
CANCEL_FAILED state. Preserve the existing response-status and response-body
details for WebClientResponseException.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: aff66789-b7ed-4c66-b9eb-b56a89c3f735
📒 Files selected for processing (11)
legend-momo-city/src/main/java/com/wanted/momocity/payment/application/port/PaymentLockPort.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/application/service/PaymentCommandService.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/application/supporter/PaymentStatusUpdater.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/exception/PaymentAccessDeniedException.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/exception/PaymentExceptionHandler.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/model/Payment.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/domain/repository/PaymentRepository.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PaymentLockAdapter.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PortOnePaymentAdapter.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/persistence/PaymentRepositoryAdapter.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/persistence/SpringDataPaymentRepository.java
💤 Files with no reviewable changes (3)
- legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/persistence/SpringDataPaymentRepository.java
- legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/persistence/PaymentRepositoryAdapter.java
- legend-momo-city/src/main/java/com/wanted/momocity/payment/domain/repository/PaymentRepository.java
🚧 Files skipped from review as they are similar to previous changes (2)
- legend-momo-city/src/main/java/com/wanted/momocity/payment/domain/exception/PaymentExceptionHandler.java
- legend-momo-city/src/main/java/com/wanted/momocity/payment/domain/model/Payment.java
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PortOnePaymentAdapter.java (2)
39-40: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick winPortOne 오류 본문을 클라이언트에 그대로 노출하지 마세요.
Line 39-40 및 Line 81-82에서
getResponseBodyAsString()전체를 예외 메시지에 넣고 있습니다.PaymentExceptionHandler가e.getMessage()를 HTTP 응답에 포함하므로 PortOne의 내부 오류 정보나 식별자가 외부에 노출될 수 있습니다. 원문은 서버 로그에 제한적으로 기록하고, 클라이언트에는 고정된 오류 메시지만 반환하는 편이 안전합니다.Also applies to: 81-82
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PortOnePaymentAdapter.java` around lines 39 - 40, Update both WebClientResponseException catch blocks in PortOnePaymentAdapter to stop including getResponseBodyAsString() in the PortOneApiException message. Log the raw response body only through the server-side logging mechanism with appropriate limits, then throw PortOneApiException with a fixed client-safe message while preserving the existing failure handling.
47-65: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winPortOne 응답 파싱 실패도
PortOneApiException으로 감싸세요
status/pgTxId의 무검증 캐스팅과amount.total변환에서ClassCastException·NumberFormatException이 나면PortOneApiException으로 안 바뀌어 502가 아니라 전역 500으로 떨어집니다. 응답 파싱 전반을 검증하고 실패 시 동일한 예외로 변환하세요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PortOnePaymentAdapter.java` around lines 47 - 65, Update the response parsing around status, pgTxId, and amountMap in PortOnePaymentAdapter so invalid types and amount.total conversion failures are caught and wrapped as PortOneApiException. Validate the required response fields before casting or converting, preserve the existing missing-amount error behavior, and ensure all parsing failures use the same domain exception instead of escaping as ClassCastException or NumberFormatException.legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PaymentLockAdapter.java (1)
18-30: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift락 소유권을 확인한 뒤 해제해야 합니다.
Line 20-22는 모든 락에 동일한 값
"locked"를 저장하고, Line 28-29는 소유권 확인 없이 키를 삭제합니다. 기존 요청의 300초 TTL이 만료된 뒤 다른 요청이 같은 키를 획득하면, 기존 요청의unlock이 새 요청의 락까지 삭제할 수 있습니다. 이로 인해 중복 결제 방지 락이 조기에 해제됩니다.획득 시 고유 토큰을 저장하고, 원자적인 compare-and-delete로 동일 토큰일 때만 삭제하도록 락 포트 계약과 구현을 변경해 주세요. 또는 소유권을 지원하는 검증된 Redis 락 구현을 사용해야 합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PaymentLockAdapter.java` around lines 18 - 30, PaymentLockAdapter의 락 해제가 소유권을 검증하지 않아 다른 요청의 락을 삭제할 수 있습니다. tryLock에서 요청별 고유 토큰을 저장하고 호출자가 해당 토큰을 보존하도록 락 포트 계약을 변경한 뒤, unlock은 Lua 스크립트 등 원자적 compare-and-delete로 동일 토큰일 때만 삭제하도록 구현하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PaymentLockAdapter.java`:
- Around line 18-30: PaymentLockAdapter의 락 해제가 소유권을 검증하지 않아 다른 요청의 락을 삭제할 수
있습니다. tryLock에서 요청별 고유 토큰을 저장하고 호출자가 해당 토큰을 보존하도록 락 포트 계약을 변경한 뒤, unlock은 Lua
스크립트 등 원자적 compare-and-delete로 동일 토큰일 때만 삭제하도록 구현하세요.
In
`@legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PortOnePaymentAdapter.java`:
- Around line 39-40: Update both WebClientResponseException catch blocks in
PortOnePaymentAdapter to stop including getResponseBodyAsString() in the
PortOneApiException message. Log the raw response body only through the
server-side logging mechanism with appropriate limits, then throw
PortOneApiException with a fixed client-safe message while preserving the
existing failure handling.
- Around line 47-65: Update the response parsing around status, pgTxId, and
amountMap in PortOnePaymentAdapter so invalid types and amount.total conversion
failures are caught and wrapped as PortOneApiException. Validate the required
response fields before casting or converting, preserve the existing
missing-amount error behavior, and ensure all parsing failures use the same
domain exception instead of escaping as ClassCastException or
NumberFormatException.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e7a76e00-594c-4a5b-9ad3-d4867fca172b
📒 Files selected for processing (2)
legend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PaymentLockAdapter.javalegend-momo-city/src/main/java/com/wanted/momocity/payment/infrastructure/adapter/PortOnePaymentAdapter.java
작업내용
추후 결제 관련 진행작업
#600
Summary by CodeRabbit