보라코딩

Day106_230530_카카오페이 결제 api (ajax로, 결제 취소까지) 본문

코딩/Spring

Day106_230530_카카오페이 결제 api (ajax로, 결제 취소까지)

new 보라 2023. 5. 30. 15:15

ajax 이용해서 값도 전달해줄 수 있도록 변경해보았다.

아직 DB랑 연동을 못해서 고민중 ~

 

 

 

저때 점심시간 전이라 배가 고팠다.




 
 

KakaoPay.html

 

<!DOCTYPE html>
<html lagn="ko"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">

<th:block th:replace="header_footer/header :: headerFragment"></th:block>

<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" th:href="@{/css/store_css.css}">
<style>
.kakaoBtn{
background-color: #F7E600;
padding: 10px;
border: none;
outline: none;
border-radius: 10px;
}

</style>
</head>
<body>


<h2>예약화면</h2>
<button class="kakaoBtn">카카오페이 결제</button>
<br><br>
<form method="post" action="/pay/refund">
<button>카카오페이 결제 취소!!</button>
</form>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
jQuery(function($){

$(".kakaoBtn").click(function(){
//alert("클릭");

//let으로 정보 전달해주기

$.ajax({
type : 'get',
url : '/pay/kakaoPay123',
data : {
//보낼정보들 (일단 임의값 테스트)
total_amount : 54321,
item_name : '배고프다'
},
dataType : 'json',
success : function(readyResponse){
console.log("성공!");
console.log(readyResponse);
console.log(readyResponse.next_redirect_pc_url);

location.href = readyResponse.next_redirect_pc_url;
},
error: function(xhr, status, error) {
console.log("에러 발생");
console.log(xhr);
}
});


});


});


</script>

<th:block th:replace="header_footer/footer :: footerFragment"></th:block>
</body>
</html>

 
 
 
 
 
 

KakaoCancelResponse   (domain)

 

package com.tastemate.domain;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class KakaoCancelResponse {

private String aid; // 요청 고유 번호
private String tid; // 결제 고유 번호
private String cid; // 가맹점 코드
private String status; // 결제 상태
private String partner_order_id; // 가맹점 주문 번호
private String partner_user_id; // 가맹점 회원 ID
private String payment_method_type; // 결제 수단
private AmountVO amount; // 결제 금액 정보, 결제 요청 구현할때 이미 구현해놓음
private ApprovedCancelAmount approved_cancel_amount; // 이번 요청으로 취소된 금액
private CanceledAmount canceled_amount; // 누계 취소 금액
private CancelAvailableAmount cancel_available_amount; // 남은 취소 금액
private String item_name; // 상품 이름
private String item_code; // 상품 코드
private int quantity; // 상품 수량
private String created_at; // 결제 준비 요청 시각
private String approved_at; // 결제 승인 시각
private String canceled_at; // 결제 취소 시각
private String payload; // 취소 요청 시 전달한 값

/**
* 이번 요청으로 취소된 금액
*/
@Getter
@Setter
@ToString
public static class ApprovedCancelAmount {

private int total; // 이번 요청으로 취소된 전체 금액
private int tax_free; // 이번 요청으로 취소된 비과세 금액
private int vat; // 이번 요청으로 취소된 부가세 금액
private int point; // 이번 요청으로 취소된 포인트 금액
private int discount; // 이번 요청으로 취소된 할인 금액
private int green_deposit; // 컵 보증금
}

/**
* 누계 취소 금액
*/
@Getter
@Setter
@ToString
public static class CanceledAmount {

private int total; // 취소된 전체 누적 금액
private int tax_free; // 취소된 비과세 누적 금액
private int vat; // 취소된 부가세 누적 금액
private int point; // 취소된 포인트 누적 금액
private int discount; // 취소된 할인 누적 금액
private int green_deposit; // 컵 보증금
}

/**
* 취소 요청 시 전달한 값
*/
@Getter
@Setter
@ToString
public static class CancelAvailableAmount {

private int total; // 전체 취소 가능 금액
private int tax_free; // 취소 가능 비과세 금액
private int vat; // 취소 가능 부가세 금액
private int point; // 취소 가능 포인트 금액
private int discount; // 취소 가능 할인 금액
private int green_deposit; // 컵 보증금
}
}

 
 
 
 
 
 

PayController

 

package com.tastemate.controller;

import com.tastemate.domain.KakaoCancelResponse;
import com.tastemate.domain.KakaoPayReadyVO;
import com.tastemate.service.KakaoPay;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;

@Controller
@RequestMapping("/pay/*")
@Slf4j
public class PayController {

@Setter(onMethod_ = @Autowired)
private KakaoPay kakaopay;


@GetMapping("/kakaoPay")
public void kakaoPayGet() {
log.info("kakaoPay get............................................");
}


//카카오페이 결제 요청 (ajax)
@GetMapping ("/kakaoPay123")
@ResponseBody
public KakaoPayReadyVO kakaoPay(int total_amount, String item_name, Model model, HttpServletRequest request) {
log.info("kakaoPay post............................................");
log.info("total_amount : " + total_amount);

KakaoPayReadyVO readyResponse = kakaopay.kakaoPayReady(total_amount, item_name);

model.addAttribute("tid", readyResponse.getTid());
log.info("tid : " + readyResponse.getTid());
log.info("readyResponse : " + readyResponse);

request.getSession().setAttribute("total_amount",total_amount);


return readyResponse;

}

// 결제 승인 요청
@GetMapping("/kakaoPaySuccess")
public void kakaoPaySuccess(@RequestParam("pg_token") String pg_token,
//@RequestParam("total_amount") int total_amount,
HttpServletRequest request,
Model model) {
log.info("kakaoPaySuccess get............................................");
log.info("kakaoPaySuccess pg_token : " + pg_token);


model.addAttribute("info", kakaopay.kakaoPayInfo(pg_token));
}


/**
* 환불
*/
@PostMapping("/refund")
public ResponseEntity refund() {

log.info("controller refund............................................");

KakaoCancelResponse kakaoCancelResponse = kakaopay.kakaoCancel();

return new ResponseEntity<>(kakaoCancelResponse, HttpStatus.OK);
}
}

 
 
 
 

KakaoPay (Service 역할)

 

package com.tastemate.service;

import com.tastemate.domain.KakaoCancelResponse;
import com.tastemate.domain.KakaoPayApprovalVO;
import com.tastemate.domain.KakaoPayReadyVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.net.URISyntaxException;

@Service
@Slf4j
public class KakaoPay {

private static final String HOST = "https://kapi.kakao.com";

private KakaoPayReadyVO kakaoPayReadyVO;
private KakaoPayApprovalVO kakaoPayApprovalVO;


//ajax 이용한 결제요청
public KakaoPayReadyVO kakaoPayReady(int total_amount, String item_name) {

log.info("kakaoPayReady..................");

RestTemplate restTemplate = new RestTemplate();

// 서버로 요청할 Header
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "KakaoAK " + "admin키!!!!!!!!!!!!");
headers.add("Accept", MediaType.APPLICATION_JSON_UTF8_VALUE);
headers.add("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8");

// 서버로 요청할 Body
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("cid", "TC0ONETIME");
params.add("partner_order_id", "1234");
params.add("partner_user_id", "bora");
params.add("item_name", item_name);
params.add("quantity", "1");
params.add("total_amount", String.valueOf(total_amount));
params.add("tax_free_amount", "100");
params.add("approval_url", "http://localhost:8080/pay/kakaoPaySuccess");
params.add("cancel_url", "http://localhost:8080/pay/kakaoPayCancel");
params.add("fail_url", "http://localhost:8080/pay/kakaoPaySuccessFail");

HttpEntity<MultiValueMap<String, String>> body = new HttpEntity<MultiValueMap<String, String>>(params, headers);

try {
kakaoPayReadyVO = restTemplate.postForObject(new URI(HOST + "/v1/payment/ready"), body, KakaoPayReadyVO.class);

//log.info("" + kakaoPayReadyVO);

return kakaoPayReadyVO;

} catch (RestClientException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}

return kakaoPayReadyVO;

}



//결제 승인
public KakaoPayApprovalVO kakaoPayInfo(String pg_token) {

log.info("KakaoPayInfoVO............................................");
log.info("-----------------------------");

RestTemplate restTemplate = new RestTemplate();

// 서버로 요청할 Header
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "KakaoAK " + "admin키!!!!!!!!!!!!");
headers.add("Accept", MediaType.APPLICATION_JSON_UTF8_VALUE);
headers.add("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8");

// 서버로 요청할 Body
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("cid", "TC0ONETIME");
params.add("tid", kakaoPayReadyVO.getTid());
params.add("partner_order_id", "1234");
params.add("partner_user_id", "bora");
params.add("pg_token", pg_token);
params.add("total_amount", "54321");

HttpEntity<MultiValueMap<String, String>> body = new HttpEntity<MultiValueMap<String, String>>(params, headers);

try {
kakaoPayApprovalVO = restTemplate.postForObject(new URI(HOST + "/v1/payment/approve"), body, KakaoPayApprovalVO.class);
log.info("" + kakaoPayApprovalVO);

return kakaoPayApprovalVO;

} catch (RestClientException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}

return null;
}



// 결제 환불
public KakaoCancelResponse kakaoCancel() {

log.info("service kakaoCancel............................................");

// 카카오페이 요청
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.add("cid", "TC0ONETIME");
//parameters.add("tid", "환불할 결제 고유 번호");
parameters.add("tid", "T47592500dc40ec901d3");
parameters.add("cancel_amount", "54321");
parameters.add("cancel_tax_free_amount", "100");
parameters.add("cancel_vat_amount", "0");
// parameters.add("cancel_available_amount", "4000");

// 파라미터, 헤더
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(parameters, this.getHeaders());

// 외부에 보낼 url
RestTemplate restTemplate = new RestTemplate();

KakaoCancelResponse cancelResponse = restTemplate.postForObject(
"https://kapi.kakao.com/v1/payment/cancel",
requestEntity,
KakaoCancelResponse.class);

return cancelResponse;
}

private HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();

String auth = "KakaoAK " + "admin키!!!!!!!!!!!!";

httpHeaders.set("Authorization", auth);
httpHeaders.set("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

return httpHeaders;
}

}

 


 
이전 VO는 여기 참고!
 

 

Day105_230526_스프링부트 카카오 페이 결제하기 (공부중...)

일단 따라해서 구현이 되긴 되었는데... DB 저장하는 것과 실제 내 카드로 결제되게 해봐야겠다..!!! HTML 참고로 html은 pay 폴더 안에 있음! kakaoPay.html 결제화면 카카오페이 결제 kakaoPaySuccess.html 카

boracoding.tistory.com

 

 

 

 

 

최종값을 이렇게 받아오는 상태이다

{"aid":"A475930662ea0baeabeb","tid":"T47592500dc40ec901d3","cid":"TC0ONETIME",
"status":"CANCEL_PAYMENT","partner_order_id":"1234","partner_user_id":"bora",
"payment_method_type":"CARD","amount":{"total":54321,"tax_free":100,"vat":4929,
"point":0,"discount":0},"approved_cancel_amount":{"total":54321,"tax_free":100,
"vat":4929,"point":0,"discount":0,"green_deposit":0},
"canceled_amount":{"total":54321,"tax_free":100,"vat":4929,"point":0,"discount":0,"green_deposit":0}
,"cancel_available_amount":{"total":0,"tax_free":0,"vat":0,"point":0,"discount":0,"green_deposit":0},
"item_name":"배고프다","item_code":null,"quantity":1,"created_at":"2023-05-30T15:06:08",
"approved_at":"2023-05-30T15:06:35","canceled_at":"2023-05-30T15:09:10","payload":null}