diff --git a/build.gradle b/build.gradle index 75c35b8..ba5ce78 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-webflux' implementation 'org.springframework.boot:spring-boot-starter-validation' // 에러 핸들러 만들 때 씀 testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/src/main/java/com/sumte/payment/controller/PaymentController.java b/src/main/java/com/sumte/payment/controller/PaymentController.java index e709a9e..ed65bc6 100644 --- a/src/main/java/com/sumte/payment/controller/PaymentController.java +++ b/src/main/java/com/sumte/payment/controller/PaymentController.java @@ -1,6 +1,7 @@ package com.sumte.payment.controller; import com.sumte.apiPayload.ApiResponse; +import com.sumte.payment.dto.KakaoPayApproveResponseDTO; import com.sumte.payment.dto.PaymentRequestDTO; import com.sumte.payment.dto.PaymentResponseDTO; import com.sumte.payment.service.PaymentService; @@ -33,11 +34,11 @@ public ResponseEntity> requestP summary = "결제 승인 처리 API", description = "PG사(예: 카카오페이) 결제 완료 후, 해당 결제 ID의 상태를 PAID로 변경합니다." ) - public ResponseEntity> approvePayment( + public ResponseEntity> approvePayment( @PathVariable("id") Long id, @RequestParam("pg_token") String pgToken) { - paymentService.approvePayment(id, pgToken); - return ResponseEntity.ok(ApiResponse.success(null)); + KakaoPayApproveResponseDTO response = paymentService.approvePayment(id, pgToken); + return ResponseEntity.ok(ApiResponse.success(response)); } } diff --git a/src/main/java/com/sumte/payment/converter/PaymentConverter.java b/src/main/java/com/sumte/payment/converter/PaymentConverter.java index acab9b1..9f5ce16 100644 --- a/src/main/java/com/sumte/payment/converter/PaymentConverter.java +++ b/src/main/java/com/sumte/payment/converter/PaymentConverter.java @@ -10,10 +10,14 @@ public class PaymentConverter { public static Payment toEntity(PaymentRequestDTO.CreatePaymentDTO dto, Reservation reservation) { + PaymentMethod method = dto.getPaymentMethod() != null + ? dto.getPaymentMethod() + : PaymentMethod.KAKAOPAY; + return Payment.builder() .reservation(reservation) .paidPrice(dto.getAmount()) - .paymentMethod(PaymentMethod.valueOf(dto.getPaymentMethod())) + .paymentMethod(method) .paymentStatus(PaymentStatus.PENDING) .build(); } diff --git a/src/main/java/com/sumte/payment/dto/KakaoPayApproveRequestDTO.java b/src/main/java/com/sumte/payment/dto/KakaoPayApproveRequestDTO.java new file mode 100644 index 0000000..1ebe6c4 --- /dev/null +++ b/src/main/java/com/sumte/payment/dto/KakaoPayApproveRequestDTO.java @@ -0,0 +1,14 @@ +package com.sumte.payment.dto; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class KakaoPayApproveRequestDTO { + private String cid; + private String tid; + private String partner_order_id; + private String partner_user_id; + private String pg_token; +} diff --git a/src/main/java/com/sumte/payment/dto/KakaoPayApproveResponseDTO.java b/src/main/java/com/sumte/payment/dto/KakaoPayApproveResponseDTO.java new file mode 100644 index 0000000..15da365 --- /dev/null +++ b/src/main/java/com/sumte/payment/dto/KakaoPayApproveResponseDTO.java @@ -0,0 +1,23 @@ +package com.sumte.payment.dto; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class KakaoPayApproveResponseDTO { + private String aid; + private String tid; + private String cid; + private String partner_order_id; + private String partner_user_id; + private String payment_method_type; + private Amount amount; + + @Getter + public static class Amount { + private int total; + private int tax_free; + private int vat; + } +} diff --git a/src/main/java/com/sumte/payment/dto/KakaoPayReadyRequestDTO.java b/src/main/java/com/sumte/payment/dto/KakaoPayReadyRequestDTO.java new file mode 100644 index 0000000..eab11c8 --- /dev/null +++ b/src/main/java/com/sumte/payment/dto/KakaoPayReadyRequestDTO.java @@ -0,0 +1,21 @@ +package com.sumte.payment.dto; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +@Getter +@Builder +public class KakaoPayReadyRequestDTO { + private String cid; + private String partner_order_id; + private String partner_user_id; + private String item_name; + private String quantity; + private String total_amount; + private String tax_free_amount; + private String approval_url; + private String cancel_url; + private String fail_url; +} diff --git a/src/main/java/com/sumte/payment/dto/KakaoPayReadyResponseDTO.java b/src/main/java/com/sumte/payment/dto/KakaoPayReadyResponseDTO.java new file mode 100644 index 0000000..c1806d9 --- /dev/null +++ b/src/main/java/com/sumte/payment/dto/KakaoPayReadyResponseDTO.java @@ -0,0 +1,13 @@ +package com.sumte.payment.dto; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class KakaoPayReadyResponseDTO { + private String tid; + private String next_redirect_app_url; + private String next_redirect_pc_url; + private String created_at; +} diff --git a/src/main/java/com/sumte/payment/dto/PaymentRequestDTO.java b/src/main/java/com/sumte/payment/dto/PaymentRequestDTO.java index 6cdbf8c..833f5d6 100644 --- a/src/main/java/com/sumte/payment/dto/PaymentRequestDTO.java +++ b/src/main/java/com/sumte/payment/dto/PaymentRequestDTO.java @@ -1,5 +1,6 @@ package com.sumte.payment.dto; +import com.sumte.payment.entity.PaymentMethod; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -14,6 +15,6 @@ public class PaymentRequestDTO { public static class CreatePaymentDTO { private Long reservationId; private Long amount; - private String paymentMethod; + private PaymentMethod paymentMethod; } } diff --git a/src/main/java/com/sumte/payment/entity/Payment.java b/src/main/java/com/sumte/payment/entity/Payment.java index d07e8a7..ede8bb1 100644 --- a/src/main/java/com/sumte/payment/entity/Payment.java +++ b/src/main/java/com/sumte/payment/entity/Payment.java @@ -52,4 +52,8 @@ public void markAsFailed() { public void markAsRefunded() { this.paymentStatus = PaymentStatus.REFUNDED; } + + public void setTid(String tid) { + this.tid = tid; + } } diff --git a/src/main/java/com/sumte/payment/kakaopay/KakaoPayClient.java b/src/main/java/com/sumte/payment/kakaopay/KakaoPayClient.java new file mode 100644 index 0000000..8f2d7fa --- /dev/null +++ b/src/main/java/com/sumte/payment/kakaopay/KakaoPayClient.java @@ -0,0 +1,44 @@ +package com.sumte.payment.kakaopay; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.sumte.payment.dto.KakaoPayApproveRequestDTO; +import com.sumte.payment.dto.KakaoPayApproveResponseDTO; +import com.sumte.payment.dto.KakaoPayReadyRequestDTO; +import com.sumte.payment.dto.KakaoPayReadyResponseDTO; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; + +@Component +@RequiredArgsConstructor +public class KakaoPayClient { + + private final WebClient webClient; + private final ObjectMapper objectMapper; + + @Value("${kakao.pay.secret-key}") + private String adminKey; + + public KakaoPayReadyResponseDTO requestPayment(KakaoPayReadyRequestDTO request) { + return webClient.post() + .uri("https://open-api.kakaopay.com/online/v1/payment/ready") + .header("Authorization", "SECRET_KEY " + adminKey) + .header("Content-Type", "application/json") + .bodyValue(request) + .retrieve() + .bodyToMono(KakaoPayReadyResponseDTO.class) + .block(); + } + + public KakaoPayApproveResponseDTO approvePayment(KakaoPayApproveRequestDTO request) { + return webClient.post() + .uri("https://open-api.kakaopay.com/online/v1/payment/approve") + .header("Authorization", "SECRET_KEY " + adminKey) + .header("Content-Type", "application/json") + .bodyValue(request) + .retrieve() + .bodyToMono(KakaoPayApproveResponseDTO.class) + .block(); + } +} diff --git a/src/main/java/com/sumte/payment/kakaopay/WebClientConfig.java b/src/main/java/com/sumte/payment/kakaopay/WebClientConfig.java new file mode 100644 index 0000000..37ae6ec --- /dev/null +++ b/src/main/java/com/sumte/payment/kakaopay/WebClientConfig.java @@ -0,0 +1,13 @@ +package com.sumte.payment.kakaopay; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; + +@Configuration +public class WebClientConfig { + @Bean + public WebClient webClient() { + return WebClient.builder().build(); + } +} diff --git a/src/main/java/com/sumte/payment/service/PaymentService.java b/src/main/java/com/sumte/payment/service/PaymentService.java index 28a9e41..69ee20d 100644 --- a/src/main/java/com/sumte/payment/service/PaymentService.java +++ b/src/main/java/com/sumte/payment/service/PaymentService.java @@ -1,9 +1,10 @@ package com.sumte.payment.service; +import com.sumte.payment.dto.KakaoPayApproveResponseDTO; import com.sumte.payment.dto.PaymentRequestDTO; import com.sumte.payment.dto.PaymentResponseDTO; public interface PaymentService { PaymentResponseDTO.CreatePaymentDTO requestPayment(PaymentRequestDTO.CreatePaymentDTO dto); - void approvePayment(Long paymentId, String pgToken); + KakaoPayApproveResponseDTO approvePayment(Long paymentId, String pgToken); } diff --git a/src/main/java/com/sumte/payment/service/PaymentServiceImpl.java b/src/main/java/com/sumte/payment/service/PaymentServiceImpl.java index 99cd598..645c16f 100644 --- a/src/main/java/com/sumte/payment/service/PaymentServiceImpl.java +++ b/src/main/java/com/sumte/payment/service/PaymentServiceImpl.java @@ -1,61 +1,95 @@ -package com.sumte.payment.service; - -import com.sumte.apiPayload.code.error.PaymentErrorCode; -import com.sumte.apiPayload.exception.SumteException; -import com.sumte.payment.converter.PaymentConverter; -import com.sumte.payment.dto.PaymentRequestDTO; -import com.sumte.payment.dto.PaymentResponseDTO; -import com.sumte.payment.entity.Payment; -import com.sumte.payment.entity.PaymentStatus; -import com.sumte.payment.repository.PaymentRepository; -import com.sumte.reservation.entity.Reservation; -import com.sumte.reservation.repository.ReservationRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@RequiredArgsConstructor -public class PaymentServiceImpl implements PaymentService { - - private final ReservationRepository reservationRepository; - private final PaymentRepository paymentRepository; - private final PaymentTransactionHelper transactionHelper; - - @Override - @Transactional - public PaymentResponseDTO.CreatePaymentDTO requestPayment(PaymentRequestDTO.CreatePaymentDTO dto) { - Reservation reservation = reservationRepository.findById(dto.getReservationId()) - .orElseThrow(() -> new SumteException(PaymentErrorCode.RESERVATION_NOT_FOUND)); - - Payment payment; - try { - payment = PaymentConverter.toEntity(dto, reservation); - } catch (IllegalArgumentException e) { - throw new SumteException(PaymentErrorCode.INVALID_PAYMENT_METHOD); - } - paymentRepository.save(payment); + package com.sumte.payment.service; - String paymentUrl = "https://pay.mock.kakao.com/" + payment.getId(); - return PaymentConverter.toCreateResponse(payment, paymentUrl); - } + import com.sumte.apiPayload.code.error.PaymentErrorCode; + import com.sumte.apiPayload.exception.SumteException; + import com.sumte.payment.converter.PaymentConverter; + import com.sumte.payment.dto.*; + import com.sumte.payment.entity.Payment; + import com.sumte.payment.entity.PaymentStatus; + import com.sumte.payment.kakaopay.KakaoPayClient; + import com.sumte.payment.repository.PaymentRepository; + import com.sumte.reservation.entity.Reservation; + import com.sumte.reservation.repository.ReservationRepository; + import lombok.RequiredArgsConstructor; + import org.springframework.beans.factory.annotation.Value; + import org.springframework.stereotype.Service; + import org.springframework.transaction.annotation.Transactional; - @Override - @Transactional - public void approvePayment(Long paymentId, String pgToken) { - Payment payment = paymentRepository.findById(paymentId) - .orElseThrow(() -> new SumteException(PaymentErrorCode.PAYMENT_NOT_FOUND)); + @Service + @RequiredArgsConstructor + public class PaymentServiceImpl implements PaymentService { - if (payment.getPaymentStatus() == PaymentStatus.PAID) { - throw new SumteException(PaymentErrorCode.ALREADY_APPROVED_PAYMENT); - } + private final ReservationRepository reservationRepository; + private final PaymentRepository paymentRepository; + private final PaymentTransactionHelper transactionHelper; + private final KakaoPayClient kakaoPayClient; + + @Value("${app.host}") + private String appHost; + + @Override + @Transactional + public PaymentResponseDTO.CreatePaymentDTO requestPayment(PaymentRequestDTO.CreatePaymentDTO dto) { + Reservation reservation = reservationRepository.findById(dto.getReservationId()) + .orElseThrow(() -> new SumteException(PaymentErrorCode.RESERVATION_NOT_FOUND)); + + Payment payment; + try { + payment = PaymentConverter.toEntity(dto, reservation); + } catch (IllegalArgumentException e) { + throw new SumteException(PaymentErrorCode.INVALID_PAYMENT_METHOD); + } + paymentRepository.save(payment); + + + String itemName = reservation.getRoom().getName(); + Long totalAmount = reservation.getRoom().getPrice(); + + KakaoPayReadyRequestDTO request = KakaoPayReadyRequestDTO.builder() + .cid("TC0ONETIME") + .partner_order_id("reservation_" + reservation.getId()) + .partner_user_id("user_" + reservation.getUser().getId()) + .item_name(itemName) + .quantity("1") + .total_amount(String.valueOf(totalAmount)) + .tax_free_amount("0") + .approval_url(appHost + "/pay/success") + .cancel_url(appHost + "/pay/cancel") + .fail_url(appHost + "/pay/fail") + .build(); - if (pgToken == null || pgToken.isBlank()) { - transactionHelper.markPaymentFailed(payment); - throw new SumteException(PaymentErrorCode.PG_TOKEN_MISSING); + KakaoPayReadyResponseDTO kakaoResponse = kakaoPayClient.requestPayment(request); + payment.setTid(kakaoResponse.getTid()); + + return PaymentConverter.toCreateResponse(payment, kakaoResponse.getNext_redirect_pc_url()); } - payment.markAsPaid(); - } + @Override + @Transactional + public KakaoPayApproveResponseDTO approvePayment(Long paymentId, String pgToken) { + Payment payment = paymentRepository.findById(paymentId) + .orElseThrow(() -> new SumteException(PaymentErrorCode.PAYMENT_NOT_FOUND)); + + if (payment.getPaymentStatus() == PaymentStatus.PAID) { + throw new SumteException(PaymentErrorCode.ALREADY_APPROVED_PAYMENT); + } + + if (pgToken == null || pgToken.isBlank()) { + transactionHelper.markPaymentFailed(payment); + throw new SumteException(PaymentErrorCode.PG_TOKEN_MISSING); + } -} + KakaoPayApproveRequestDTO request = KakaoPayApproveRequestDTO.builder() + .cid("TC0ONETIME") + .tid(payment.getTid()) + .partner_order_id("reservation_" + payment.getReservation().getId()) + .partner_user_id("user_" + payment.getReservation().getUser().getId()) + .pg_token(pgToken) + .build(); + + KakaoPayApproveResponseDTO response = kakaoPayClient.approvePayment(request); + payment.markAsPaid(); + + return response; + } + } diff --git a/src/main/java/com/sumte/reservation/controller/ReservationController.java b/src/main/java/com/sumte/reservation/controller/ReservationController.java index 1ae8640..5dd678b 100644 --- a/src/main/java/com/sumte/reservation/controller/ReservationController.java +++ b/src/main/java/com/sumte/reservation/controller/ReservationController.java @@ -41,43 +41,38 @@ public class ReservationController { @PostMapping @Operation(summary = "예약 생성 API", description = "요청 본문으로 객실 ID, 투숙 인원, 날짜 정보를 입력받고, 헤더의 userId를 통해 예약을 생성합니다.") public ResponseEntity> createReservation( - @Parameter(description = "요청한 사용자 ID") @UserId Long userId, - @Valid @RequestBody ReservationRequestDTO.CreateReservationDTO request + @Valid @RequestBody ReservationRequestDTO.CreateReservationDTO request ) { - reservationService.createReservation(request, userId); + reservationService.createReservation(request); return ResponseEntity.ok(ApiResponse.success(null)); } @GetMapping("/my") @Operation(summary = "내 예약 목록 조회 API", description = "내가 예약한 숙소 목록을 페이지 단위로 조회합니다.") public ResponseEntity>> getMyReservations( - @Parameter(description = "요청한 사용자 ID") @UserId Long userId, - @CheckPage @RequestParam(name = "page", defaultValue = "1") int page, - @CheckPageSize @RequestParam(name = "size", defaultValue = "10") int size + @CheckPage @RequestParam(name = "page", defaultValue = "1") int page, + @CheckPageSize @RequestParam(name = "size", defaultValue = "10") int size ) { Pageable pageable = PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "startDate")); - Page result = reservationService.getMyReservations(userId, pageable); + Page result = reservationService.getMyReservations(pageable); return ResponseEntity.ok(ApiResponse.success(result)); } @GetMapping("/{id}") @Operation(summary = "예약 상세 조회 API", description = "예약 ID를 기준으로 상세 정보를 조회합니다.") public ResponseEntity> getReservationDetail( - @Parameter(description = "요청한 사용자 ID") @UserId Long userId, - @PathVariable("id") Long reservationId + @PathVariable("id") Long reservationId ) { - ReservationResponseDTO.ReservationDetailDTO result = reservationService.getReservationDetail(reservationId, - userId); + ReservationResponseDTO.ReservationDetailDTO result = reservationService.getReservationDetail(reservationId); return ResponseEntity.ok(ApiResponse.success(result)); } @PatchMapping("/{id}") @Operation(summary = "예약 취소 API", description = "예약 ID를 기준으로 사용자의 예약을 취소합니다.") public ResponseEntity> cancelReservation( - @Parameter(description = "요청한 사용자 ID") @UserId Long userId, - @PathVariable("id") Long reservationId + @PathVariable("id") Long reservationId ) { - reservationService.cancelReservation(reservationId, userId); + reservationService.cancelReservation(reservationId); return ResponseEntity.ok(ApiResponse.success(null)); } } diff --git a/src/main/java/com/sumte/reservation/service/ReservationService.java b/src/main/java/com/sumte/reservation/service/ReservationService.java index e9b661a..08742c5 100644 --- a/src/main/java/com/sumte/reservation/service/ReservationService.java +++ b/src/main/java/com/sumte/reservation/service/ReservationService.java @@ -6,9 +6,9 @@ import org.springframework.data.domain.Pageable; public interface ReservationService { - ReservationResponseDTO.CreateReservationDTO createReservation(ReservationRequestDTO.CreateReservationDTO request,Long UserId); - Page getMyReservations(Long userId, Pageable pageable); - ReservationResponseDTO.ReservationDetailDTO getReservationDetail(Long reservationId, Long userId); - void cancelReservation(Long reservationId, Long userId); - void updateCompletedReservations(); + ReservationResponseDTO.CreateReservationDTO createReservation(ReservationRequestDTO.CreateReservationDTO request); + Page getMyReservations(Pageable pageable); + ReservationResponseDTO.ReservationDetailDTO getReservationDetail(Long reservationId); + void cancelReservation(Long reservationId); + void updateCompletedReservations(); } diff --git a/src/main/java/com/sumte/reservation/service/ReservationServiceImpl.java b/src/main/java/com/sumte/reservation/service/ReservationServiceImpl.java index e301ae0..fd060ac 100644 --- a/src/main/java/com/sumte/reservation/service/ReservationServiceImpl.java +++ b/src/main/java/com/sumte/reservation/service/ReservationServiceImpl.java @@ -1,29 +1,28 @@ package com.sumte.reservation.service; -import java.time.LocalDate; -import java.time.LocalTime; -import java.util.List; -import java.util.Optional; +import com.sumte.apiPayload.code.error.CommonErrorCode; +import com.sumte.apiPayload.code.error.ReservationErrorCode; +import com.sumte.apiPayload.exception.SumteException; +import com.sumte.payment.entity.Payment; +import com.sumte.payment.entity.PaymentStatus; +import com.sumte.reservation.entity.ReservationStatus; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import com.sumte.apiPayload.code.error.CommonErrorCode; -import com.sumte.apiPayload.code.error.ReservationErrorCode; -import com.sumte.apiPayload.exception.SumteException; import com.sumte.image.entity.Image; import com.sumte.image.entity.OwnerType; import com.sumte.image.repository.ImageRepository; -import com.sumte.payment.entity.Payment; -import com.sumte.payment.entity.PaymentStatus; import com.sumte.payment.repository.PaymentRepository; import com.sumte.reservation.converter.ReservationConverter; import com.sumte.reservation.dto.ReservationRequestDTO; import com.sumte.reservation.dto.ReservationResponseDTO; import com.sumte.reservation.entity.Reservation; -import com.sumte.reservation.entity.ReservationStatus; import com.sumte.reservation.repository.ReservationRepository; import com.sumte.review.repository.ReviewRepository; import com.sumte.room.entity.Room; @@ -33,8 +32,14 @@ import lombok.RequiredArgsConstructor; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; +import java.util.Optional; + @Service @RequiredArgsConstructor +@Slf4j public class ReservationServiceImpl implements ReservationService { private final ReservationRepository reservationRepository; @@ -47,10 +52,11 @@ public class ReservationServiceImpl implements ReservationService { @Override @Transactional - public ReservationResponseDTO.CreateReservationDTO createReservation( - ReservationRequestDTO.CreateReservationDTO request, Long userId) { + public ReservationResponseDTO.CreateReservationDTO createReservation(ReservationRequestDTO.CreateReservationDTO request) { + Long userId = currentUserId(); User user = userRepository.findById(userId) - .orElseThrow(() -> new SumteException(CommonErrorCode.USER_NOT_FOUND)); + .orElseThrow(() -> new SumteException(CommonErrorCode.USER_NOT_FOUND)); + Room room = roomRepository.findById(request.getRoomId()) .orElseThrow(() -> new SumteException(ReservationErrorCode.ROOM_NOT_FOUND)); @@ -78,7 +84,8 @@ public ReservationResponseDTO.CreateReservationDTO createReservation( @Override @Transactional(readOnly = true) - public Page getMyReservations(Long userId, Pageable pageable) { + public Page getMyReservations(Pageable pageable) { + Long userId = currentUserId(); User user = userRepository.findById(userId) .orElseThrow(() -> new SumteException(CommonErrorCode.USER_NOT_FOUND)); @@ -107,38 +114,28 @@ public Page getMyReservations(Long user @Override @Transactional(readOnly = true) - public ReservationResponseDTO.ReservationDetailDTO getReservationDetail(Long reservationId, Long userId) { + public ReservationResponseDTO.ReservationDetailDTO getReservationDetail(Long reservationId) { Reservation reservation = reservationRepository.findById(reservationId) .orElseThrow(() -> new SumteException(ReservationErrorCode.RESERVATION_NOT_FOUND)); - if (!reservation.getUser().getId().equals(userId)) { - throw new SumteException(CommonErrorCode.FORBIDDEN); - } - - // 첫 번째 방 이미지 URL 조회 String firstImageUrl = imageRepository - .findByOwnerTypeAndOwnerIdOrderBySortOrderAsc( - OwnerType.ROOM, - reservation.getRoom().getId() - ) - .stream() - .map(Image::getUrl) - .findFirst() - .orElse(null); - + .findByOwnerTypeAndOwnerIdOrderBySortOrderAsc( + OwnerType.ROOM, + reservation.getRoom().getId() + ) + .stream() + .map(Image::getUrl) + .findFirst() + .orElse(null); + return reservationConverter.toReservationDetailDTO(reservation, firstImageUrl); } @Override @Transactional - public void cancelReservation(Long reservationId, Long userId) { + public void cancelReservation(Long reservationId) { Reservation reservation = reservationRepository.findById(reservationId) - .orElseThrow(() -> new SumteException(ReservationErrorCode.RESERVATION_NOT_FOUND)); - - // 사용자 본인 확인 - if (!reservation.getUser().getId().equals(userId)) { - throw new SumteException(CommonErrorCode.FORBIDDEN); - } + .orElseThrow(() -> new SumteException(ReservationErrorCode.RESERVATION_NOT_FOUND)); // 이미 취소된 예약인지 확인 if (reservation.getReservationStatus() == ReservationStatus.CANCELED) { throw new SumteException(ReservationErrorCode.ALREADY_CANCELED); @@ -146,6 +143,16 @@ public void cancelReservation(Long reservationId, Long userId) { reservation.cancel(); } + private Long currentUserId() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + + Object details = auth.getDetails(); + if (details instanceof Long id) { + return id; + } + throw new IllegalStateException("인증 사용자 정보를 확인할 수 없습니다."); + } + @Override @Transactional public void updateCompletedReservations() { @@ -174,5 +181,4 @@ public void updateCompletedReservations() { } } } - } diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 54c594a..a7d4d2d 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -37,4 +37,11 @@ management: endpoints: web: exposure: - include: health \ No newline at end of file + include: health + +app: + host: ${EC2_HOST} + +kakao: + pay: + secret-key: ${KAKAOPAY_SECRET_KEY} \ No newline at end of file