스프링부트 SpringBoot 쇼핑몰 토스 Toss Payment API 연동 결제 구현하기
my code archive
article thumbnail
반응형

외부 결제 API를 연동하라고 적혀 있어서

사용해본 적 없는 결제 API 중에 토스 결제 API를 써보기로 했다.

 

1. 토스 개발자센터에서 시크릿키 발급받기

https://developers.tosspayments.com/

 

토스페이먼츠 개발자센터

토스페이먼츠 결제 연동 문서, API, 키, 테스트 내역, 웹훅 등록 등 개발에 필요한 정보와 기능을 확인해 보세요. 결제 연동에 필요한 모든 개발자 도구를 제공해 드립니다.

developers.tosspayments.com

 

테스트키를 사용하면 실제 결제는 이뤄지지 않는다.

 

 

2.토스 결제 Entity

@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TossPayment extends BaseEntity{

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "payment_id", nullable = false, unique = true)
	private Long paymentId;
	
	@Column(nullable = false, unique = true)
	private String paymentKey;
	
	@Column(nullable = false)
	private String orderId;

	@Column(nullable = false)
	private String orderName;
	
	@Column(nullable = false, name="amount")
	private Long amount;
	
	@ManyToOne(cascade = CascadeType.PERSIST)
	@JoinColumn(name = "customer_id")
	private Customer customer;
	
}

 

3. 응답 Response Dto

@Getter
@Setter
public class TossConfirmRequestDto {

	String orderId;
	
	Long amount;
	
	String paymentKey;
}
/*
 * Record 클래스
 * -Java 14부터 도입
 * -필드별 getter가 자동으로 생성됨
 * -모든 버변수를 인자로 하는 public 생성자를 자동으로 생성함.
 */
public record TossResponseDto (
		
    String paymentKey,

    String orderId,

    Long amount
) {
	
	public static TossResponseDto from(TossPayment tossPayment) {
		return new TossResponseDto(tossPayment.getPaymentKey(), tossPayment.getOrderId(), tossPayment.getAmount());
	}
}

 

 

4. Controller

@Controller
@RequiredArgsConstructor
public class TossController {
	
	private final CustomerService customerService;
	
	private final TossService tossService;
	
	@GetMapping("/pay")
	public String pay(Principal principal, Model model) {
		
		Customer customer = customerService.getCustomerInfo(principal.getName());
		
		model.addAttribute("customer", customer);
		
		return "/toss/checkout";
	}
	
	@GetMapping("/success")
    public String success(HttpServletRequest request, Model model) {
		
		String paymentKey = request.getParameter("paymentKey");
		String orderId = request.getParameter("orderId");
		String amount = request.getParameter("amount");
		
		model.addAttribute("paymentKey", paymentKey);
		model.addAttribute("orderId", orderId);
		model.addAttribute("amount", amount);

		return "/product/success";
    }

    @GetMapping("/fail")
    public String fail(HttpServletRequest request, Model model) {
    	
    	String code = request.getParameter("code");
    	String message = request.getParameter("message");
    	String orderId = request.getParameter("orderId");
        
    	model.addAttribute("code", code);
		model.addAttribute("message", message);
		model.addAttribute("orderId", orderId);
		
    	return "/product/fail";
    }
	
	@RequestMapping(value = "/confirm")
	public ResponseEntity<TossResponseDto> confirmPayment(Principal principal,  @Valid @RequestBody TossConfirmRequestDto requestDto) throws Exception {
		
		Customer customer = customerService.getCustomerInfo(principal.getName());
		
		TossPayment tossPayment = tossService.confirm(requestDto);
		
		tossPayment.setAmount(requestDto.getAmount());
		tossPayment.setCustomer(customer);
		
		tossService.save(tossPayment);
		
        return ResponseEntity.ok().body(TossResponseDto.from(tossPayment));
	}
}

 

5. 프론트단

getOrderTotal, chkAll

function getOrderTotal(){
    var orderTotal = 0;
    $("input:checkbox[name=cartChkbox]:checked").each(function() {
        var cartProductId = $(this).val();
        var price = $("#price_" + cartProductId).attr("data-price"); // 가격을 숫자로 처리
        var count = $("#count_" + cartProductId).val();  // 수량을 숫자로 처리
        orderTotal += price*count;
    });

    $("#orderTotal").html(orderTotal+'원');
}

function changeCount(obj){

    var count = obj.value;
    var cartProductId = obj.id.split('_')[1];
    var price = $("#price_" + cartProductId).data("price");
    var total = count*price;

    $("#totalPrice_" + cartProductId).html(total+"원");

    getOrderTotal();

}

function chkAll(){
    if($("#checkall").is(":checked")){
        $("input:checkbox[name=cartChkbox]").prop("checked",true);
    }else{
        $("input:checkbox[name=cartChkbox]").prop("checked",false);
    }
    getOrderTotal();
}

 

requestPayment

const clientKey = "test_클라이언트키";	//토스 결제 개발 연동 클라이언트키 
const customerKey = generateRandomString();
const secretKey = "test_시크릿키";
const tossPayments = TossPayments(clientKey);
const payment = tossPayments.payment({ customerKey });

async function requestPayment() {

    var nameList = '';

    var checkbox = $("input:checkbox[name=cartChkbox]:checked");

    checkbox.each(function(i) {

        var tr = checkbox.parent().parent().eq(i);
        var td = tr.children();

        var name = td.eq(1).find("#productName").text();

        nameList += ', ' + name;

    });

    const productName = nameList.substring(1);
    const totalText = $("#orderTotal").text();
    const total = totalText.substring(0, totalText.length -1);
    const orderId = generateRandomString();

    console.log(orderId);

   await payment.requestPayment({
       method: "CARD", // 카드 결제
       amount: {
           currency: "KRW",
           value: parseInt(total),
       },
       orderId: orderId, // 고유 주분번호
       orderName: productName,
       successUrl: window.location.origin + "/success", // 결제 요청이 성공하면 리다이렉트되는 URL
       failUrl: window.location.origin + "/fail", // 결제 요청이 실패하면 리다이렉트되는 URL
       customerEmail: [[${customerEmail}]],
       customerName:  [[${customerName}]],
       customerMobilePhone: null,
       // 카드 결제에 필요한 정보
       card: {
           useEscrow: false,
           flowMode: "DEFAULT", // 통합결제창 여는 옵션
           useCardPoint: false,
           useAppCardOnly: false,
       },
   });

}

function generateRandomString() {
    return window.btoa(Math.random()).slice(0, 20);
}

 

결제 버튼

<h2 class="text-center">
    총 주문 금액 : <span id="orderTotal" class="text-danger">0원</span>
</h2>
<div class="text-center">
    <button type="button" class="btn btn-primary btn-lg" onclick="requestPayment()">토스로 결제하기</button>
</div>

 

체크박스 클릭 시 주문 금액 바뀜

 

토스 결제창 띄우기

반응형
profile

my code archive

@얼레벌레 개발자👩‍💻

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

반응형