[#41] 예약 스키마 재정의 #42

Merged
pricelees merged 41 commits from refactor/#41 into main 2025-09-09 00:43:39 +00:00
Showing only changes of commit 7f4af4770d - Show all commits

View File

@ -1,99 +1,112 @@
package roomescape.payment.business
import io.github.oshai.kotlinlogging.KLogger
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import roomescape.payment.implement.PaymentFinder
import roomescape.payment.implement.PaymentWriter
import roomescape.payment.infrastructure.client.PaymentApproveResponse
import roomescape.common.util.TransactionExecutionUtil
import roomescape.payment.exception.PaymentErrorCode
import roomescape.payment.exception.PaymentException
import roomescape.payment.infrastructure.client.PaymentClientCancelResponse
import roomescape.payment.infrastructure.client.PaymentClientConfirmResponse
import roomescape.payment.infrastructure.client.TosspaymentClient
import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity
import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository
import roomescape.payment.infrastructure.persistence.PaymentDetailEntity
import roomescape.payment.infrastructure.persistence.PaymentDetailRepository
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.PaymentConfirmRequest
import roomescape.payment.web.PaymentCreateResponse
import roomescape.payment.web.toCreateResponse
import roomescape.reservation.infrastructure.persistence.ReservationEntity
import java.time.OffsetDateTime
import roomescape.payment.web.PaymentRetrieveResponse
import roomescape.payment.web.toCancelDetailResponse
import roomescape.payment.web.toPaymentDetailResponse
import roomescape.payment.web.toRetrieveResponse
private val log = KotlinLogging.logger {}
private val log: KLogger = KotlinLogging.logger {}
@Service
class PaymentService(
private val paymentFinder: PaymentFinder,
private val paymentWriter: PaymentWriter
private val paymentClient: TosspaymentClient,
private val paymentRepository: PaymentRepository,
private val paymentDetailRepository: PaymentDetailRepository,
private val canceledPaymentRepository: CanceledPaymentRepository,
private val paymentWriter: PaymentWriter,
private val transactionExecutionUtil: TransactionExecutionUtil,
) {
@Transactional(readOnly = true)
fun existsByReservationId(reservationId: Long): Boolean {
log.debug { "[PaymentService.existsByReservationId] 시작: reservationId=$reservationId" }
return paymentFinder.existsPaymentByReservationId(reservationId)
.also { log.info { "[PaymentService.existsByReservationId] 완료: reservationId=$reservationId, isPaid=$it" } }
}
@Transactional
fun createPayment(
approvedPaymentInfo: PaymentApproveResponse,
reservation: ReservationEntity,
): PaymentCreateResponse {
log.debug { "[PaymentService.createPayment] 시작: paymentKey=${approvedPaymentInfo.paymentKey}, reservationId=${reservation.id}" }
val created: PaymentEntity = paymentWriter.create(
paymentKey = approvedPaymentInfo.paymentKey,
orderId = approvedPaymentInfo.orderId,
totalAmount = approvedPaymentInfo.totalAmount,
approvedAt = approvedPaymentInfo.approvedAt,
reservation = reservation
fun confirm(reservationId: Long, request: PaymentConfirmRequest): PaymentCreateResponse {
val clientConfirmResponse: PaymentClientConfirmResponse = paymentClient.confirm(
paymentKey = request.paymentKey,
orderId = request.orderId,
amount = request.amount,
)
return created.toCreateResponse()
.also { log.info { "[PaymentService.createPayment] 완료: paymentKey=${it.paymentKey}, reservationId=${reservation.id}, paymentId=${it.id}" } }
return transactionExecutionUtil.withNewTransaction(isReadOnly = false) {
val payment: PaymentEntity = paymentWriter.createPayment(
reservationId = reservationId,
orderId = request.orderId,
paymentType = request.paymentType,
paymentClientConfirmResponse = clientConfirmResponse
)
val detail: PaymentDetailEntity = paymentWriter.createDetail(clientConfirmResponse, payment.id)
PaymentCreateResponse(paymentId = payment.id, detailId = detail.id)
}
}
@Transactional
fun createCanceledPayment(
canceledPaymentInfo: PaymentCancelResponse,
approvedAt: OffsetDateTime,
paymentKey: String,
): CanceledPaymentEntity {
log.debug { "[PaymentService.createCanceledPayment] 시작: paymentKey=$paymentKey" }
fun cancel(memberId: Long, request: PaymentCancelRequest) {
val payment: PaymentEntity = findByReservationIdOrThrow(request.reservationId)
val created: CanceledPaymentEntity = paymentWriter.createCanceled(
cancelReason = canceledPaymentInfo.cancelReason,
cancelAmount = canceledPaymentInfo.cancelAmount,
canceledAt = canceledPaymentInfo.canceledAt,
approvedAt = approvedAt,
paymentKey = paymentKey
val clientCancelResponse: PaymentClientCancelResponse = paymentClient.cancel(
paymentKey = payment.paymentKey,
amount = payment.totalAmount,
cancelReason = request.cancelReason
)
return created.also {
log.info { "[PaymentService.createCanceledPayment] 완료: paymentKey=${paymentKey}, canceledPaymentId=${it.id}" }
}
}
@Transactional
fun createCanceledPayment(reservationId: Long): PaymentCancelRequest {
log.debug { "[PaymentService.createCanceledPayment] 시작: reservationId=$reservationId" }
val payment: PaymentEntity = paymentFinder.findByReservationId(reservationId)
val canceled: CanceledPaymentEntity = paymentWriter.createCanceled(
transactionExecutionUtil.withNewTransaction(isReadOnly = false) {
paymentWriter.cancel(
memberId = memberId,
payment = payment,
cancelReason = "예약 취소",
canceledAt = OffsetDateTime.now(),
requestedAt = request.requestedAt,
cancelResponse = clientCancelResponse
)
return PaymentCancelRequest(canceled.paymentKey, canceled.cancelAmount, canceled.cancelReason)
.also { log.info { "[PaymentService.createCanceledPayment] 완료: reservationId=$reservationId, paymentKey=${it.paymentKey}" } }
}
@Transactional
fun updateCanceledTime(
paymentKey: String,
canceledAt: OffsetDateTime,
) {
log.debug { "[PaymentService.updateCanceledTime] 시작: paymentKey=$paymentKey, canceledAt=$canceledAt" }
paymentFinder.findCanceledByKey(paymentKey).apply { this.canceledAt = canceledAt }
log.info { "[PaymentService.updateCanceledTime] 완료: paymentKey=$paymentKey, canceledAt=$canceledAt" }
}.also {
log.info { "[PaymentService.cancel] 결제 취소 완료: paymentId=${payment.id}" }
}
}
@Transactional(readOnly = true)
fun findDetailByReservationId(reservationId: Long): PaymentRetrieveResponse {
val payment: PaymentEntity = findByReservationIdOrThrow(reservationId)
val paymentDetail: PaymentDetailEntity = findDetailByPaymentIdOrThrow(payment.id)
val cancelDetail: CanceledPaymentEntity? = canceledPaymentRepository.findByPaymentId(payment.id)
return payment.toRetrieveResponse(
detail = paymentDetail.toPaymentDetailResponse(),
cancel = cancelDetail?.toCancelDetailResponse()
)
}
private fun findByReservationIdOrThrow(reservationId: Long): PaymentEntity {
log.info { "[PaymentService.findByReservationIdOrThrow] 결제 정보 조회 시작: reservationId=: $reservationId" }
return paymentRepository.findByReservationId(reservationId)
?.also { log.info { "[PaymentService.findByReservationIdOrThrow] 결제 정보 조회 완료: reservationId=$reservationId, paymentId=${it.id}" } }
?: run {
log.warn { "[PaymentService.findByReservationIdOrThrow] 결제 정보 조회 실패: reservationId=$reservationId" }
throw PaymentException(PaymentErrorCode.PAYMENT_NOT_FOUND)
}
}
private fun findDetailByPaymentIdOrThrow(paymentId: Long): PaymentDetailEntity {
log.info { "[PaymentService.findDetailByPaymentIdOrThrow] 결제 상세 정보 조회 시작: paymentId=$paymentId" }
return paymentDetailRepository.findByPaymentId(paymentId)
?.also { log.info { "[PaymentService.findDetailByPaymentIdOrThrow] 결제 상세 정보 조회 완료: paymentId=$paymentId, detailId=${it.id}}" } }
?: run {
log.warn { "[PaymentService.findDetailByPaymentIdOrThrow] 결제 상세 정보 조회 실패: paymentId=$paymentId" }
throw PaymentException(PaymentErrorCode.PAYMENT_DETAIL_NOT_FOUND)
}
}
}