Skip to content

Conversation

@youngw0130
Copy link

@youngw0130 youngw0130 commented Dec 2, 2025

PaymentRepositoryPort를 application 계층으로 이동
PaymentRepositoryAdapter에 QueryDSL을 도입
PaymentCommandService의 트랜잭션 범위를 메서드 단위로 최적화

- 결제(Payment) 도메인 엔티티 추가
- 결제 상태 관리 (대기, 완료, 취소, 실패)
- 환불 처리 및 환불 가능 금액 관리
- 7일 환불 제한 기능
- 본인인증 대기(PendingVerification) 도메인 엔티티 추가
- 인증 상태 관리 (대기, 완료, 실패, 만료)
- 인증 코드 관리 및 만료 시간 관리
- Payment DTO 추가 (PaymentConfirmResponse, PaymentHistoryResponse, RefundRequest, RefundResponse, WebhookResult)
- PaymentServicePort 인터페이스 추가
- PaymentRepositoryPort 인터페이스 추가
- PortOnePort 인터페이스 추가 (PortOne API 연동)
- PaymentApplicationService 비즈니스 로직 구현
- PortOne API를 통한 결제 확인
- 환불 처리 (7일 제한)
- 결제 이력 조회
- Verification DTO 추가 (CreateVerificationRequest, VerificationResult, VerificationStatusResponse)
- VerificationServicePort 인터페이스 추가
- VerificationRepositoryPort 인터페이스 추가
- VerificationApplicationService 비즈니스 로직 구현
- 본인인증 대기/확인 시스템
- 인증 코드 생성 및 검증
- PaymentJpaRepository JPA 쿼리 메서드 추가
- VerificationJpaRepository JPA 쿼리 메서드 추가
- PaymentRepositoryAdapter 포트 구현
- VerificationRepositoryAdapter 포트 구현
- Hexagonal Architecture 준수
- PortOneAdapter PortOne API 연동 구현
- WebhookVerificationService 웹훅 검증 서비스 구현
- WebClientConfig WebClient 설정 추가
- HMAC-SHA256 서명 검증
- PaymentController API 엔드포인트 추가
- VerificationController API 엔드포인트 추가
- 결제 확인, 환불, 결제 이력 조회 API 제공
- 본인인증 대기/확인 API 제공
- Payment 관련 예외 추가 (PaymentNotFoundException, PaymentProcessingException, PaymentVerificationException, RefundValidationException, RefundProcessingException)
- Verification 관련 예외 추가 (VerificationNotFoundException, VerificationProcessingException, VerificationValidationException)
- PortOne API 관련 예외 추가 (PortOneApiException, WebhookVerificationException)
- InvalidUserException 추가
- PaymentExceptionHandler 추가
- ProductMapper 수정
- ProductServiceImpl 수정
- ProductNotSynchronizedException 수정
- ProductRepository 수정
- build.gradle.kts: WebClient 의존성 추가
- application.yaml: PortOne API 설정 추가
- ProductServiceAdapter.kt: 예외 처리 방식 통일
- ProductNotSynchronizedException.kt: 생성자 파라미터 제거
- application.yaml: 두 버전 병합 (server context-path, portone, payment, verification, jwt 설정 통합)
- global/exception → application/payment/exception으로 이동
- 모든 Exception에 @ResponseStatus 적용
- PaymentNotFoundException, PaymentProcessingException 등 7개 Exception 이동
- application/{domain}/port/out → repository/{domain}으로 이동
- PaymentRepositoryPort, UserRepositoryPort, ProductRepositoryPort, VerificationRepositoryPort 이동
- Repository 인터페이스를 별도 계층으로 분리
- PaymentApplicationService를 Command와 Query로 분리
- PaymentCommandService: 결제 확인, 환불, 웹훅 처리
- PaymentQueryService: 결제 조회, 내역 조회
- PaymentCommandPort, PaymentQueryPort 인터페이스 분리
- WebClient → @FeignClient로 변경
- PortOneClient 인터페이스 생성
- PortOneFeignConfig Bean Config 추가
- PortOneAdapter에서 FeignClient 사용
- ResponseEntity → @ResponseStatus로 변경
- 모든 메서드에 @ResponseStatus 적용
- Exception의 @ResponseStatus로 자동 응답 처리
- Spring Cloud BOM 추가 (2024.0.0)
- spring-cloud-starter-openfeign 의존성 추가
- @EnableFeignClients 어노테이션 추가
- 모든 Repository import 경로를 repository 계층으로 수정
- 함수 depth 2 이하로 개선 (handlePaymentPaid)
- 불필요한 import 제거
- 사용하지 않는 파라미터 활용
- main 브랜치의 domain 중심 구조 변경사항 반영
- Auth, Product(Item), User 도메인 구조 변경 수용
- Payment 관련 변경사항은 toss 브랜치의 변경사항 유지
- 충돌 해결: 삭제된 파일 제거 및 AuthDetailsService 수정
- PaymentApplicationService 삭제 (PaymentCommandService, PaymentQueryService로 분리 완료)
- PaymentServicePort 삭제 (PaymentCommandPort, PaymentQueryPort로 분리 완료)
- PaymentHistoryResponse의 사용하지 않는 message 파라미터 제거
- application.yaml에서 portone.api.base-url을 환경 변수로 변경 (PORTONE_BASE_URL)
- OccountApplication에서 @EnableFeignClients 제거 (FeignConfig에서 관리)
- Bean 설정은 별도 @configuration 클래스에서 관리하도록 정리
- Payment 엔티티 필드를 private으로 변경하고 getter 제공
- 컨트롤러를 CQRS 패턴으로 분리 (PaymentCommandController, PaymentQueryController)
- DTO 중첩 클래스를 별도 파일로 분리
- 예외 클래스 @ResponseStatus 어노테이션 제거
- 모든 예외/로그/Validation 메시지 한국어 통일
- RequestMapping 경로 /payment로 변경
- web-flux 의존성 제거
@coderabbitai
Copy link

coderabbitai bot commented Dec 2, 2025

Walkthrough

This PR introduces payment and verification features using hexagonal architecture. It adds domain entities (Payment, PendingVerification), application services for payment commands/queries and verification workflows, infrastructure adapters for PortOne API integration via Feign, comprehensive exception handling, and REST controllers. Configuration is extended with payment/verification-specific properties and dependency management for Spring Cloud.

Changes

Cohort / File(s) Summary
Build & Configuration
build.gradle.kts, .gitignore
Added Spring Cloud BOM import (2024.0.0), OpenFeign and Jackson Kotlin module dependencies; added .env ignore rule under VS Code section
Payment Domain
src/main/kotlin/bssm/devcoop/occount/domain/payment/Payment.kt, src/main/kotlin/bssm/devcoop/occount/domain/payment/repository/PaymentRepositoryPort.kt
New Payment JPA entity with lifecycle methods (isPaid, isRefundable, canRefund, updateStatus, validateForRefund); PaymentStatus and PaymentMethod enums; PaymentRepositoryPort interface with save, find, and paginated query operations
Payment DTOs
src/main/kotlin/bssm/devcoop/occount/application/payment/dto/*
Created data transfer objects: PaymentAmount, PaymentConfirmResponse, PaymentCustomer, PaymentHistoryItem, PaymentHistoryResponse, PaymentMethodDto, RefundAccount, RefundRequest, RefundResponse, WebhookResult; PaymentConfirmRequest for presentation layer
Payment Application Ports & Services
src/main/kotlin/bssm/devcoop/occount/application/payment/command/port/in/PaymentCommandPort.kt, src/main/kotlin/bssm/devcoop/occount/application/payment/command/PaymentCommandService.kt, src/main/kotlin/bssm/devcoop/occount/application/payment/query/port/in/PaymentQueryPort.kt, src/main/kotlin/bssm/devcoop/occount/application/payment/query/PaymentQueryService.kt
Defined PaymentCommandPort and PaymentQueryPort interfaces; implemented PaymentCommandService with confirmPayment, processRefund, and processWebhook operations; implemented PaymentQueryService with getPaymentById and getPaymentHistory operations
Payment PortOne Integration
src/main/kotlin/bssm/devcoop/occount/infrastructure/adapter/out/portone/PortOneClient.kt, src/main/kotlin/bssm/devcoop/occount/infrastructure/adapter/out/portone/PortOneAdapter.kt, src/main/kotlin/bssm/devcoop/occount/infrastructure/adapter/out/portone/PortOneFeignConfig.kt, src/main/kotlin/bssm/devcoop/occount/infrastructure/adapter/out/portone/WebhookVerificationService.kt
Created Feign client for PortOne API (getPayment, cancelPayment endpoints); PortOneAdapter implementing PortOnePort; Feign configuration with Content-Type and timeout settings; WebhookVerificationService with HMAC-SHA256 verification and timestamp validation
Payment Persistence & Ports
src/main/kotlin/bssm/devcoop/occount/infrastructure/repository/PaymentJpaRepository.kt, src/main/kotlin/bssm/devcoop/occount/infrastructure/adapter/out/payment/PaymentRepositoryAdapter.kt, src/main/kotlin/bssm/devcoop/occount/application/payment/port/out/PortOnePort.kt
Created PaymentJpaRepository with query methods; PaymentRepositoryAdapter with QueryDSL-based paginated queries; PortOnePort interface defining confirmPayment, processRefund, verifyWebhook
Payment Exception Handlers
src/main/kotlin/bssm/devcoop/occount/application/payment/exception/*, src/main/kotlin/bssm/devcoop/occount/global/exception/Payment*.kt, src/main/kotlin/bssm/devcoop/occount/global/exception/PortOneApiException.kt, src/main/kotlin/bssm/devcoop/occount/global/exception/Refund*.kt, src/main/kotlin/bssm/devcoop/occount/global/exception/WebhookVerificationException.kt
Added exception types in application and global layers: PaymentNotFoundException, PaymentProcessingException, PaymentVerificationException, PortOneApiException, RefundProcessingException, RefundValidationException, WebhookVerificationException; all global layer exceptions annotated with @ResponseStatus for HTTP mapping
Payment Controllers
src/main/kotlin/bssm/devcoop/occount/presentation/payment/PaymentCommandController.kt, src/main/kotlin/bssm/devcoop/occount/presentation/payment/PaymentQueryController.kt, src/main/kotlin/bssm/devcoop/occount/presentation/payment/dto/PaymentConfirmRequest.kt
Added PaymentCommandController with POST /payment/confirm, /payment/refund, /payment/webhook endpoints; PaymentQueryController with GET /payment/history endpoint with pagination; PaymentConfirmRequest DTO with validation
Verification Domain
src/main/kotlin/bssm/devcoop/occount/domain/verification/PendingVerification.kt
New PendingVerification JPA entity with embedded UserInfo; VerificationStatus enum (PENDING, VERIFIED, EXPIRED, FAILED); lifecycle methods (isExpired, isVerified, markAsVerified, markAsFailed, markAsExpired); 5-minute expiration default
Verification DTOs
src/main/kotlin/bssm/devcoop/occount/application/verification/dto/*, src/main/kotlin/bssm/devcoop/occount/presentation/verification/dto/*
Application layer: CreateVerificationRequest (with nested UserInfo), VerificationResult enum, VerificationStatusResponse; Presentation layer: CreateVerificationRequest, ConfirmVerificationRequest with validation and mapping to application DTOs
Verification Application Ports & Services
src/main/kotlin/bssm/devcoop/occount/application/verification/port/in/VerificationServicePort.kt, src/main/kotlin/bssm/devcoop/occount/application/verification/VerificationApplicationService.kt, src/main/kotlin/bssm/devcoop/occount/repository/verification/VerificationRepositoryPort.kt
Defined VerificationServicePort interface; implemented VerificationApplicationService with createPendingVerification, confirmVerification, getVerificationStatus; VerificationRepositoryPort interface with CRUD and expiration cleanup
Verification Persistence & Infrastructure
src/main/kotlin/bssm/devcoop/occount/infrastructure/repository/VerificationJpaRepository.kt, src/main/kotlin/bssm/devcoop/occount/infrastructure/adapter/out/verification/VerificationRepositoryAdapter.kt
Created VerificationJpaRepository with custom JPQL query for deleting expired verifications; VerificationRepositoryAdapter implementing VerificationRepositoryPort
Verification Exception & Controller
src/main/kotlin/bssm/devcoop/occount/global/exception/Verification*.kt, src/main/kotlin/bssm/devcoop/occount/presentation/verification/VerificationController.kt
Added VerificationNotFoundException, VerificationProcessingException, VerificationValidationException with HTTP status mappings; VerificationController with POST /api/v1/verification/create and /confirm, GET /status/{verificationId} endpoints
Global Configuration & Error Handling
src/main/kotlin/bssm/devcoop/occount/global/config/WebClientConfig.kt, src/main/kotlin/bssm/devcoop/occount/global/error/ErrorMessage.kt, src/main/kotlin/bssm/devcoop/occount/global/exception/PaymentExceptionHandler.kt, src/main/kotlin/bssm/devcoop/occount/global/exception/InvalidUserException.kt
Added WebClient bean configuration with base URL and timeout; extended ErrorMessage enum with payment/verification error codes and Korean messages; created centralized PaymentExceptionHandler with @ExceptionHandler methods for domain exceptions; added InvalidUserException with BAD_REQUEST status
Application Configuration
src/main/resources/application.yaml
Added portone (api.key/base-url/timeout, webhook.secret/tolerance-seconds), payment (refund.max-period-days, webhook.timeout, validation.*), verification (timeout, max-retries) configuration sections; extended jwt section with prefix and header keys
Minor Updates
src/main/kotlin/bssm/devcoop/occount/global/security/auth/AuthDetailsService.kt
Removed blank line between imports and @Service annotation (formatting only)

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant PaymentCmd as PaymentCommandController
    participant PaymentService as PaymentCommandService
    participant PortOneAdapter as PortOneAdapter
    participant PortOne as PortOne API
    participant WebhookVerifier as WebhookVerificationService
    participant Repo as PaymentRepository

    rect rgb(200, 220, 255)
    note over Client,Repo: Payment Confirmation Flow
    Client->>PaymentCmd: POST /payment/confirm (paymentId, userEmail)
    PaymentCmd->>PaymentService: confirmPayment(paymentId, userEmail)
    PaymentService->>PortOneAdapter: confirmPayment(paymentId)
    PortOneAdapter->>PortOne: GET /payments/{paymentId}
    PortOne-->>PortOneAdapter: PaymentConfirmResponse
    PaymentService->>PaymentService: validateEmail(userEmail)
    PaymentService-->>PaymentCmd: PaymentConfirmResponse
    PaymentCmd-->>Client: 200 PaymentConfirmResponse
    end

    rect rgb(220, 255, 200)
    note over Client,Repo: Webhook Processing Flow
    Client->>PaymentCmd: POST /payment/webhook (payload, headers)
    PaymentCmd->>PaymentService: processWebhook(payload, headers)
    PaymentService->>WebhookVerifier: verify(payload, signature, timestamp)
    WebhookVerifier-->>PaymentService: Boolean
    alt Verification Failed
        PaymentService-->>PaymentCmd: FAILED
    else Verification Success
        PaymentService->>PaymentService: parseEvent(payload)
        PaymentService->>PaymentService: handlePaymentPaid/Refund/Cancelled()
        PaymentService->>Repo: save(updatedPayment)
        PaymentService-->>PaymentCmd: SUCCESS
    end
    PaymentCmd-->>Client: 200 WebhookResult
    end

    rect rgb(255, 220, 200)
    note over Client,Repo: Refund Processing Flow
    Client->>PaymentCmd: POST /payment/refund (refundRequest, userEmail)
    PaymentCmd->>PaymentService: processRefund(refundRequest, userEmail)
    PaymentService->>Repo: findByPaymentId(paymentId)
    Repo-->>PaymentService: Payment
    PaymentService->>PaymentService: validateRefund(payment, userEmail)
    PaymentService->>PortOneAdapter: processRefund(paymentId, reason)
    PortOneAdapter->>PortOne: POST /payments/{paymentId}/cancel
    PortOne-->>PortOneAdapter: RefundResponse
    PaymentService->>Repo: save(refundedPayment)
    PaymentService-->>PaymentCmd: RefundResponse
    PaymentCmd-->>Client: 200 RefundResponse
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • PaymentCommandService: Complex webhook parsing, multi-branch event handling, and refund eligibility logic with extensive validation
  • PortOne API Integration: Feign client configuration and error handling; HTTP integration correctness must be verified
  • WebhookVerificationService: HMAC-SHA256 signature generation and timestamp validation; security-critical code requiring careful review
  • PaymentRepositoryAdapter: QueryDSL-based pagination implementation with manual Page construction; verify query correctness
  • Exception hierarchy: Dual exception definitions (application and global layers) with HTTP status mappings; ensure consistency
  • Configuration schema: Multiple new property keys and nested structures; verify alignment with property injection in services

Poem

🐰 Payment workflows hop into place,
Webhooks dance with verification grace,
PortOne calls bridge the API space,
Refunds flow at a swift pace,
Domain and ports interlace! 💳✨


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Free

📥 Commits

Reviewing files that changed from the base of the PR and between 157ad12 and e56cc24.

📒 Files selected for processing (4)
  • src/main/kotlin/bssm/devcoop/occount/application/payment/command/PaymentCommandService.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/application/payment/query/PaymentQueryService.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/domain/payment/repository/PaymentRepositoryPort.kt (1 hunks)
  • src/main/kotlin/bssm/devcoop/occount/infrastructure/adapter/out/payment/PaymentRepositoryAdapter.kt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/kotlin/bssm/devcoop/occount/application/payment/query/PaymentQueryService.kt

Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants