refactor: PaymentDTO에서의 inner class 분리

This commit is contained in:
이상진 2025-07-22 10:43:59 +09:00
parent c857480753
commit 8a2e13ae2f
13 changed files with 88 additions and 90 deletions

View File

@ -9,10 +9,7 @@ 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.PaymentApprove
import roomescape.payment.web.PaymentCancel
import roomescape.payment.web.ReservationPaymentResponse
import roomescape.payment.web.toReservationPaymentResponse
import roomescape.payment.web.*
import roomescape.reservation.infrastructure.persistence.ReservationEntity
import java.time.OffsetDateTime
@ -23,7 +20,7 @@ class PaymentService(
) {
@Transactional
fun savePayment(
paymentResponse: PaymentApprove.Response,
paymentResponse: PaymentApproveResponse,
reservation: ReservationEntity
): ReservationPaymentResponse = PaymentEntity(
orderId = paymentResponse.orderId,
@ -42,7 +39,7 @@ class PaymentService(
@Transactional
fun saveCanceledPayment(
cancelInfo: PaymentCancel.Response,
cancelInfo: PaymentCancelResponse,
approvedAt: OffsetDateTime,
paymentKey: String
): CanceledPaymentEntity = CanceledPaymentEntity(
@ -55,7 +52,7 @@ class PaymentService(
@Transactional
fun cancelPaymentByAdmin(reservationId: Long): PaymentCancel.Request {
fun cancelPaymentByAdmin(reservationId: Long): PaymentCancelRequest {
val paymentKey: String = paymentRepository.findPaymentKeyByReservationId(reservationId)
?: throw RoomescapeException(
ErrorType.PAYMENT_NOT_FOUND,
@ -65,7 +62,7 @@ class PaymentService(
// 취소 시간은 현재 시간으로 일단 생성한 뒤, 결제 취소 완료 후 해당 시간으로 변경합니다.
val canceled: CanceledPaymentEntity = cancelPayment(paymentKey)
return PaymentCancel.Request(paymentKey, canceled.cancelAmount, canceled.cancelReason)
return PaymentCancelRequest(paymentKey, canceled.cancelAmount, canceled.cancelReason)
}
private fun cancelPayment(

View File

@ -5,23 +5,23 @@ import com.fasterxml.jackson.core.TreeNode
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import roomescape.payment.web.PaymentCancel
import roomescape.payment.web.PaymentCancelResponse
import java.io.IOException
import java.time.OffsetDateTime
class PaymentCancelResponseDeserializer(
vc: Class<PaymentCancel.Response>? = null
) : StdDeserializer<PaymentCancel.Response>(vc) {
vc: Class<PaymentCancelResponse>? = null
) : StdDeserializer<PaymentCancelResponse>(vc) {
@Throws(IOException::class)
override fun deserialize(
jsonParser: JsonParser,
deserializationContext: DeserializationContext?
): PaymentCancel.Response {
): PaymentCancelResponse {
val cancels: JsonNode = jsonParser.codec.readTree<TreeNode>(jsonParser)
.get("cancels")
.get(0) as JsonNode
return PaymentCancel.Response(
return PaymentCancelResponse(
cancels.get("cancelStatus").asText(),
cancels.get("cancelReason").asText(),
cancels.get("cancelAmount").asLong(),

View File

@ -12,8 +12,10 @@ import org.springframework.stereotype.Component
import org.springframework.web.client.RestClient
import roomescape.common.exception.ErrorType
import roomescape.common.exception.RoomescapeException
import roomescape.payment.web.PaymentApprove
import roomescape.payment.web.PaymentCancel
import roomescape.payment.web.PaymentApproveRequest
import roomescape.payment.web.PaymentApproveResponse
import roomescape.payment.web.PaymentCancelRequest
import roomescape.payment.web.PaymentCancelResponse
import java.io.IOException
import java.util.Map
@ -30,7 +32,7 @@ class TossPaymentClient(
private val tossPaymentClient: RestClient = tossPaymentClientBuilder.build()
fun confirmPayment(paymentRequest: PaymentApprove.Request): PaymentApprove.Response {
fun confirmPayment(paymentRequest: PaymentApproveRequest): PaymentApproveResponse {
logPaymentInfo(paymentRequest)
return tossPaymentClient.post()
@ -42,11 +44,11 @@ class TossPaymentClient(
{ status: HttpStatusCode -> status.is4xxClientError || status.is5xxServerError },
{ req: HttpRequest, res: ClientHttpResponse -> handlePaymentError(res) }
)
.body(PaymentApprove.Response::class.java)
.body(PaymentApproveResponse::class.java)
?: throw RoomescapeException(ErrorType.PAYMENT_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR)
}
fun cancelPayment(cancelRequest: PaymentCancel.Request): PaymentCancel.Response {
fun cancelPayment(cancelRequest: PaymentCancelRequest): PaymentCancelResponse {
logPaymentCancelInfo(cancelRequest)
val param = Map.of<String, String>("cancelReason", cancelRequest.cancelReason)
@ -59,18 +61,18 @@ class TossPaymentClient(
{ status: HttpStatusCode -> status.is4xxClientError || status.is5xxServerError },
{ req: HttpRequest, res: ClientHttpResponse -> handlePaymentError(res) }
)
.body(PaymentCancel.Response::class.java)
.body(PaymentCancelResponse::class.java)
?: throw RoomescapeException(ErrorType.PAYMENT_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR)
}
private fun logPaymentInfo(paymentRequest: PaymentApprove.Request) {
private fun logPaymentInfo(paymentRequest: PaymentApproveRequest) {
log.info {
"결제 승인 요청: paymentKey=${paymentRequest.paymentKey}, orderId=${paymentRequest.orderId}, " +
"amount=${paymentRequest.amount}, paymentType=${paymentRequest.paymentType}"
}
}
private fun logPaymentCancelInfo(cancelRequest: PaymentCancel.Request) {
private fun logPaymentCancelInfo(cancelRequest: PaymentCancelRequest) {
log.info {
"결제 취소 요청: paymentKey=${cancelRequest.paymentKey}, amount=${cancelRequest.amount}, " +
"cancelReason=${cancelRequest.cancelReason}"

View File

@ -8,38 +8,34 @@ import roomescape.reservation.web.ReservationResponse
import roomescape.reservation.web.toResponse
import java.time.OffsetDateTime
class PaymentApprove {
data class Request(
data class PaymentApproveRequest(
val paymentKey: String,
val orderId: String,
val amount: Long,
val paymentType: String
)
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class Response(
@JsonIgnoreProperties(ignoreUnknown = true)
data class PaymentApproveResponse(
val paymentKey: String,
val orderId: String,
val approvedAt: OffsetDateTime,
val totalAmount: Long
)
}
)
class PaymentCancel {
data class Request(
data class PaymentCancelRequest(
val paymentKey: String,
val amount: Long,
val cancelReason: String
)
)
@JsonDeserialize(using = PaymentCancelResponseDeserializer::class)
data class Response(
@JsonDeserialize(using = PaymentCancelResponseDeserializer::class)
data class PaymentCancelResponse(
val cancelStatus: String,
val cancelReason: String,
val cancelAmount: Long,
val canceledAt: OffsetDateTime
)
}
)
data class ReservationPaymentResponse(
val id: Long,

View File

@ -3,8 +3,9 @@ package roomescape.reservation.business
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import roomescape.payment.business.PaymentService
import roomescape.payment.web.PaymentApprove
import roomescape.payment.web.PaymentCancel
import roomescape.payment.web.PaymentApproveResponse
import roomescape.payment.web.PaymentCancelRequest
import roomescape.payment.web.PaymentCancelResponse
import roomescape.reservation.infrastructure.persistence.ReservationEntity
import roomescape.reservation.web.ReservationRequest
import roomescape.reservation.web.ReservationResponse
@ -18,7 +19,7 @@ class ReservationWithPaymentService(
) {
fun addReservationWithPayment(
request: ReservationRequest,
paymentInfo: PaymentApprove.Response,
paymentInfo: PaymentApproveResponse,
memberId: Long
): ReservationResponse {
val reservation: ReservationEntity = reservationService.addReservation(request, memberId)
@ -28,7 +29,7 @@ class ReservationWithPaymentService(
}
fun saveCanceledPayment(
cancelInfo: PaymentCancel.Response,
cancelInfo: PaymentCancelResponse,
approvedAt: OffsetDateTime,
paymentKey: String
) {
@ -38,7 +39,7 @@ class ReservationWithPaymentService(
fun removeReservationWithPayment(
reservationId: Long,
memberId: Long
): PaymentCancel.Request {
): PaymentCancelRequest {
val paymentCancelRequest = paymentService.cancelPaymentByAdmin(reservationId)
reservationService.removeReservationById(reservationId, memberId)

View File

@ -8,8 +8,9 @@ import roomescape.auth.web.support.MemberId
import roomescape.common.dto.response.CommonApiResponse
import roomescape.common.exception.RoomescapeException
import roomescape.payment.infrastructure.client.TossPaymentClient
import roomescape.payment.web.PaymentApprove
import roomescape.payment.web.PaymentCancel
import roomescape.payment.web.PaymentApproveRequest
import roomescape.payment.web.PaymentApproveResponse
import roomescape.payment.web.PaymentCancelRequest
import roomescape.reservation.business.ReservationService
import roomescape.reservation.business.ReservationWithPaymentService
import roomescape.reservation.docs.ReservationAPI
@ -74,8 +75,8 @@ class ReservationController(
@Valid @RequestBody reservationRequest: ReservationRequest,
@MemberId @Parameter(hidden = true) memberId: Long
): ResponseEntity<CommonApiResponse<ReservationResponse>> {
val paymentRequest: PaymentApprove.Request = reservationRequest.paymentRequest
val paymentResponse: PaymentApprove.Response = paymentClient.confirmPayment(paymentRequest)
val paymentRequest: PaymentApproveRequest = reservationRequest.paymentRequest
val paymentResponse: PaymentApproveResponse = paymentClient.confirmPayment(paymentRequest)
try {
val reservationResponse: ReservationResponse = reservationWithPaymentService.addReservationWithPayment(
@ -86,7 +87,7 @@ class ReservationController(
return ResponseEntity.created(URI.create("/reservations/${reservationResponse.id}"))
.body(CommonApiResponse(reservationResponse))
} catch (e: RoomescapeException) {
val cancelRequest = PaymentCancel.Request(paymentRequest.paymentKey,
val cancelRequest = PaymentCancelRequest(paymentRequest.paymentKey,
paymentRequest.amount, e.message!!)
val paymentCancelResponse = paymentClient.cancelPayment(cancelRequest)
reservationWithPaymentService.saveCanceledPayment(paymentCancelResponse, paymentResponse.approvedAt,

View File

@ -2,7 +2,7 @@ package roomescape.reservation.web
import com.fasterxml.jackson.annotation.JsonIgnore
import io.swagger.v3.oas.annotations.media.Schema
import roomescape.payment.web.PaymentApprove
import roomescape.payment.web.PaymentApproveRequest
import java.time.LocalDate
@Schema(name = "관리자 예약 저장 요청", description = "관리자의 예약 저장 요청시 사용됩니다.")
@ -41,8 +41,8 @@ data class ReservationRequest(
val paymentType: String
) {
@get:JsonIgnore
val paymentRequest: PaymentApprove.Request
get() = PaymentApprove.Request(paymentKey, orderId, amount, paymentType)
val paymentRequest: PaymentApproveRequest
get() = PaymentApproveRequest(paymentKey, orderId, amount, paymentType)
}
@Schema(name = "예약 대기 저장 요청", description = "회원의 예약 대기 요청시 사용됩니다.")

View File

@ -13,7 +13,7 @@ import roomescape.common.exception.ErrorType
import roomescape.common.exception.RoomescapeException
import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository
import roomescape.payment.infrastructure.persistence.PaymentRepository
import roomescape.payment.web.PaymentCancel
import roomescape.payment.web.PaymentCancelRequest
import roomescape.util.PaymentFixture
import java.time.OffsetDateTime
@ -79,7 +79,7 @@ class PaymentServiceTest : FunSpec({
cancelAmount = paymentEntity.totalAmount,
)
val result: PaymentCancel.Request = paymentService.cancelPaymentByAdmin(reservationId)
val result: PaymentCancelRequest = paymentService.cancelPaymentByAdmin(reservationId)
assertSoftly(result) {
this.paymentKey shouldBe paymentKey

View File

@ -6,22 +6,22 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import io.kotest.assertions.assertSoftly
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
import roomescape.payment.web.PaymentCancel
import roomescape.payment.web.PaymentCancelResponse
class PaymentCancelResponseDeserializerTest : StringSpec({
val objectMapper: ObjectMapper = jacksonObjectMapper().registerModule(
SimpleModule().addDeserializer(
PaymentCancel.Response::class.java,
PaymentCancelResponse::class.java,
PaymentCancelResponseDeserializer()
)
)
"결제 취소 응답을 역직렬화하여 PaymentCancelResponse 객체를 생성한다" {
val cancelResponseJson: String = SampleTossPaymentConst.cancelJson
val cancelResponse: PaymentCancel.Response = objectMapper.readValue(
val cancelResponse: PaymentCancelResponse = objectMapper.readValue(
cancelResponseJson,
PaymentCancel.Response::class.java
PaymentCancelResponse::class.java
)
assertSoftly(cancelResponse) {

View File

@ -1,8 +1,7 @@
package roomescape.payment.infrastructure.client
import roomescape.payment.infrastructure.client.SampleTossPaymentConst.amount
import roomescape.payment.web.PaymentApprove
import roomescape.payment.web.PaymentCancel
import roomescape.payment.web.PaymentApproveRequest
import roomescape.payment.web.PaymentCancelRequest
import kotlin.math.roundToLong
object SampleTossPaymentConst {
@ -16,7 +15,7 @@ object SampleTossPaymentConst {
val cancelReason: String = "테스트 결제 취소"
val paymentRequest: PaymentApprove.Request = PaymentApprove.Request(
val paymentRequest: PaymentApproveRequest = PaymentApproveRequest(
paymentKey,
orderId,
amount,
@ -32,7 +31,7 @@ object SampleTossPaymentConst {
}
""".trimIndent()
val cancelRequest: PaymentCancel.Request = PaymentCancel.Request(
val cancelRequest: PaymentCancelRequest = PaymentCancelRequest(
paymentKey,
amount,
cancelReason

View File

@ -16,8 +16,9 @@ import org.springframework.test.web.client.response.MockRestResponseCreators.wit
import org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess
import roomescape.common.exception.ErrorType
import roomescape.common.exception.RoomescapeException
import roomescape.payment.web.PaymentApprove
import roomescape.payment.web.PaymentCancel
import roomescape.payment.web.PaymentApproveResponse
import roomescape.payment.web.PaymentCancelRequest
import roomescape.payment.web.PaymentCancelResponse
@RestClientTest(TossPaymentClient::class)
class TossPaymentClientTest(
@ -47,7 +48,7 @@ class TossPaymentClientTest(
// when
val paymentRequest = SampleTossPaymentConst.paymentRequest
val paymentResponse: PaymentApprove.Response = client.confirmPayment(paymentRequest)
val paymentResponse: PaymentApproveResponse = client.confirmPayment(paymentRequest)
assertSoftly(paymentResponse) {
this.paymentKey shouldBe paymentRequest.paymentKey
@ -101,8 +102,8 @@ class TossPaymentClientTest(
}
// when
val cancelRequest: PaymentCancel.Request = SampleTossPaymentConst.cancelRequest
val cancelResponse: PaymentCancel.Response = client.cancelPayment(cancelRequest)
val cancelRequest: PaymentCancelRequest = SampleTossPaymentConst.cancelRequest
val cancelResponse: PaymentCancelResponse = client.cancelPayment(cancelRequest)
assertSoftly(cancelResponse) {
this.cancelStatus shouldBe "DONE"
@ -120,7 +121,7 @@ class TossPaymentClientTest(
}
// when
val cancelRequest: PaymentCancel.Request = SampleTossPaymentConst.cancelRequest
val cancelRequest: PaymentCancelRequest = SampleTossPaymentConst.cancelRequest
// then
val exception = shouldThrow<RoomescapeException> {

View File

@ -9,7 +9,7 @@ import io.mockk.just
import io.mockk.mockk
import roomescape.payment.business.PaymentService
import roomescape.payment.infrastructure.persistence.PaymentEntity
import roomescape.payment.web.PaymentCancel
import roomescape.payment.web.PaymentCancelRequest
import roomescape.payment.web.toReservationPaymentResponse
import roomescape.reservation.infrastructure.persistence.ReservationEntity
import roomescape.reservation.infrastructure.persistence.ReservationStatus
@ -74,7 +74,7 @@ class ReservationWithPaymentServiceTest : FunSpec({
context("removeReservationWithPayment") {
test("예약 및 결제 정보를 삭제하고, 결제 취소 정보를 저장한다.") {
val paymentCancelRequest: PaymentCancel.Request = PaymentFixture.createCancelRequest().copy(
val paymentCancelRequest: PaymentCancelRequest = PaymentFixture.createCancelRequest().copy(
paymentKey = paymentEntity.paymentKey,
amount = paymentEntity.totalAmount,
cancelReason = "고객 요청"
@ -88,7 +88,7 @@ class ReservationWithPaymentServiceTest : FunSpec({
reservationService.removeReservationById(reservationEntity.id!!, reservationEntity.member.id!!)
} just Runs
val result: PaymentCancel.Request = reservationWithPaymentService.removeReservationWithPayment(
val result: PaymentCancelRequest = reservationWithPaymentService.removeReservationWithPayment(
reservationId = reservationEntity.id!!,
memberId = reservationEntity.member.id!!
)

View File

@ -6,8 +6,10 @@ import roomescape.member.infrastructure.persistence.MemberEntity
import roomescape.member.infrastructure.persistence.Role
import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity
import roomescape.payment.infrastructure.persistence.PaymentEntity
import roomescape.payment.web.PaymentApprove
import roomescape.payment.web.PaymentCancel
import roomescape.payment.web.PaymentApproveRequest
import roomescape.payment.web.PaymentApproveResponse
import roomescape.payment.web.PaymentCancelRequest
import roomescape.payment.web.PaymentCancelResponse
import roomescape.reservation.infrastructure.persistence.ReservationEntity
import roomescape.reservation.infrastructure.persistence.ReservationStatus
import roomescape.reservation.infrastructure.persistence.ReservationTimeEntity
@ -17,7 +19,6 @@ import roomescape.theme.infrastructure.persistence.ThemeEntity
import java.time.LocalDate
import java.time.LocalTime
import java.time.OffsetDateTime
import kotlin.random.Random
object MemberFixture {
const val NOT_LOGGED_IN_USERID: Long = 0
@ -156,27 +157,27 @@ object PaymentFixture {
)
fun createApproveRequest(): PaymentApprove.Request = PaymentApprove.Request(
fun createApproveRequest(): PaymentApproveRequest = PaymentApproveRequest(
paymentKey = PAYMENT_KEY,
orderId = ORDER_ID,
amount = AMOUNT,
paymentType = "CARD"
)
fun createApproveResponse(): PaymentApprove.Response = PaymentApprove.Response(
fun createApproveResponse(): PaymentApproveResponse = PaymentApproveResponse(
paymentKey = PAYMENT_KEY,
orderId = ORDER_ID,
approvedAt = OffsetDateTime.now(),
totalAmount = AMOUNT
)
fun createCancelRequest(): PaymentCancel.Request = PaymentCancel.Request(
fun createCancelRequest(): PaymentCancelRequest = PaymentCancelRequest(
paymentKey = PAYMENT_KEY,
amount = AMOUNT,
cancelReason = "Test Cancel"
)
fun createCancelResponse(): PaymentCancel.Response = PaymentCancel.Response(
fun createCancelResponse(): PaymentCancelResponse = PaymentCancelResponse(
cancelStatus = "SUCCESS",
cancelReason = "Test Cancel",
cancelAmount = AMOUNT,