TossPaymentApiClient.java

package com.newbit.payment.command.application.service;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Collections;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import com.newbit.payment.command.application.dto.TossPaymentApiDto;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class TossPaymentApiClient {

    private final RestTemplate restTemplate;
    
    @Value("${payment.toss.api-key}")
    private String apiKey;
    
    @Value("${payment.toss.secret-key}")
    private String secretKey;
    
    @Value("${payment.toss.api-url}")
    private String apiUrl;
    
    @Value("${payment.toss.success-url}")
    private String successUrl;
    
    @Value("${payment.toss.fail-url}")
    private String failUrl;

    public String createPaymentWidgetUrl(Long amount, String orderId, String orderName) {
        TossPaymentApiDto.WidgetUrlRequest request = TossPaymentApiDto.WidgetUrlRequest.builder()
                .amount(amount)
                .orderId(orderId)
                .orderName(orderName)
                .successUrl(successUrl)
                .failUrl(failUrl)
                .build();
                
        return UriComponentsBuilder.fromUriString(apiUrl)
                .path("/v1/payments/widget/")
                .queryParam("amount", request.getAmount())
                .queryParam("orderId", request.getOrderId())
                .queryParam("orderName", request.getOrderName())
                .queryParam("successUrl", request.getSuccessUrl())
                .queryParam("failUrl", request.getFailUrl())
                .build()
                .toUriString();
    }

    public TossPaymentApiDto.PaymentResponse requestPaymentApproval(String paymentKey, String orderId, Long amount) {
        TossPaymentApiDto.PaymentConfirmRequest request = TossPaymentApiDto.PaymentConfirmRequest.builder()
                .paymentKey(paymentKey)
                .orderId(orderId)
                .amount(amount)
                .build();
        
        HttpEntity<TossPaymentApiDto.PaymentConfirmRequest> entity = new HttpEntity<>(request, createAuthHeaders());
        
        return restTemplate.postForObject(
                apiUrl + "/v1/payments/confirm",
                entity,
                TossPaymentApiDto.PaymentResponse.class
        );
    }

    public TossPaymentApiDto.PaymentResponse getPaymentDetails(String paymentKey) {
        HttpEntity<String> entity = new HttpEntity<>(createAuthHeaders());
        
        return restTemplate.getForObject(
                apiUrl + "/v1/payments/" + paymentKey,
                TossPaymentApiDto.PaymentResponse.class,
                entity
        );
    }

    public TossPaymentApiDto.PaymentResponse cancelPayment(String paymentKey, String cancelReason) {
        TossPaymentApiDto.PaymentCancelRequest request = TossPaymentApiDto.PaymentCancelRequest.builder()
                .cancelReason(cancelReason)
                .build();
                
        return requestCancelPayment(paymentKey, request);
    }
    
    public TossPaymentApiDto.PaymentResponse cancelPaymentPartial(String paymentKey, String cancelReason, Long cancelAmount) {
        TossPaymentApiDto.PaymentCancelRequest request = TossPaymentApiDto.PaymentCancelRequest.builder()
                .cancelReason(cancelReason)
                .cancelAmount(cancelAmount)
                .build();
                
        return requestCancelPayment(paymentKey, request);
    }
    
    private TossPaymentApiDto.PaymentResponse requestCancelPayment(String paymentKey, TossPaymentApiDto.PaymentCancelRequest request) {
        HttpEntity<TossPaymentApiDto.PaymentCancelRequest> entity = new HttpEntity<>(request, createAuthHeaders());
        
        return restTemplate.postForObject(
                apiUrl + "/v1/payments/" + paymentKey + "/cancel",
                entity,
                TossPaymentApiDto.PaymentResponse.class
        );
    }
    
    private HttpHeaders createAuthHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        headers.set("Authorization", "Basic " + Base64.getEncoder().encodeToString(
                (secretKey + ":").getBytes(StandardCharsets.UTF_8)));
        return headers;
    }
}