https://developers.kakao.com/console/app
카카오계정
accounts.kakao.com
카카오 디벨로퍼에 카카오 아이디로 로그인
https://developers.kakaopay.com/docs/payment/online/single-payment
카카오페이 | 개발자센터
새로운 기회와 가치를 함께 만들어봐요
developers.kakaopay.com
위 api를 적용해 보자.
일단 카카오페이 컨트롤러를 만듦.
그리코 카카오 결제 넣을 jsp 페이지 만들어서 연결함.
그리고 ajax로 보내기 전 데이터 전처리 (및 확인) 을 해줌.
$("#btnPay").on("click", function() {
$("#btnPay").prop("disabled",true);
if($.trim($("#itemCode").val().length<=0)){
alert("상품코드를 입력하세요.");
$("#itemCode").val("");
$("#itemCode").focus();
$("#btnPay").prop("disabled",false);
return;
}
if($.trim($("#itemName").val().length<=0)){
alert("상품명을 입력하세요");
$("#itemName").val("");
$("#itemName").focus();
$("#btnPay").prop("disabled",false);
return;
}
if($.trim($("#quantity").val().length<=0)){
alert("수량을 입력하세요.");
$("#quantity").val("");
$("#quantity").focus();
$("#btnPay").prop("disabled",false);
return;
}
//수량에 숫자가 아닌 다른 문자를 적었을 때 처리.
if(!icia.common.isNumber($("#quantity").val())){
alert("수량은 숫자만 입력 가능합니다.");
$("#quantity").val("");
$("#quantity").focus();
$("#btnPay").prop("disabled",false);
return;
}
if($.trim($("#totalAmount").val().length<=0)){
alert("금액을 입력하세요.");
$("#totalAmount").val("");
$("#totalAmount").focus();
$("#btnPay").prop("disabled",false);
return;
}
if(!icia.common.isNumber($("#totalAmount").val())){
alert("금액은 숫자만 입력 가능합니다.");
$("#totalAmount").val("");
$("#totalAmount").focus();
$("#btnPay").prop("disabled",false);
return;
}
컨트롤러에 매핑함
@RequestMapping (value="/kakao/payReady", method=RequestMethod.POST)
@ResponseBody
public Response<Object> payReady (HttpServletRequest request, HttpServletResponse response){
Response<Object> ajaxRes = new Response<Object>();
return ajaxRes;
}
}
여기까지 해놓고 카카오페이지 개발자 페이지를 띁어보자.
우리가 봐야 할 것은 테스트 결제 코드(가맹점 코드는 카카오 측에서 만들어서 주는 코드.)다.
페이로드에 필요한것들 설명해줌.
required 에 o 되어 있는 것들은 필수적으로 있어야 하는 것. x는 선택.
예제도 잘 나와 있으니 위 링크는 필수적으로 잘 읽어보고 시작해보자.
우선 카카오에서 필요로 하는 정보를 담을 모델을 정의해줌.
package com.sist.web.model;
import java.io.Serializable;
public class KakaoPayOrder implements Serializable{
private static final long serialVersionUID = 1L;
private String partnerOrderId; //String O 가맹점 주문번호, 최대 100자
private String partnerUserId; //String O 가맹점 회원 id, 최대 100자
private String itemName; //String O 상품명, 최대 100자
private String itemCode; //String X 상품코드, 최대 100자
private int quantity; //Integer O 상품 수량
private int totalAmount; //Integer O 상품 총액
private int taxFreeAmount; //Integer O 상품 비과세 금액
private int vatAmount; //Integer X 상품 부가세 금액(값을 보내지 않을 경우 다음과 같이 VAT 자동 계산(상품총액 - 상품 비과세 금액)/11 : 소숫점 이하 반올림)
private String tId; //결제 고유번호
private String pgToken; //결제 승인 요청을 인증하는 토큰
//사용자 결제 수단 선택 완료시, approval_url로 redirection 했을 때 pg_token을 query String으로 변경.
}
이렇게 정의함(생성자, getter setter 생략)
그 다음 다시 컨트롤러로 가서, 필요한 정보를 받아서 객체에 세팅해줌.
@RequestMapping (value="/kakao/payReady", method=RequestMethod.POST)
@ResponseBody
public Response<Object> payReady (HttpServletRequest request, HttpServletResponse response){
Response<Object> ajaxRes = new Response<Object>();
String orderId = StringUtil.uniqueValue(); //자체 주문번호
String userId = CookieUtil.getHexValue(request, AUTH_COOKIE_NAME);
String itemCode= HttpUtil.get(request, "itemCode","");
String itemName = HttpUtil.get(request, "itemName","");
int quantity = HttpUtil.get(request, "quantity", 0);
int totalAmount = HttpUtil.get(request, "totalAmount", 0);
int taxFreeAmount = HttpUtil.get(request, "taxFreeAmount", 0);
int vatAmount = HttpUtil.get(request, "vatAmount", 0);
KakaoPayOrder kakaoPayOrder = new KakaoPayOrder();
kakaoPayOrder.setPartnerOrderId(orderId);
kakaoPayOrder.setPartnerUserId(userId);
kakaoPayOrder.setItemCode(itemCode);
kakaoPayOrder.setItemName(itemName);
kakaoPayOrder.setQuantity(quantity);
kakaoPayOrder.setTotalAmount(totalAmount);
kakaoPayOrder.setTaxFreeAmount(taxFreeAmount);
kakaoPayOrder.setVatAmount(vatAmount);
return ajaxRes;
}
카카오페이랑 보내고 받는 서비스(Service) 만들고, env.xml에 카카오페이 추가하기.
<!-- ########## 카카오페이 시작 ########## -->
<entry key="kakao.pay.host">https://kapi.kakao.com</entry>
<entry key="kakao.pay.admin.key">본인이 할당받은 admin key</entry>
<entry key="kakao.pay.cid">TC0ONETIME</entry> <!-- 가맹점 코드(10자) 테스트 결제시 사용 -->
<entry key="kakao.pay.ready.url">/v1/payment/ready</entry>
<entry key="kakao.pay.approve.url">/v1/payment/approve</entry>
<entry key="kakao.pay.success.url">http://hiboard.sist.co.kr:8088/kakao/paySuccess</entry>
<entry key="kakao.pay.cancel.url">http://hiboard.sist.co.kr:8088/kakao/payCancel</entry><!-- 결제 취소시 url -->
<entry key="kakao.pay.fail.url">http://hiboard.sist.co.kr:8088/kakao/payFail</entry>
<!-- ########## 카카오페이 종료 ########## -->
그리고 실제로 사용할 service.java에다가 @Value 어노테이션으로 상수 정의해준다.
//카카오페이 호스트
@Value("#{env['kakao.pay.host']}")
private String KAKAO_PAY_HOST;
//관리자 키
@Value("#{env['kakao.pay.admin.key']}")
private String KAKAO_PAY_ADMIN_KEY;
//가맹점 코드
@Value("#{env['kakao.pay.cid']}")
private String KAKAO_PAY_CID;
//결제 url
@Value("#{env['kakao.pay.ready.url']}")
private String KAKAO_PAY_READY_URL;
//카카오페이 결제 요청 url
@Value("#{env['kakao.pay.approve.url']}")
private String KAKAO_PAY_APPROVE_URL;
//카카오페이 결제 성공 url
@Value("#{env['kakao.pay.success.url']}")
private String KAKAO_PAY_SUCCESS_URL;
//카카오페이 결제 취소 url
@Value("#{env['kakao.pay.cancel.url']}")
private String KAKAO_PAY_CANCEL_URL;
//카카오페이 결제 실패 url
@Value("#{env['kakao.pay.fail.url']}")
private String KAKAO_PAY_FAIL_URL;
*Sample 페이지에는 이런 다소 번거로운 과정 없이 바로 사용했는데, 의존성을 낮추기 위해 env.xml 파일을 활용하는 것 같다.
이제 카카오 디밸로퍼에 내가 카카오페이를 사용할 도메인을 등록해줌.
kakao developer > 내 애플리케이션>플랫폼 등록 에서 도메인 등록할 수 있음.
객체 하나 더 정의함...
public class KakaoPayReady implements Serializable{
private static final long serialVersionUID = 1L;
private String tid; //String 결제 고유 번호, 20자
private String next_redirect_app_url; //String 요청한 클라이언트(Client)가 모바일 앱일 경우 카카오톡 결제 페이지 Redirect URL
private String next_redirect_mobile_url; //String 요청한 클라이언트가 모바일 웹일 경우 카카오톡 결제 페이지 Redirect URL
private String next_redirect_pc_url; //String 요청한 클라이언트가 PC 웹일 경우 카카오톡으로 결제 요청 메시지(TMS)를 보내기 위한 사용자 정보 입력 화면 Redirect URL
private String android_app_scheme; //String 카카오페이 결제 화면으로 이동하는 Android 앱 스킴(Scheme) - 내부 서비스용
private String ios_app_scheme; //String 카카오페이 결제 화면으로 이동하는 iOS 앱 스킴 - 내부 서비스용
private Date created_at;
정의한 객체를 매개변수로 써서 이제 진짜 서비스 로직 만듦. (여기서 사용한 정의하지 않은 객체들은 모두 Spring 프레임워크에서 편리한 rest api 사용을 위해 제공하는 객체들)
package com.sist.web.service;
import java.net.URI;
import java.net.URISyntaxException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import com.sist.web.model.KakaoPayOrder;
import com.sist.web.model.KakaoPayReady;
@Service("KaokaoPayService")
public class KakaoPayService {
private static Logger logger = LoggerFactory.getLogger(KakaoPayService.class);
//카카오페이 호스트
@Value("#{env['kakao.pay.host']}")
private String KAKAO_PAY_HOST;
//관리자 키
@Value("#{env['kakao.pay.admin.key']}")
private String KAKAO_PAY_ADMIN_KEY;
//가맹점 코드
@Value("#{env['kakao.pay.cid']}")
private String KAKAO_PAY_CID;
//결제 url
@Value("#{env['kakao.pay.ready.url']}")
private String KAKAO_PAY_READY_URL;
//카카오페이 결제 요청 url
@Value("#{env['kakao.pay.approve.url']}")
private String KAKAO_PAY_APPROVE_URL;
//카카오페이 결제 성공 url
@Value("#{env['kakao.pay.success.url']}")
private String KAKAO_PAY_SUCCESS_URL;
//카카오페이 결제 취소 url
@Value("#{env['kakao.pay.cancel.url']}")
private String KAKAO_PAY_CANCEL_URL;
//카카오페이 결제 실패 url
@Value("#{env['kakao.pay.fail.url']}")
private String KAKAO_PAY_FAIL_URL;
public KakaoPayReady kakaoPayReady(KakaoPayOrder order) {
KakaoPayReady ready = new KakaoPayReady();
if(order!=null) {
//RestTemplate = 스프링에서 지원하는 내장 클래스 객체로, 간편하게 rest 방식 API 호출 가능
RestTemplate restTemplet = new RestTemplate();
//서버로 요청할 헤더 (스프링에서 지원해 줌)
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization","KakaoAK"+KAKAO_PAY_ADMIN_KEY);
headers.add("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE+";charset=utf-8");
//서버로 요청할 바디(다형성)
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("cid",KAKAO_PAY_CID);
params.add("partner_order_id", order.getPartnerOrderId());
params.add("partner_user_id", order.getPartnerUserId());
params.add("item_name", order.getItemName());
params.add("item_code", order.getItemCode());
params.add("quantity", String.valueOf(order.getQuantity()));
params.add("total_amount", String.valueOf(order.getTotalAmount()));
params.add("tax_free_amount",String.valueOf(order.getTaxFreeAmount()));
params.add("approval_url", KAKAO_PAY_SUCCESS_URL);
params.add("cancel_url", KAKAO_PAY_CANCEL_URL);
params.add("fail_url", KAKAO_PAY_FAIL_URL);
//요청하기 위해서 header와 body 합치기
//Spring fraimwork에서 제공하는 HttpEntity class에 header와 body 합치기
HttpEntity<MultiValueMap<String, String>> body =
new HttpEntity<MultiValueMap<String,String>>(params,headers);
try {
//postForObject 메서드는 POST 요청을 보내고 객체로 결과를 반환받음.
ready = restTemplet.postForObject(new URI(KAKAO_PAY_HOST+KAKAO_PAY_READY_URL), body, KakaoPayReady.class);
if(ready!=null) {
order.settId(ready.getTid());
logger.debug("!!!!!!!!!!!ready.getTid:"+ready.getTid());
logger.debug("!!!!!!!!!!![kakaoPayService] ready:"+ready);
}
}
catch (URISyntaxException e) {
logger.error("[kakaoService]kakaoPayReady URISyntaxException",e);
}
}
else {
logger.error("[kakaoPayService] kakaoPayOrder is null");
}
return ready;
}
}
RestTemplete : rest 형식 api 호출에 필요함.
MultivalueMap, HttpEntity : 각각 body, header+body 합쳐주는 거
postForObject : post로 요청을 보내고 객체로 결과를 받아옴
URI:
- URI 클래스: URI 객체를 생성하고 관련된 기능(상대적, 절대적 URI 처리, URI 구성 요소 가져오기 등)을 제공합니다.
- 출처: https://wikidocs.net/207550
URL, URI
## **URL (Uniform Resource Locator)** **`URL`**은 웹 리소스에 대한 참조로 사용되며, 해당 리소스의 위치와 사용할 프로토콜을 나타냅니다. …
wikidocs.net
uri 때문에 예외처리 해줘야 함.
그리고 서비스 호출하고 결과 받는 컨트롤러 설계하기
@RequestMapping (value="/kakao/payReady", method=RequestMethod.POST)
@ResponseBody
public Response<Object> payReady (HttpServletRequest request, HttpServletResponse response){
Response<Object> ajaxRes = new Response<Object>();
String orderId = StringUtil.uniqueValue(); //자체 주문번호
String userId = CookieUtil.getHexValue(request, AUTH_COOKIE_NAME);
String itemCode= HttpUtil.get(request, "itemCode","");
String itemName = HttpUtil.get(request, "itemName","");
int quantity = HttpUtil.get(request, "quantity", 0);
int totalAmount = HttpUtil.get(request, "totalAmount", 0);
int taxFreeAmount = HttpUtil.get(request, "taxFreeAmount", 0);
int vatAmount = HttpUtil.get(request, "vatAmount", 0);
KakaoPayOrder kakaoPayOrder = new KakaoPayOrder();
kakaoPayOrder.setPartnerOrderId(orderId);
kakaoPayOrder.setPartnerUserId(userId);
kakaoPayOrder.setItemCode(itemCode);
kakaoPayOrder.setItemName(itemName);
kakaoPayOrder.setQuantity(quantity);
kakaoPayOrder.setTotalAmount(totalAmount);
kakaoPayOrder.setTaxFreeAmount(taxFreeAmount);
kakaoPayOrder.setVatAmount(vatAmount);
KakaoPayReady ready= kakaoPayService.kakaoPayReady(kakaoPayOrder);
if(ready!=null) {
logger.debug("[KakaoPayController] payReady:"+ready);
//service에서 kakaopayReady와 중복 (근데 이걸 왜 중복해서 하냐고)
kakaoPayOrder.settId(ready.getTid());
JsonObject json = new JsonObject();
json.addProperty("orderId", orderId);
json.addProperty("tId",ready.getTid());
json.addProperty("appUrl", ready.getNext_redirect_app_url());
json.addProperty("mobileUrl", ready.getNext_redirect_mobile_url());
json.addProperty("pcUrl", ready.getNext_redirect_pc_url());
ajaxRes.setResponse(0, "success", json);
}
else {
ajaxRes.setResponse(-1, "fail", null);
}
return ajaxRes;
}
ajax로 jsp 단에서 결과 받음
icia.ajax.post({
url:"/kakao/payReady",
data:{
itemCode:$("#itemCode").val(),
itemName:$("#itemName").val(),
quantity:$("#quantity").val(),
totalAmount:$("#totalAmount").val(),
success:function(response){
icia.common.log(response)
if(response.code==0){
var orderId = response.data.orderId;
var tId = response.data.tId;
var pcUrl = response.data.pcUrl;
$("#orderId").val(orderId);
$("#tId").val(tId);
$("#pcUrl").val(pcUrl);
var win = window.open('','kakaoPopUp','toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=no,width=540,height=700,left=100,top=100');
$("#kakaoForm").submit();
}
else{
alert("오류가 발생하였습니다.");
$("#btnPay").prop("disabled",false);
}
},
error:function(error){
icia.common.error(error);
$("#btnPay").prop("disabled",false);
}
이렇게 해준 다음, ifram으로 새로운 창을 띄워서 보여줌 (controller로 jsp 연결)
아래 네 개는 model 로 보내주
model.addAttribute("pcUrl",pcUrl);
model.addAttribute("orderId", orderId);
model.addAttribute("tId", tId);
model.addAttribute("userId",userId);
'Study > Spring' 카테고리의 다른 글
[스프링] org.apache.ibatis.binding.BindingException (0) | 2024.03.01 |
---|---|
[스프링] 카카오페이 단건 결제 401 [no body] error(에러) (1) | 2024.02.27 |
[STS] 콘솔 경고 (CONSOLE PERFORMANCE WARNING) Word wrap enabled. (2) | 2024.02.26 |
[스프링] 새로운 스프링 프로젝트 세팅하기 (0) | 2024.02.22 |
[스프링] 0215 게시글 수정/삭제 (0) | 2024.02.15 |
댓글