diff --git a/src/main/kotlin/roomescape/payment/infrastructure/client/TossPaymentClient.kt b/src/main/kotlin/roomescape/payment/infrastructure/client/TossPaymentClient.kt index 5fa16a83..baa6594e 100644 --- a/src/main/kotlin/roomescape/payment/infrastructure/client/TossPaymentClient.kt +++ b/src/main/kotlin/roomescape/payment/infrastructure/client/TossPaymentClient.kt @@ -4,17 +4,15 @@ import com.fasterxml.jackson.databind.ObjectMapper import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.http.HttpRequest -import org.springframework.http.HttpStatus import org.springframework.http.HttpStatusCode import org.springframework.http.MediaType import org.springframework.http.client.ClientHttpResponse import org.springframework.stereotype.Component import org.springframework.web.client.RestClient -import roomescape.common.exception.ErrorType -import roomescape.common.exception.RoomescapeException +import roomescape.payment.exception.PaymentErrorCode +import roomescape.payment.exception.PaymentException import roomescape.payment.web.PaymentCancelRequest import roomescape.payment.web.PaymentCancelResponse -import java.io.IOException import java.util.Map @Component @@ -43,7 +41,7 @@ class TossPaymentClient( { req: HttpRequest, res: ClientHttpResponse -> handlePaymentError(res) } ) .body(PaymentApproveResponse::class.java) - ?: throw RoomescapeException(ErrorType.PAYMENT_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR) + ?: throw PaymentException(PaymentErrorCode.PAYMENT_PROVIDER_ERROR) } fun cancel(cancelRequest: PaymentCancelRequest): PaymentCancelResponse { @@ -60,7 +58,7 @@ class TossPaymentClient( { req: HttpRequest, res: ClientHttpResponse -> handlePaymentError(res) } ) .body(PaymentCancelResponse::class.java) - ?: throw RoomescapeException(ErrorType.PAYMENT_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR) + ?: throw PaymentException(PaymentErrorCode.PAYMENT_PROVIDER_ERROR) } private fun logPaymentInfo(paymentRequest: PaymentApproveRequest) { @@ -80,32 +78,25 @@ class TossPaymentClient( private fun handlePaymentError( res: ClientHttpResponse ): Nothing { - val statusCode = res.statusCode - val errorType = getErrorTypeByStatusCode(statusCode) - val errorResponse = getErrorResponse(res) - - throw RoomescapeException( - errorType, - "[ErrorCode = ${errorResponse.code}, ErrorMessage = ${errorResponse.message}]", - statusCode - ) + getErrorCodeByHttpStatus(res.statusCode).also { + logTossPaymentError(res) + throw PaymentException(it) + } } - private fun getErrorResponse( - res: ClientHttpResponse - ): TossPaymentErrorResponse { + private fun logTossPaymentError(res: ClientHttpResponse): TossPaymentErrorResponse { val body = res.body val errorResponse = objectMapper.readValue(body, TossPaymentErrorResponse::class.java) body.close() + + log.error { "결제 실패. response: $errorResponse" } return errorResponse } - private fun getErrorTypeByStatusCode( - statusCode: HttpStatusCode - ): ErrorType { + private fun getErrorCodeByHttpStatus(statusCode: HttpStatusCode): PaymentErrorCode { if (statusCode.is4xxClientError) { - return ErrorType.PAYMENT_ERROR + return PaymentErrorCode.PAYMENT_CLIENT_ERROR } - return ErrorType.PAYMENT_SERVER_ERROR + return PaymentErrorCode.PAYMENT_PROVIDER_ERROR } } diff --git a/src/test/kotlin/roomescape/payment/infrastructure/client/TossPaymentClientTest.kt b/src/test/kotlin/roomescape/payment/infrastructure/client/TossPaymentClientTest.kt index b4f6e5c4..8f6eae96 100644 --- a/src/test/kotlin/roomescape/payment/infrastructure/client/TossPaymentClientTest.kt +++ b/src/test/kotlin/roomescape/payment/infrastructure/client/TossPaymentClientTest.kt @@ -14,8 +14,8 @@ import org.springframework.test.web.client.ResponseActions import org.springframework.test.web.client.match.MockRestRequestMatchers.* import org.springframework.test.web.client.response.MockRestResponseCreators.withStatus import org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess -import roomescape.common.exception.ErrorType -import roomescape.common.exception.RoomescapeException +import roomescape.payment.exception.PaymentErrorCode +import roomescape.payment.exception.PaymentException import roomescape.payment.web.PaymentCancelRequest import roomescape.payment.web.PaymentCancelResponse @@ -56,28 +56,32 @@ class TossPaymentClientTest( } } - test("400 에러 발생") { - commonAction().andRespond { - withStatus(HttpStatus.BAD_REQUEST) - .contentType(MediaType.APPLICATION_JSON) - .body(SampleTossPaymentConst.tossPaymentErrorJson) - .createResponse(it) + context("실패 응답") { + fun runTest(httpStatus: HttpStatus, expectedError: PaymentErrorCode) { + commonAction().andRespond { + withStatus(httpStatus) + .contentType(MediaType.APPLICATION_JSON) + .body(SampleTossPaymentConst.tossPaymentErrorJson) + .createResponse(it) + } + + // when + val paymentRequest = SampleTossPaymentConst.paymentRequest + + // then + val exception = shouldThrow { + client.confirm(paymentRequest) + } + exception.errorCode shouldBe expectedError } - // when - val paymentRequest = SampleTossPaymentConst.paymentRequest - - // then - val exception = shouldThrow { - client.confirm(paymentRequest) + test("결제 서버에서 4XX 응답 시") { + runTest(HttpStatus.BAD_REQUEST, PaymentErrorCode.PAYMENT_CLIENT_ERROR) } - assertSoftly(exception) { - this.errorType shouldBe ErrorType.PAYMENT_ERROR - this.invalidValue shouldBe "[ErrorCode = ERROR_CODE, ErrorMessage = Error message]" - this.httpStatus shouldBe HttpStatus.BAD_REQUEST + test("결제 서버에서 5XX 응답 시") { + runTest(HttpStatus.INTERNAL_SERVER_ERROR, PaymentErrorCode.PAYMENT_PROVIDER_ERROR) } - } } @@ -111,26 +115,29 @@ class TossPaymentClientTest( } } - test("500 에러 발생") { - commonAction().andRespond { - withStatus(HttpStatus.INTERNAL_SERVER_ERROR) - .contentType(MediaType.APPLICATION_JSON) - .body(SampleTossPaymentConst.tossPaymentErrorJson) - .createResponse(it) + context("실패 응답") { + fun runTest(httpStatus: HttpStatus, expectedError: PaymentErrorCode) { + commonAction().andRespond { + withStatus(httpStatus) + .contentType(MediaType.APPLICATION_JSON) + .body(SampleTossPaymentConst.tossPaymentErrorJson) + .createResponse(it) + } + + val cancelRequest: PaymentCancelRequest = SampleTossPaymentConst.cancelRequest + + val exception = shouldThrow { + client.cancel(cancelRequest) + } + exception.errorCode shouldBe expectedError } - // when - val cancelRequest: PaymentCancelRequest = SampleTossPaymentConst.cancelRequest - - // then - val exception = shouldThrow { - client.cancel(cancelRequest) + test("결제 서버에서 4XX 응답 시") { + runTest(HttpStatus.BAD_REQUEST, PaymentErrorCode.PAYMENT_CLIENT_ERROR) } - assertSoftly(exception) { - this.errorType shouldBe ErrorType.PAYMENT_SERVER_ERROR - this.invalidValue shouldBe "[ErrorCode = ERROR_CODE, ErrorMessage = Error message]" - this.httpStatus shouldBe HttpStatus.INTERNAL_SERVER_ERROR + test("결제 서버에서 5XX 응답 시") { + runTest(HttpStatus.INTERNAL_SERVER_ERROR, PaymentErrorCode.PAYMENT_PROVIDER_ERROR) } } }