refactor: PaymentConfig의 빈 타입을 RestClient -> RestClient.Builder으로 롤백 및 테스트 전용 property 추가

This commit is contained in:
이상진 2025-07-16 11:16:17 +09:00
parent 635f015b3c
commit 51b0877508
4 changed files with 38 additions and 39 deletions

View File

@ -15,21 +15,19 @@ import java.util.*
class PaymentConfig { class PaymentConfig {
@Bean @Bean
fun paymentClient( fun tossPaymentClientBuilder(
paymentProperties: PaymentProperties, paymentProperties: PaymentProperties,
restClientBuilder: RestClient.Builder ): RestClient.Builder {
): RestClient {
val settings: ClientHttpRequestFactorySettings = ClientHttpRequestFactorySettings.defaults().also { val settings: ClientHttpRequestFactorySettings = ClientHttpRequestFactorySettings.defaults().also {
it.withReadTimeout(Duration.ofSeconds(paymentProperties.readTimeout.toLong())) it.withReadTimeout(Duration.ofSeconds(paymentProperties.readTimeout.toLong()))
it.withConnectTimeout(Duration.ofSeconds(paymentProperties.connectTimeout.toLong())) it.withConnectTimeout(Duration.ofSeconds(paymentProperties.connectTimeout.toLong()))
} }
val requestFactory = ClientHttpRequestFactoryBuilder.jdk().build(settings) val requestFactory = ClientHttpRequestFactoryBuilder.jdk().build(settings)
return restClientBuilder return RestClient.builder()
.baseUrl(paymentProperties.apiBaseUrl) .baseUrl(paymentProperties.apiBaseUrl)
.defaultHeader("Authorization", getAuthorizations(paymentProperties.confirmSecretKey)) .defaultHeader("Authorization", getAuthorizations(paymentProperties.confirmSecretKey))
.requestFactory(requestFactory) .requestFactory(requestFactory)
.build()
} }
private fun getAuthorizations(secretKey: String): String { private fun getAuthorizations(secretKey: String): String {

View File

@ -16,58 +16,55 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import roomescape.common.exception.ErrorType; import roomescape.common.exception.ErrorType;
import roomescape.common.exception.RoomescapeException; import roomescape.common.exception.RoomescapeException;
import roomescape.payment.web.dto.request.PaymentCancelRequest; import roomescape.payment.web.PaymentApprove;
import roomescape.payment.web.dto.request.PaymentRequest; import roomescape.payment.web.PaymentCancel;
import roomescape.payment.web.dto.response.PaymentCancelResponse;
import roomescape.payment.web.dto.response.PaymentResponse;
import roomescape.payment.web.dto.response.TossPaymentErrorResponse;
@Component @Component
public class TossPaymentClient { public class TossPaymentClient {
private static final Logger log = LoggerFactory.getLogger(TossPaymentClient.class); private static final Logger log = LoggerFactory.getLogger(TossPaymentClient.class);
private final RestClient paymentClient; private final RestClient tossPaymentClient;
public TossPaymentClient(RestClient paymentClient) { public TossPaymentClient(RestClient.Builder tossPaymentClientBuilder) {
this.paymentClient = paymentClient; this.tossPaymentClient = tossPaymentClientBuilder.build();
} }
public PaymentResponse confirmPayment(PaymentRequest paymentRequest) { public PaymentApprove.Response confirmPayment(PaymentApprove.Request paymentRequest) {
logPaymentInfo(paymentRequest); logPaymentInfo(paymentRequest);
return paymentClient.post() return tossPaymentClient.post()
.uri("/v1/payments/confirm") .uri("/v1/payments/confirm")
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.body(paymentRequest) .body(paymentRequest)
.retrieve() .retrieve()
.onStatus(status -> status.is4xxClientError() || status.is5xxServerError(), .onStatus(status -> status.is4xxClientError() || status.is5xxServerError(),
(req, res) -> handlePaymentError(res)) (req, res) -> handlePaymentError(res))
.body(PaymentResponse.class); .body(PaymentApprove.Response.class);
} }
public PaymentCancelResponse cancelPayment(PaymentCancelRequest cancelRequest) { public PaymentCancel.Response cancelPayment(PaymentCancel.Request cancelRequest) {
logPaymentCancelInfo(cancelRequest); logPaymentCancelInfo(cancelRequest);
Map<String, String> param = Map.of("cancelReason", cancelRequest.cancelReason()); Map<String, String> param = Map.of("cancelReason", cancelRequest.cancelReason);
return paymentClient.post() return tossPaymentClient.post()
.uri("/v1/payments/{paymentKey}/cancel", cancelRequest.paymentKey()) .uri("/v1/payments/{paymentKey}/cancel", cancelRequest.paymentKey)
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.body(param) .body(param)
.retrieve() .retrieve()
.onStatus(status -> status.is4xxClientError() || status.is5xxServerError(), .onStatus(status -> status.is4xxClientError() || status.is5xxServerError(),
(req, res) -> handlePaymentError(res)) (req, res) -> handlePaymentError(res))
.body(PaymentCancelResponse.class); .body(PaymentCancel.Response.class);
} }
private void logPaymentInfo(PaymentRequest paymentRequest) { private void logPaymentInfo(PaymentApprove.Request paymentRequest) {
log.info("결제 승인 요청: paymentKey={}, orderId={}, amount={}, paymentType={}", log.info("결제 승인 요청: paymentKey={}, orderId={}, amount={}, paymentType={}",
paymentRequest.paymentKey(), paymentRequest.orderId(), paymentRequest.amount(), paymentRequest.paymentKey, paymentRequest.orderId, paymentRequest.amount,
paymentRequest.paymentType()); paymentRequest.paymentType);
} }
private void logPaymentCancelInfo(PaymentCancelRequest cancelRequest) { private void logPaymentCancelInfo(PaymentCancel.Request cancelRequest) {
log.info("결제 취소 요청: paymentKey={}, amount={}, cancelReason={}", log.info("결제 취소 요청: paymentKey={}, amount={}, cancelReason={}",
cancelRequest.paymentKey(), cancelRequest.amount(), cancelRequest.cancelReason()); cancelRequest.paymentKey, cancelRequest.amount, cancelRequest.cancelReason);
} }
private void handlePaymentError(ClientHttpResponse res) private void handlePaymentError(ClientHttpResponse res)
@ -77,7 +74,7 @@ public class TossPaymentClient {
TossPaymentErrorResponse errorResponse = getErrorResponse(res); TossPaymentErrorResponse errorResponse = getErrorResponse(res);
throw new RoomescapeException(errorType, throw new RoomescapeException(errorType,
String.format("[ErrorCode = %s, ErrorMessage = %s]", errorResponse.code(), errorResponse.message()), String.format("[ErrorCode = %s, ErrorMessage = %s]", errorResponse.code, errorResponse.message),
statusCode); statusCode);
} }

View File

@ -19,10 +19,8 @@ import org.springframework.test.web.client.MockRestServiceServer;
import roomescape.common.exception.ErrorType; import roomescape.common.exception.ErrorType;
import roomescape.common.exception.RoomescapeException; import roomescape.common.exception.RoomescapeException;
import roomescape.payment.SampleTossPaymentConst; import roomescape.payment.SampleTossPaymentConst;
import roomescape.payment.web.dto.request.PaymentCancelRequest; import roomescape.payment.web.PaymentApprove;
import roomescape.payment.web.dto.request.PaymentRequest; import roomescape.payment.web.PaymentCancel;
import roomescape.payment.web.dto.response.PaymentCancelResponse;
import roomescape.payment.web.dto.response.PaymentResponse;
@RestClientTest(TossPaymentClient.class) @RestClientTest(TossPaymentClient.class)
class TossPaymentClientTest { class TossPaymentClientTest {
@ -46,12 +44,12 @@ class TossPaymentClientTest {
.body(SampleTossPaymentConst.confirmJson)); .body(SampleTossPaymentConst.confirmJson));
// when // when
PaymentRequest paymentRequest = SampleTossPaymentConst.paymentRequest; PaymentApprove.Request paymentRequest = SampleTossPaymentConst.paymentRequest;
PaymentResponse paymentResponse = tossPaymentClient.confirmPayment(paymentRequest); PaymentApprove.Response paymentResponse = tossPaymentClient.confirmPayment(paymentRequest);
// then // then
assertThat(paymentResponse.paymentKey()).isEqualTo(paymentRequest.paymentKey()); assertThat(paymentResponse.paymentKey).isEqualTo(paymentRequest.paymentKey);
assertThat(paymentResponse.orderId()).isEqualTo(paymentRequest.orderId()); assertThat(paymentResponse.orderId).isEqualTo(paymentRequest.orderId);
} }
@Test @Test
@ -67,12 +65,12 @@ class TossPaymentClientTest {
.body(SampleTossPaymentConst.cancelJson)); .body(SampleTossPaymentConst.cancelJson));
// when // when
PaymentCancelRequest cancelRequest = SampleTossPaymentConst.cancelRequest; PaymentCancel.Request cancelRequest = SampleTossPaymentConst.cancelRequest;
PaymentCancelResponse paymentCancelResponse = tossPaymentClient.cancelPayment(cancelRequest); PaymentCancel.Response paymentCancelResponse = tossPaymentClient.cancelPayment(cancelRequest);
// then // then
assertThat(paymentCancelResponse.cancelStatus()).isEqualTo("DONE"); assertThat(paymentCancelResponse.cancelStatus).isEqualTo("DONE");
assertThat(paymentCancelResponse.cancelReason()).isEqualTo(cancelRequest.cancelReason()); assertThat(paymentCancelResponse.cancelReason).isEqualTo(cancelRequest.cancelReason);
} }
@Test @Test

View File

@ -25,3 +25,9 @@ security:
secret-key: daijawligagaf@LIJ$@U)9nagnalkkgalijaddljfi secret-key: daijawligagaf@LIJ$@U)9nagnalkkgalijaddljfi
access: access:
expire-length: 1800000 # 30 분 expire-length: 1800000 # 30 분
payment:
api-base-url: https://api.tosspayments.com
confirm-secret-key: test_gsk_docs_OaPz8L5KdmQXkzRz3y47BMw6
read-timeout: 3
connect-timeout: 30