[#66] 결제 & 예약 확정 로직 수정 #67

Merged
pricelees merged 17 commits from refactor/#66 into main 2025-10-17 04:59:12 +00:00
7 changed files with 0 additions and 198 deletions
Showing only changes of commit 9fe576af11 - Show all commits

View File

@ -1,60 +0,0 @@
package com.sangdol.roomescape.order.business
import com.sangdol.common.persistence.IDGenerator
import com.sangdol.common.persistence.TransactionExecutionUtil
import com.sangdol.roomescape.order.infrastructure.persistence.PostOrderTaskEntity
import com.sangdol.roomescape.order.infrastructure.persistence.PostOrderTaskRepository
import com.sangdol.roomescape.payment.business.PaymentService
import com.sangdol.roomescape.payment.dto.PaymentGatewayResponse
import com.sangdol.roomescape.reservation.business.ReservationService
import io.github.oshai.kotlinlogging.KLogger
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
import java.time.Instant
private val log: KLogger = KotlinLogging.logger {}
@Service
class OrderPostProcessorService(
private val idGenerator: IDGenerator,
private val reservationService: ReservationService,
private val paymentService: PaymentService,
private val postOrderTaskRepository: PostOrderTaskRepository,
private val transactionExecutionUtil: TransactionExecutionUtil
) {
@Transactional(propagation = Propagation.REQUIRES_NEW)
fun processAfterPaymentConfirmation(
reservationId: Long,
paymentResponse: PaymentGatewayResponse
) {
val paymentKey = paymentResponse.paymentKey
try {
log.info { "[processAfterPaymentConfirmation] 결제 정보 저장 및 예약 확정 처리 시작: reservationId=${reservationId}, paymentKey=${paymentKey}" }
val paymentCreateResponse = paymentService.savePayment(reservationId, paymentResponse)
reservationService.confirmReservation(reservationId)
log.info {
"[processAfterPaymentConfirmation] 결제 정보 저장 및 예약 확정 처리 완료: reservationId=${reservationId}, paymentKey=${paymentKey}, paymentId=${paymentCreateResponse.paymentId}, paymentDetailId=${paymentCreateResponse.detailId}"
}
} catch (e: Exception) {
log.warn(e) { "[processAfterPaymentConfirmation] 결제 정보 저장 및 예약 확정 처리 실패. 작업 저장 시작: reservationId=${reservationId}, paymentKey=$paymentKey}" }
transactionExecutionUtil.withNewTransaction(isReadOnly = false) {
PostOrderTaskEntity(
id = idGenerator.create(),
reservationId = reservationId,
paymentKey = paymentKey,
trial = 1,
nextRetryAt = Instant.now().plusSeconds(30),
).also {
postOrderTaskRepository.save(it)
}
}
log.info { "[processAfterPaymentConfirmation] 작업 저장 완료" }
}
}
}

View File

@ -1,45 +0,0 @@
package com.sangdol.roomescape.order.exception
import com.sangdol.common.types.exception.ErrorCode
import com.sangdol.common.types.web.HttpStatus
import com.sangdol.common.web.support.log.WebLogMessageConverter
import io.github.oshai.kotlinlogging.KLogger
import io.github.oshai.kotlinlogging.KotlinLogging
import jakarta.servlet.http.HttpServletRequest
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice
private val log: KLogger = KotlinLogging.logger {}
@RestControllerAdvice
class OrderExceptionHandler(
private val messageConverter: WebLogMessageConverter
) {
@ExceptionHandler(OrderException::class)
fun handleOrderException(
servletRequest: HttpServletRequest,
e: OrderException
): ResponseEntity<OrderErrorResponse> {
val errorCode: ErrorCode = e.errorCode
val httpStatus: HttpStatus = errorCode.httpStatus
val errorResponse = OrderErrorResponse(
code = errorCode.errorCode,
message = if (httpStatus.isClientError()) e.message else errorCode.message,
trial = e.trial
)
log.info {
messageConverter.convertToErrorResponseMessage(
servletRequest = servletRequest,
httpStatus = httpStatus,
responseBody = errorResponse,
exception = if (errorCode.message == e.message) null else e
)
}
return ResponseEntity
.status(httpStatus.value())
.body(errorResponse)
}
}

View File

@ -1,38 +0,0 @@
package com.sangdol.roomescape.order.infrastructure.persistence
import com.sangdol.common.persistence.PersistableBaseEntity
import jakarta.persistence.*
import org.springframework.data.annotation.CreatedBy
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.jpa.domain.support.AuditingEntityListener
import java.time.Instant
@Entity
@EntityListeners(AuditingEntityListener::class)
@Table(name = "payment_attempts")
class PaymentAttemptEntity(
id: Long,
val reservationId: Long,
@Enumerated(value = EnumType.STRING)
val result: AttemptResult,
@Column(columnDefinition = "VARCHAR(50)")
val errorCode: String? = null,
@Column(columnDefinition = "TEXT")
val message: String? = null,
) : PersistableBaseEntity(id) {
@Column(updatable = false)
@CreatedDate
lateinit var createdAt: Instant
@Column(updatable = false)
@CreatedBy
var createdBy: Long = 0L
}
enum class AttemptResult {
SUCCESS, FAILED
}

View File

@ -1,28 +0,0 @@
package com.sangdol.roomescape.order.infrastructure.persistence
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
interface PaymentAttemptRepository: JpaRepository<PaymentAttemptEntity, Long> {
fun countByReservationId(reservationId: Long): Long
@Query(
"""
SELECT
CASE
WHEN COUNT(pa) > 0
THEN TRUE
ELSE FALSE
END
FROM
PaymentAttemptEntity pa
WHERE
pa.reservationId = :reservationId
AND pa.result = com.sangdol.roomescape.order.infrastructure.persistence.AttemptResult.SUCCESS
"""
)
fun isSuccessAttemptExists(reservationId: Long): Boolean
fun findAllByReservationId(reservationId: Long): List<PaymentAttemptEntity>
}

View File

@ -1,16 +0,0 @@
package com.sangdol.roomescape.order.infrastructure.persistence
import com.sangdol.common.persistence.PersistableBaseEntity
import jakarta.persistence.Entity
import jakarta.persistence.Table
import java.time.Instant
@Entity
@Table(name = "post_order_tasks")
class PostOrderTaskEntity(
id: Long,
val reservationId: Long,
val paymentKey: String,
val trial: Int,
val nextRetryAt: Instant
) : PersistableBaseEntity(id)

View File

@ -1,6 +0,0 @@
package com.sangdol.roomescape.order.infrastructure.persistence
import org.springframework.data.jpa.repository.JpaRepository
interface PostOrderTaskRepository : JpaRepository<PostOrderTaskEntity, Long> {
}

View File

@ -8,11 +8,6 @@ data class PaymentConfirmRequest(
val amount: Int, val amount: Int,
) )
data class PaymentCreateResponse(
val paymentId: Long,
val detailId: Long
)
data class PaymentCancelRequest( data class PaymentCancelRequest(
val reservationId: Long, val reservationId: Long,
val cancelReason: String, val cancelReason: String,