Spring
[Spring] Day16 결제 API (Code)
jnk1m
2022. 4. 7. 16:07
1.
package com.board.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.board.service.KakaoPayService;
@Controller
@RequestMapping("/kakao/*")
public class KakaoController {
@Autowired
private KakaoPayService service;
@GetMapping("kakaoPay")
public void kakaoPayGet() {
System.out.println("kakaoPay 요청");
}
@PostMapping("kakaoPay")
public String kakaoPay() {
System.out.println("kakao pay post 요청 ");
//자바를 통해서 kakao api에서 제공한 주소로 http 요청하자
String responseURL = service.kakaoPayReady();
//서비스 처리 결과 중 카카오 페이지 준비 결과에 따른 이동할 페이지 주소로 redirect 해주기
return "redirect:" + responseURL; //주소 연결
}
//준비 요청 성공 시 실행될 요청 메서드
//파라미터로 넘어오는 pg_token을 받고
@GetMapping("kakaoPaySuccess")
public void kakaoPaySuccess(@RequestParam("pg_token") String pg_token, Model model) {
System.out.println("kakaoPaySuccess");
System.out.println("pg_token" + pg_token);
//결제 승인 요청 (pg_token 전달하면서)
model.addAttribute("payInfo", service.kakaoPayApprove(pg_token));
}
}
2. /kakaoPay
package com.board.service;
import com.board.domain.KakaoPayApprovalVO;
public interface KakaoPayService {
//결제 준비 처리
public String kakaoPayReady();
//결제 승인 요청 처리
public KakaoPayApprovalVO kakaoPayApprove(String pg_token);
}
-
package com.board.service;
import java.net.URI;
import java.net.URISyntaxException;
import org.springframework.beans.factory.annotation.Autowired;
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 com.board.domain.KakaoPayApprovalVO;
import com.board.domain.KakaoPayReadyVO;
@Service
public class KakaoPayServiceImpl implements KakaoPayService {
//HTTP RESP 요청해주는 기능을 가진 클래스: 빈으로 등록해서 얻어다 쓰자.
@Autowired
private RestTemplate restTemplate;
private KakaoPayReadyVO readyVO ;
@Override
public String kakaoPayReady() {
System.out.println("!!ready!!");
//카카오 결제 준비 처리 요청
//1. 요청 시 필요한 헤더 정보
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "KakaoAK "+ "bc8dcbd5277002466c5eca4bc49f2f21");
//카카오 서버에게 난 JSON 형태로 응답 받고 싶어~라는 정보를 주는 것.
headers.add("Accept", MediaType.APPLICATION_JSON_UTF8_VALUE);
headers.add("Content-type", MediaType.APPLICATION_FORM_URLENCODED_VALUE+";charset=utf-8"); //application/x-www-form-urlencoded;charset=utf-8와 동일한 것
//2. 카카오 서버로 요청할 때 보내줄 데이터 BODY 준비
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("cid", "TC0ONETIME"); //가맹점 코드 (테스트는 TC0ONETIME 으로 사용)
params.add("partner_order_id", "202204070001"); //우리 사이트에서 생성한 주문 고유 번호
params.add("partner_user_id","pikachu"); //결제하는 사용자 id
params.add("item_name", "chocochip cookies"); //상품명
params.add("item_code", "P00012"); //상품 코드
params.add("quantity","1"); //상품 수량
params.add("total_amount","1000"); //상품 총액
params.add("tax_free_amount","0"); //비과세 상품 금액
params.add("approval_url", "http://localhost:8080/kakao/kakaoPaySuccess"); //결제 성공시 redirect할 주소
params.add("cancel_url", "http://localhost:8080/kakao/kakaoPayCancel"); //결제 취소시 redirect할 주소
params.add("fail_url", "http://localhost:8080/kakao/kakaoPayFail"); //결제 실패시 redirect할 주소
//헤더와 바디 합치기
HttpEntity<MultiValueMap<String, String>> body
= new HttpEntity<MultiValueMap<String,String>>(params, headers);
//3. 요청
//매개변수: 1. 요청 주소 2. 요청 headers + body 3. 응답 받을 타입
try {
readyVO =
restTemplate.postForObject(new URI("https://kapi.kakao.com/v1/payment/ready"),body, KakaoPayReadyVO.class);
System.out.println("!! KakaoPayReadyVO 출력 확인: "+ readyVO);
//응답으로 받은 redirect: 이동할 주소 리턴
return readyVO.getNext_redirect_pc_url();
} catch (Exception e) {
e.printStackTrace();
}
//위에서 정상적으로 처리 안되면 이동할 주소 예시로 넣기
return "/mypage/cart"; //지금은 임의로 작성
}
//결제 승인 처리 메서드
@Override
public KakaoPayApprovalVO kakaoPayApprove(String pg_token) {
//헤더 정보
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "KakaoAK "+ "bc8dcbd5277002466c5eca4bc49f2f21");
headers.add("Accept", MediaType.APPLICATION_JSON_UTF8_VALUE);
headers.add("Content-type", MediaType.APPLICATION_FORM_URLENCODED_VALUE+";charset=utf-8"); //application/x-www-form-urlencoded;charset=utf-8와 동일한 것
//바디 파라미터
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("cid", "TC0ONETIME");
params.add("tid", readyVO.getTid());
params.add("partner_order_id", "202204070001");
params.add("partner_user_id","pikachu");
params.add("pg_token", pg_token);
params.add("total_amount","1000");
//헤더, 바디 합치기
HttpEntity<MultiValueMap<String, String>> body
= new HttpEntity<MultiValueMap<String,String>>(params, headers);
//요청
try {
KakaoPayApprovalVO approvalVO =
restTemplate.postForObject(new URI("https://kapi.kakao.com/v1/payment/approve"),
body, KakaoPayApprovalVO.class);
System.out.println("!approvalVO! "+ approvalVO);
return approvalVO; //응답으로 받은 정보 리턴
}catch(Exception e) {
e.printStackTrace();
}
return null; //위에 처리 안되면 null 리턴
}
}
3.
package com.board.domain;
import java.util.Date;
import lombok.Data;
@Data
public class KakaoPayApprovalVO {
private String aid;
private String tid;
private String cid;
private String sid;
private String partner_order_id;
private String partner_user_id;
private String payment_method_type;
private AmountVO amount;
private CardInfoVO card_info;
private String item_name;
private String item_code;
private Integer quantity;
private Date created_at;
private Date approved_at;
private String payload;
}
4.
package com.board.domain;
import java.util.Date;
import lombok.Data;
//카카오 결제 준비에서 요청하고 응답 받을 데이터들의 VO
@Data
public class KakaoPayReadyVO {
private String tid; //결제 고유 번호
private String next_redirect_app_url; //모바일 앱인 경우 결제 페이지 redirect url
private String next_redirect_mobile_url; // 모바일 웹인 경우
private String next_redirect_pc_url; //pc 웹일 경우 요청 메세지 보내기 위한 사용자 정보 입력 화면 redirect url
private Date created_at;
}
5.
package com.board.domain;
import lombok.Data;
@Data
public class AmountVO {
private Integer total;
private Integer tax_free;
private Integer vat;
private Integer point;
private Integer discount;
}
6.
package com.board.domain;
import lombok.Data;
@Data
public class CardInfoVO {
private String purchase_corp, purchase_corp_code;
private String issuer_corp, issuer_corp_code;
private String kakaopay_purchase_corp, kakaopay_purchase_corp_code;
private String kakaopay_issuer_corp, kakaopay_issuer_corp_code;
private String bin, card_type, install_month;
private String approved_id, card_mid, interest_free_install, card_item_code;
}
7.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>카카오 테스트</h1>
<form action="/kakao/kakaoPay" method="post">
<%--결제할 상품과 금액 등의 정보 추가 --%>
<button>결제하기</button>
</form>
</body>
</html>
8.
package com.board.domain;
import java.util.Date;
import lombok.Data;
@Data
public class KakaoPayApprovalVO {
private String aid;
private String tid;
private String cid;
private String sid;
private String partner_order_id;
private String partner_user_id;
private String payment_method_type;
private AmountVO amount;
private CardInfoVO card_info;
private String item_name;
private String item_code;
private Integer quantity;
private Date created_at;
private Date approved_at;
private String payload;
}
9.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>success</title>
</head>
<body>
<h1>카카오 페이 성공</h1>
<%--결제 승인 요청 후 응답 받는 내용 확인 차 view에 출력 --%>
<p>요청 고유 번호: ${payInfo.aid}</p>
<p>결제 고유 번호: ${payInfo.tid}</p>
<p>가맹점 코드: ${payInfo.cid}</p>
<p>가맹점 주문 코드: ${partner_order_id}</p>
<p>가맹점 회원: ${partner_user_id}</p>
<p>결제 수단: ${payment_method_type}</p>
<p>결제 금액: ${payInfo.amount}</p>
</body>
</html>