package roomescape.payment.business import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import roomescape.payment.exception.PaymentErrorCode import roomescape.payment.exception.PaymentException import roomescape.payment.infrastructure.client.PaymentApproveResponse import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository import roomescape.payment.infrastructure.persistence.PaymentEntity import roomescape.payment.infrastructure.persistence.PaymentRepository import roomescape.payment.web.PaymentCancelRequest import roomescape.payment.web.PaymentCancelResponse import roomescape.payment.web.PaymentCreateResponse import roomescape.payment.web.toCreateResponse import roomescape.reservation.infrastructure.persistence.ReservationEntity import java.time.OffsetDateTime private val log = KotlinLogging.logger {} @Service class PaymentService( private val paymentRepository: PaymentRepository, private val canceledPaymentRepository: CanceledPaymentRepository, ) { @Transactional fun createPayment( approveResponse: PaymentApproveResponse, reservation: ReservationEntity, ): PaymentCreateResponse { log.debug { "[PaymentService.createPayment] 결제 정보 저장 시작: request=$approveResponse, reservationId=${reservation.id}" } val payment = PaymentEntity( orderId = approveResponse.orderId, paymentKey = approveResponse.paymentKey, totalAmount = approveResponse.totalAmount, reservation = reservation, approvedAt = approveResponse.approvedAt ) return paymentRepository.save(payment) .toCreateResponse() .also { log.info { "[PaymentService.createPayment] 결제 정보 저장 완료: paymentId=${it.id}, reservationId=${reservation.id}" } } } @Transactional(readOnly = true) fun isReservationPaid(reservationId: Long): Boolean { log.debug { "[PaymentService.isReservationPaid] 예약 결제 여부 확인 시작: reservationId=$reservationId" } return paymentRepository.existsByReservationId(reservationId) .also { log.info { "[PaymentService.isReservationPaid] 예약 결제 여부 확인 완료: reservationId=$reservationId, isPaid=$it" } } } @Transactional fun createCanceledPayment( cancelInfo: PaymentCancelResponse, approvedAt: OffsetDateTime, paymentKey: String, ): CanceledPaymentEntity { log.debug { "[PaymentService.createCanceledPayment] 결제 취소 정보 저장 시작: paymentKey=$paymentKey" + ", cancelInfo=$cancelInfo" } val canceledPayment = CanceledPaymentEntity( paymentKey = paymentKey, cancelReason = cancelInfo.cancelReason, cancelAmount = cancelInfo.cancelAmount, approvedAt = approvedAt, canceledAt = cancelInfo.canceledAt ) return canceledPaymentRepository.save(canceledPayment) .also { log.info { "[PaymentService.createCanceledPayment] 결제 취소 정보 생성 완료: canceledPaymentId=${it.id}" + ", paymentKey=${paymentKey}, amount=${cancelInfo.cancelAmount}, canceledAt=${it.canceledAt}" } } } @Transactional fun createCanceledPaymentByReservationId(reservationId: Long): PaymentCancelRequest { log.debug { "[PaymentService.createCanceledPaymentByReservationId] 예약 삭제 & 결제 취소 정보 저장 시작: reservationId=$reservationId" } val paymentKey: String = paymentRepository.findPaymentKeyByReservationId(reservationId) ?: run { log.warn { "[PaymentService.createCanceledPaymentByReservationId] 예약 조회 실패: reservationId=$reservationId" } throw PaymentException(PaymentErrorCode.PAYMENT_NOT_FOUND) } val canceled: CanceledPaymentEntity = cancelPayment(paymentKey) return PaymentCancelRequest(paymentKey, canceled.cancelAmount, canceled.cancelReason) .also { log.info { "[PaymentService.createCanceledPaymentByReservationId] 예약 ID로 결제 취소 완료: reservationId=$reservationId" } } } private fun cancelPayment( paymentKey: String, cancelReason: String = "고객 요청", canceledAt: OffsetDateTime = OffsetDateTime.now(), ): CanceledPaymentEntity { log.debug { "[PaymentService.cancelPayment] 결제 취소 정보 저장 시작: paymentKey=$paymentKey" } val payment: PaymentEntity = paymentRepository.findByPaymentKey(paymentKey) ?.also { paymentRepository.delete(it) log.info { "[PaymentService.cancelPayment] 결제 정보 삭제 완료: paymentId=${it.id}, paymentKey=$paymentKey" } } ?: run { log.warn { "[PaymentService.cancelPayment] 결제 정보 조회 실패: paymentKey=$paymentKey" } throw PaymentException(PaymentErrorCode.PAYMENT_NOT_FOUND) } val canceledPayment = CanceledPaymentEntity( paymentKey = paymentKey, cancelReason = cancelReason, cancelAmount = payment.totalAmount, approvedAt = payment.approvedAt, canceledAt = canceledAt ) return canceledPaymentRepository.save(canceledPayment) .also { log.info { "[PaymentService.cancelPayment] 결제 취소 정보 저장 완료: canceledPaymentId=${it.id}" } } } @Transactional fun updateCanceledTime( paymentKey: String, canceledAt: OffsetDateTime, ) { log.debug { "[PaymentService.updateCanceledTime] 취소 시간 업데이트 시작: paymentKey=$paymentKey, canceledAt=$canceledAt" } canceledPaymentRepository.findByPaymentKey(paymentKey) ?.apply { this.canceledAt = canceledAt } ?.also { log.info { "[PaymentService.updateCanceledTime] 취소 시간 업데이트 완료: paymentKey=$paymentKey" + ", canceledAt=$canceledAt" } } ?: run { log.warn { "[PaymentService.updateCanceledTime] 결제 정보 조회 실패: paymentKey=$paymentKey" } throw PaymentException(PaymentErrorCode.PAYMENT_NOT_FOUND) } } }