코딩 노트
결제04 - 마무리 본문
payment-mapper.xml 구문 추가
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="payment">
<!-- 결제 대표정보 등록 -->
<select id="sequence" resultType="int">
select payment_seq.nextval from dual
</select>
<insert id="save">
insert into payment(
payment_no, payment_member, payment_tid,
payment_name, payment_price, payment_remain
)
values(
#{paymentNo}, #{paymentMember}, #{paymentTid},
#{paymentName}, #{paymentPrice}, #{paymentRemain}
)
</insert>
<!-- 결제 상세정보 등록 -->
<insert id="saveDetail">
insert into payment_detail (
payment_detail_no, payment_detail_origin, payment_detail_product,
payment_detail_product_name, payment_detail_product_price,
payment_detail_product_qty, payment_detail_status
)
values(
payment_detail_seq.nextval,
#{paymentDetailOrigin}, #{paymentDetailProduct}, #{paymentDetailProductName},
#{paymentDetailProductPrice}, #{paymentDetailProductQty},
'완료'
)
</insert>
<!-- 결제 대표정보만 조회(사용자별/전체) -->
<select id="list" resultType="PaymentDto">
select * from payment order by payment_no asc
</select>
<!--
결제 대표정보와 상세정보를 계층형으로 조회
- resultType은 필드명으로 데이터베이스 조회 결과를 매핑하여 처리한다
- 지금과 같이 복잡한 구조는 처리가 불가능하다
- resultMap 항목을 선언하여 수동으로 매핑하도록 처리한다
- association 은 객체를 의미
- collection 은 리스트를 의미
- property는 항목명(필드명)을 의미
- column은 DB의 조회결과 열명을 의미
-->
<resultMap type="PaymentListVO" id="paymentListVO">
<!-- 일단 조회 결과는 내가 지정한 객체에 필드에 맞게 배치해라! -->
<association property="paymentDto">
<result column="payment_no" property="paymentNo"/>
<result column="payment_member" property="paymentMember"/>
<result column="payment_tid" property="paymentTid"/>
<result column="payment_name" property="paymentName"/>
<result column="payment_price" property="paymentPrice"/>
<result column="payment_remain" property="paymentRemain"/>
<result column="payment_time" property="paymentTime"/>
</association>
<!--
List가 하나 있는데 구문 알려줄테니까 니가 알아서 조회해서 채워라!
- select에 적힌 구문을 부르면 내용을 채울 수 있을거야!
- 구문을 실행하는 데 필요한 값은 column에 적혀있는 항목이야!
- 이 저장소는 javaType에 적혀 있는 형태야!
- 이 저장소에 들어갈 수 있는 데이터는 ofType에 적혀 있는 형태야!
-->
<collection property="paymentDetailList" select="listDetail" column="payment_no"
javaType="java.util.List" ofType="PaymentDto">
<result column="payment_detail_no" property="paymentDetailNo"/>
<result column="payment_detail_origin" property="paymentDetailOrigin"/>
<result column="payment_detail_product" property="paymentDetailProduct"/>
<result column="payment_detail_product_name" property="paymentDetailProductName"/>
<result column="payment_detail_product_price" property="paymentDetailProductPrice"/>
<result column="payment_detail_product_qty" property="paymentDetailProductQty"/>
<result column="payment_detail_status" property="paymentDetailStatus"/>
</collection>
</resultMap>
<select id="listAll" resultMap="paymentListVO">
select * from payment
<if test="paymentMember != null">
where payment_member = #{paymentMember}
</if>
order by payment_no asc
</select>
<!-- 결제 상세정보를 조회(결제 대표번호별) -->
<select id="listDetail" resultType="PaymentDetailDto">
select * from payment_detail
where payment_detail_origin = #{paymentDetailOrigin}
order by payment_detail_no asc
</select>
<!-- payment 상세조회 -->
<select id="find" resultType="PaymentDto">
select * from payment where payment_no = #{paymentNo}
</select>
<!-- payment_detail 상세조회 -->
<select id="selectDetail" resultType="PaymentDetailDto">
select * from payment_detail where payment_detail_no = #{paymentDetailNo}
</select>
<!-- payment 잔여금액처리 -->
<update id="cancel">
update payment
set payment_remain = #{paymentRemain}
where payment_no = #{paymentNo}
</update>
<!-- payment_detail 취소처리 -->
<update id="cancelDetail">
update payment_detail
set payment_detail_status = '취소'
where payment_detail_no = #{paymentDetailNo}
</update>
<!-- 특정 payment_no에 속한 payment_detail에 대한 취소처리 -->
<update id="cancelDetailGroup">
update payment_detail
set payment_detail_status = '취소'
where payment_detail_origin = #{paymentDetailOrigin}
</update>
</mapper>
KakaoPayController 구문 추가
@Slf4j
@Controller
@RequestMapping("/pay")
public class KakaoPayController {
@Autowired
private KakaoPayService kakaoPayService;
@Autowired
private PaymentDao paymentDao;
@GetMapping("/test1")
public String test1() {
//return "/WEB-INF/views/pay/test1.jsp";
return "pay/test1";
}
@PostMapping("/test1")
public String test1(@ModelAttribute KakaoPayReadyRequestVO request,
HttpSession session) throws URISyntaxException {
request.setPartnerOrderId(UUID.randomUUID().toString());
KakaoPayReadyResponseVO response = kakaoPayService.ready(request);
//session에 flash value를 저장(잠시 쓰고 지우는 데이터)
//- 사용자를 거치지 않는 범위 내에서 사용해야 안전하게 쓸 수 있다
session.setAttribute("approve", KakaoPayApproveRequestVO.builder()
.partnerOrderId(request.getPartnerOrderId())
.partnerUserId(request.getPartnerUserId())
.tid(response.getTid())
.build());
return "redirect:" + response.getNextRedirectPcUrl();
}
@GetMapping("/test1/success")
public String test1Success(HttpSession session, @RequestParam String pg_token) throws URISyntaxException {
//session에 저장되어 있는 flash value를 꺼내어 pg_token을 추가한 뒤 승인 요청
KakaoPayApproveRequestVO request =
(KakaoPayApproveRequestVO) session.getAttribute("approve");
session.removeAttribute("approve");
request.setPgToken(pg_token);//토큰 추가
//결제 승인 요청
KakaoPayApproveResponseVO response = kakaoPayService.approve(request);
return "redirect:successResult";
}
@GetMapping("/test1/successResult")
public String successResult() {
//return "/WEB-INF/views/pay/successResult.jsp";
return "pay/successResult";
}
@GetMapping("/test1/detail")
public String detail(Model model, @RequestParam String tid) throws URISyntaxException {
KakaoPayDetailResponseVO response =
kakaoPayService.detail(KakaoPayDetailRequestVO.builder()
.tid(tid)
.build());
model.addAttribute("vo", response);
//return "/WEB-INF/views/pay/detail.jsp";
return "pay/detail";
}
@RequestMapping("/test1/cancel")
public String cancel(@ModelAttribute KakaoPayCancelRequestVO request) throws URISyntaxException {
KakaoPayCancelResponseVO response = kakaoPayService.cancel(request);
return "redirect:detail?tid="+request.getTid();
}
///////////////////////////////////////////////////////////////////////////////////////////
@Autowired
private ProductDao productDao;
@RequestMapping("/test2")
public String test2(Model model) {
model.addAttribute("list", productDao.selectList());
//return "/WEB-INF/views/pay2/home.jsp";
return "pay2/home";
}
@GetMapping("/test2/purchase")
public String purchase(HttpSession session, @RequestParam int productNo) throws URISyntaxException {
//상품정보 조회
ProductDto productDto = productDao.selectOne(productNo);
//상품정보를 이용하여 결제준비요청
KakaoPayReadyRequestVO request = KakaoPayReadyRequestVO.builder()
.itemName(productDto.getProductName())
.itemPrice(productDto.getProductPrice())
.partnerOrderId(UUID.randomUUID().toString())
.partnerUserId("testuser1")
.build();
KakaoPayReadyResponseVO response = kakaoPayService.ready(request);
//session에 flash value를 저장(잠시 쓰고 지우는 데이터)
//- 사용자를 거치지 않는 범위 내에서 사용해야 안전하게 쓸 수 있다
session.setAttribute("approve", KakaoPayApproveRequestVO.builder()
.partnerOrderId(request.getPartnerOrderId())
.partnerUserId(request.getPartnerUserId())
.tid(response.getTid())
.build());
session.setAttribute("productNo", productNo);
//결제페이지를 사용자에게 안내
return "redirect:"+response.getNextRedirectPcUrl();
}
//결제 성공
@GetMapping("/test2/purchase/success")
public String test2Success(HttpSession session, @RequestParam String pg_token) throws URISyntaxException {
//session에 저장되어 있는 flash value를 꺼내어 pg_token을 추가한 뒤 승인 요청
KakaoPayApproveRequestVO request =
(KakaoPayApproveRequestVO) session.getAttribute("approve");
int productNo = (int)session.getAttribute("productNo");
session.removeAttribute("approve");
session.removeAttribute("productNo");
request.setPgToken(pg_token);//토큰 추가
//결제 승인 요청
KakaoPayApproveResponseVO response = kakaoPayService.approve(request);
//결제 승인이 완료되었다면 DB에 결제 정보를 저장
int paymentNo = paymentDao.sequence();
paymentDao.insert(PaymentDto.builder()
.paymentNo(paymentNo)
.paymentMember(response.getPartnerUserId())
.paymentTid(response.getTid())
.paymentName(response.getItemName())
.paymentPrice(response.getAmount().getTotal())
.paymentRemain(response.getAmount().getTotal())
.build());
return "redirect:successResult";
}
@RequestMapping("/test2/purchase/successResult")
public String test2SuccessResult() {
return "pay2/successResult";
}
///////////////////////////////////////////////////////////////////////////////
@RequestMapping("/test3")
public String test3(Model model) {
model.addAttribute("list", productDao.selectList());
return "pay3/home";
}
//결제 확인화면
@GetMapping("/test3/purchase")
public String test3Purchase(@ModelAttribute PurchaseListVO listVO, Model model) {
List<PurchaseVO> purchaseList = listVO.getProduct();
List<PurchaseConfirmVO> confirmList = new ArrayList<>();//옮겨담을 리스트
int total = 0;
for(PurchaseVO vo : purchaseList) {//사용자가 선택한 번호와 수량의 목록을 반복하며
ProductDto productDto = productDao.selectOne(vo.getProductNo());//상품정보를 구한다
//vo와 dto를 신규객체로 만들어 추가
PurchaseConfirmVO confirmVO = PurchaseConfirmVO.builder()
.purchaseVO(vo).productDto(productDto)
.build();
confirmList.add(confirmVO);//화면에 출력할 데이터 추가
total += confirmVO.getTotal();//총 구매금액 합산
}
model.addAttribute("list", confirmList);
model.addAttribute("total", total);
return "pay3/purchase";
}
//결제 처리
@PostMapping("/test3/purchase")
public String test3Purchse(HttpSession session,
@ModelAttribute PurchaseListVO listVO) throws URISyntaxException {
log.debug("listVO = {}", listVO);
//listVO에 들어있는 product 항목들을 이용해서 결제 준비 요청 처리 후 결제 페이지로 안내
//- 결제이름은 대표 상품명 외 ?개 와 같이 작성
//- 결제금액은 모든 상품의 가격과 수량의 총합계
//- 결론적으로 만들어야 하는 데이터는 KakaoPayReadyRequestVO
KakaoPayReadyRequestVO request = kakaoPayService.convert(listVO);
String memberId = (String)session.getAttribute("name");
request.setPartnerUserId(memberId);
KakaoPayReadyResponseVO response = kakaoPayService.ready(request);
//session에 flash value를 저장(잠시 쓰고 지우는 데이터)
//- 사용자를 거치지 않는 범위 내에서 사용해야 안전하게 쓸 수 있다
session.setAttribute("approve", KakaoPayApproveRequestVO.builder()
.partnerOrderId(request.getPartnerOrderId())
.partnerUserId(request.getPartnerUserId())
.tid(response.getTid())
.build());//승인요청을 위한 준비데이터 --> 카카오페이
session.setAttribute("listVO", listVO);//구매한 상품의 번호와 수량 목록 --> 우리 DB
return "redirect:" + response.getNextRedirectPcUrl();
}
@GetMapping("/test3/purchase/success")
public String test3Success(HttpSession session, @RequestParam String pg_token) throws URISyntaxException {
//session에 저장한 flash value 추출 및 삭제
KakaoPayApproveRequestVO request =
(KakaoPayApproveRequestVO)session.getAttribute("approve");
PurchaseListVO listVO = (PurchaseListVO) session.getAttribute("listVO");
session.removeAttribute("approve");
session.removeAttribute("listVO");
request.setPgToken(pg_token);//토큰 설정
KakaoPayApproveResponseVO response = kakaoPayService.approve(request);//승인요청
//DB작업
//- 상품을 3개 구매했다면 payment 1회, payment_detail 3회의 insert가 필요(N+1)
//[1] 결제번호 생성
//int paymentNo = paymentDao.sequence();
int paymentNo = Integer.parseInt(response.getPartnerOrderId());
//[2] 결제정보 등록
paymentDao.insert(PaymentDto.builder()
.paymentNo(paymentNo)//결제고유번호
.paymentMember(response.getPartnerUserId())//결제자ID
.paymentTid(response.getTid())//PG사 거래번호
.paymentName(response.getItemName())//PG사 결제상품명
.paymentPrice(response.getAmount().getTotal())//총 결제액
.paymentRemain(response.getAmount().getTotal())//총 취소가능액
.build());
//[3] 상품 개수만큼 결제 상세정보를 등록
List<PurchaseVO> list = listVO.getProduct();
for(PurchaseVO vo : list) {
ProductDto productDto = productDao.selectOne(vo.getProductNo());//상품정보 조회
paymentDao.insertDetail(PaymentDetailDto.builder()
.paymentDetailOrigin(paymentNo)//상위결제번호
.paymentDetailProduct(vo.getProductNo())//상품번호 (vo, productDto)
.paymentDetailProductName(productDto.getProductName())//상품명 (productDto)
.paymentDetailProductPrice(productDto.getProductPrice())//상품가격 (productDto)
.paymentDetailProductQty(vo.getQty())//구매수량 (vo)
.build());
}
return "redirect:successResult";
}
@RequestMapping("/test3/purchase/successResult")
public String test3SuccessResult() {
return "pay3/successResult";
}
@RequestMapping("/test3/list")
public String test3list(Model model) {
model.addAttribute("list", paymentDao.selectList());
return "pay3/list";
}
@RequestMapping("/test3/list2")
public String test3list2(HttpSession session, Model model) {
String memberId = (String)session.getAttribute("name");
//model.addAttribute("list", paymentDao.selectTotalList());//전체내역
model.addAttribute("list", paymentDao.selectTotalListByMember(memberId));//나의내역
return "pay3/list2";
}
//[1] 결제 상세 번호로 PaymentDetailDto를 조회
//[2] 1번에서 조회한 PaymentDetailDto의 정보로 PaymentDto를 조회
//[3] 1번에서 취소금액을 알 수 있고, 2번에서 거래번호(tid)를 알 수 있다
//[4] 3번의 정보로 카카오페이 취소 요청을 보낸다
//[5] DB의 정보를 업데이트
// - 현재 항목에 대한 상태를 취소로 변경해야 한다(payment_detail)
// - 결제 대표 정보의 잔여 금액을 차감해야 한다(payment)
@RequestMapping("/test3/cancel")
public String test3cancel(@RequestParam int paymentDetailNo) throws URISyntaxException {
PaymentDetailDto paymentDetailDto = paymentDao.selectDetail(paymentDetailNo);//1
//if(paymentDetailDto.getPaymentDetailStatus().equals("취소")) {
if(paymentDetailDto == null || paymentDetailDto.isCanceled()) {
throw new NoTargetException();
}
PaymentDto paymentDto =
paymentDao.selectOne(paymentDetailDto.getPaymentDetailOrigin());//2
//3
KakaoPayCancelRequestVO request = KakaoPayCancelRequestVO.builder()
.tid(paymentDto.getPaymentTid())
.cancelAmount(
paymentDetailDto.getPaymentDetailProductPrice()//상품판매가
* paymentDetailDto.getPaymentDetailProductQty()//구매수량
)
.build();
//4
KakaoPayCancelResponseVO response = kakaoPayService.cancel(request);
//5
paymentDao.cancelDetail(paymentDetailNo);
paymentDao.cancel(PaymentDto.builder()
.paymentNo(paymentDto.getPaymentNo())//결제대표번호
.paymentRemain(response.getCancelAvailableAmount().getTotal())//잔여금액
.build());
return "redirect:list2";
}
//결제 그룹 전체 취소
//[1] 전달받은 paymentNo로 PaymentDto를 조회
// - 잔여금액이 0이라면 차단
//[2] 1번에서 거래번호(tid)와 잔여금액을 꺼내서 카카오페이에 취소 요청을 전송
//[3] 취소가 성공하였다면 DB에서 다음의 항목을 수정
// - payment에서 잔여금액을 0으로 변경
// - payment_detail에서 해당 payment_no에 대한 모든 상태를 취소로 변경
@RequestMapping("/test3/cancelAll")
public String test3cancelAll(@RequestParam int paymentNo) throws URISyntaxException {
//1
PaymentDto paymentDto = paymentDao.selectOne(paymentNo);
if(paymentDto == null || paymentDto.getPaymentRemain() == 0) {
throw new NoTargetException("이미 취소된 결제입니다");
}
//2
KakaoPayCancelRequestVO request = KakaoPayCancelRequestVO.builder()
.tid(paymentDto.getPaymentTid())//거래번호
.cancelAmount(paymentDto.getPaymentRemain())//잔여금액
.build();
KakaoPayCancelResponseVO response = kakaoPayService.cancel(request);
//3
paymentDao.cancel(PaymentDto.builder()
.paymentNo(paymentNo).paymentRemain(0)
.build());
paymentDao.cancelDetailGroup(paymentNo);
return "redirect:list2";
}
//결제취소와 결제실패의 경우에도 세션에 저장한 flash value를 삭제해야 한다
@RequestMapping("/test3/purchase/cancel")
public String test3cancel(HttpSession session) {
session.removeAttribute("approve");
session.removeAttribute("listVO");
return "취소했을때 보여줄 페이지";
}
@RequestMapping("/test3/purchase/fail")
public String test3fail(HttpSession session) {
session.removeAttribute("approve");
session.removeAttribute("listVO");
return "실패했을때 보여줄 페이지";
}
}
PaymentDao 구문 추가
public interface PaymentDao {
int sequence();
void insert(PaymentDto paymentDto);
List<PaymentDto> selectList();
PaymentDto selectOne(int paymentNo);
void insertDetail(PaymentDetailDto paymentDetailDto);
List<PaymentListVO> selectTotalList();
List<PaymentListVO> selectTotalListByMember(String paymentMember);
PaymentDetailDto selectDetail(int paymentDetailNo);
void cancelDetail(int paymentDetailNo);
void cancel(PaymentDto paymentDto);
void cancelDetailGroup(int paymentDetailOrigin);
}
PaymentDaoImpl 구문 추가
@Repository
public class PaymentDaoImpl implements PaymentDao{
@Autowired
private SqlSession sqlSession;
@Override
public int sequence() {
return sqlSession.selectOne("payment.sequence");
}
@Override
public void insert(PaymentDto paymentDto) {
sqlSession.insert("payment.save", paymentDto);
}
@Override
public void insertDetail(PaymentDetailDto paymentDetailDto) {
sqlSession.insert("payment.saveDetail", paymentDetailDto);
}
@Override
public List<PaymentDto> selectList() {
return sqlSession.selectList("payment.list");
}
@Override
public List<PaymentListVO> selectTotalList() {
return sqlSession.selectList("payment.listAll");
}
@Override
public PaymentDetailDto selectDetail(int paymentDetailNo) {
return sqlSession.selectOne("payment.selectDetail", paymentDetailNo);
}
@Override
public PaymentDto selectOne(int paymentNo) {
return sqlSession.selectOne("payment.find", paymentNo);
}
@Override
public void cancel(PaymentDto paymentDto) {
sqlSession.update("payment.cancel", paymentDto);
}
@Override
public void cancelDetail(int paymentDetailNo) {
sqlSession.update("payment.cancelDetail", paymentDetailNo);
}
@Override
public void cancelDetailGroup(int paymentDetailOrigin) {
sqlSession.update("payment.cancelDetailGroup", paymentDetailOrigin);
}
@Override
public List<PaymentListVO> selectTotalListByMember(String paymentMember) {
return sqlSession.selectList("payment.listAll", paymentMember);
}
}
HomeController 생성
@Controller
public class HomeController {
@RequestMapping("/")
public String home() {
return "home";
}
@RequestMapping("/login")
public String login(HttpSession session,
@RequestParam String memberId,
@RequestParam String memberPw) {
if(memberId.equals("testuser999") && memberPw.equals("Testuser999!")) {
session.setAttribute("name", memberId);
session.setAttribute("level", "VIP");
}
return "redirect:/";
}
@RequestMapping("/logout")
public String logout(HttpSession session) {
session.removeAttribute("name");
session.removeAttribute("level");
return "redirect:/";
}
}
KakaoPayServiceImpl 구문 추가
@Slf4j
@Service
public class KakaoPayServiceImpl implements KakaoPayService{
@Autowired
private KakaoPayProperties kakaoPayProperties;
@Autowired
private RestTemplate template;
@Autowired
private HttpHeaders headers;
@Autowired
private ProductDao productDao;
@Autowired
private PaymentDao paymentDao;
@Override
public KakaoPayReadyResponseVO ready(KakaoPayReadyRequestVO request) throws URISyntaxException {
//주소 설정
URI uri = new URI("https://kapi.kakao.com/v1/payment/ready");
//바디 설정
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("cid", kakaoPayProperties.getCid());
body.add("partner_order_id", request.getPartnerOrderId());
body.add("partner_user_id", request.getPartnerUserId());
body.add("item_name", request.getItemName());
body.add("quantity", "1");
body.add("total_amount", String.valueOf(request.getItemPrice()));
body.add("tax_free_amount", "0");
//현재 페이지 주소 계산
String path = ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString();
body.add("approval_url", path+"/success");
body.add("cancel_url", path+"/cancel");
body.add("fail_url", path+"/fail");
//요청 발송
HttpEntity entity = new HttpEntity(body, headers);
KakaoPayReadyResponseVO response =
template.postForObject(uri, entity, KakaoPayReadyResponseVO.class);
return response;
}
@Override
public KakaoPayApproveResponseVO approve(KakaoPayApproveRequestVO request) throws URISyntaxException {
URI uri = new URI("https://kapi.kakao.com/v1/payment/approve");
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("cid", kakaoPayProperties.getCid());
body.add("tid", request.getTid());
body.add("partner_order_id", request.getPartnerOrderId());
body.add("partner_user_id", request.getPartnerUserId());
body.add("pg_token", request.getPgToken());
HttpEntity entity = new HttpEntity(body, headers);
KakaoPayApproveResponseVO response =
template.postForObject(uri, entity, KakaoPayApproveResponseVO.class);
log.debug("결제 승인 완료 = {}", response.getTid());
return response;
}
@Override
public KakaoPayDetailResponseVO detail(KakaoPayDetailRequestVO request) throws URISyntaxException {
URI uri = new URI("https://kapi.kakao.com/v1/payment/order");
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("cid", kakaoPayProperties.getCid());
body.add("tid", request.getTid());
HttpEntity entity = new HttpEntity(body, headers);
KakaoPayDetailResponseVO response =
template.postForObject(uri, entity, KakaoPayDetailResponseVO.class);
return response;
}
@Override
public KakaoPayCancelResponseVO cancel(KakaoPayCancelRequestVO request) throws URISyntaxException {
URI uri = new URI("https://kapi.kakao.com/v1/payment/cancel");
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("cid", kakaoPayProperties.getCid());
body.add("tid", request.getTid());
body.add("cancel_amount", String.valueOf(request.getCancelAmount()));
body.add("cancel_tax_free_amount", "0");
HttpEntity entity = new HttpEntity(body, headers);
KakaoPayCancelResponseVO response =
template.postForObject(uri, entity, KakaoPayCancelResponseVO.class);
return response;
}
@Override
public KakaoPayReadyRequestVO convert(PurchaseListVO listVO) {
//구매목록 추출
List<PurchaseVO> list = listVO.getProduct();
//결제 사용할 정보를 저장할 변수들
String name = null;
int total = 0;
//구매목록을 모두 조사하여 상품정보 추출 후 필요한 항목을 계산
for(PurchaseVO vo : list) {
//vo 안에는 상품번호(productNo)와 구매수량(qty)가 있다
ProductDto dto = productDao.selectOne(vo.getProductNo());
if(name == null) {//이름이 없을때만 이름을 넣어라(최초 이름만 들어감)
name = dto.getProductName();
}
total += dto.getProductPrice() * vo.getQty();//상품가격과 수량을 곱해서 합산해라!
}
//구매 수량이 2개 이상이라면 이름에 "외 ?건"를 추가
if(list.size() >= 2) {
name += " 외 "+(list.size()-1)+"건";
}
//partner_order_id에 결제번호를 집어넣으면 충돌도 없고 좋지 않을까?
int paymentNo = paymentDao.sequence();
return KakaoPayReadyRequestVO.builder()
//.partnerOrderId(UUID.randomUUID().toString())
.partnerOrderId(String.valueOf(paymentNo))
.itemName(name)
.itemPrice(total)
.build();
}
}
PaymentDetailDto 구문 수정
@Data @Builder @NoArgsConstructor @AllArgsConstructor
public class PaymentDetailDto {
private int paymentDetailNo;//하위 결제번호
private int paymentDetailOrigin;//상위 결제번호
private int paymentDetailProduct;//구매상품번호
private String paymentDetailProductName;//구매상품명
private int paymentDetailProductPrice;//구매상품가격
private int paymentDetailProductQty;//구매상품수량
private String paymentDetailStatus;//구매상품상태(완료/취소)
public boolean isCanceled() {
return paymentDetailStatus.equals("취소");
}
}
error 패키지 생성 후 NoTargetException 파일 생성
package com.kh.spring21.error;
import lombok.NoArgsConstructor;
/**
* 대상이 없을 경우 발생하는 예외
* 1. Exception을 상속받는다
* - RuntimeException을 상속받으면 예외 전가를 작성하지 않아도 된다
* 2. 기본생성자와 메세지를 설정할 수 있는 생성자를 구현한다
*/
@NoArgsConstructor
public class NoTargetException extends RuntimeException {
public NoTargetException(String message) {
super(message);
}
}
successResult.jsp 수정
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<h1>결제가 완료되었습니다</h1>
<h2><a href="../list2">나의 결제 내역 보기</a></h2>
<h2><a href="${pageContext.request.contextPath}/pay/test3/list2">나의 결제 내역 보기</a></h2>
home.jsp 수정
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<h1>결제 내역(계층형)</h1>
<!-- 전체 목록 -->
<c:forEach var="paymentListVO" items="${list}">
<div style="border:1px solid black; margin:30px 0px; padding:10px">
<!-- 대표 정보 -->
<div style="border:1px solid blue; padding:10px">
${paymentListVO.paymentDto}
<hr>
<%-- 전체취소 버튼은 잔여금액이 있을 때만 출력되어야 한다 --%>
<c:if test="${paymentListVO.paymentDto.paymentRemain > 0}">
<a href="cancelAll?paymentNo=${paymentListVO.paymentDto.paymentNo}">전체(잔여) 금액 취소</a>
</c:if>
</div>
<!-- 상세 목록 정보 -->
<div style="border:1px solid red; padding:10px; margin-top:10px">
<c:forEach var="paymentDetailDto" items="${paymentListVO.paymentDetailList}">
<div style="border:1px solid gray; padding:10px; margin-top:10px;">
${paymentDetailDto}
<hr>
<%-- 취소가 가능한 상황일 경우에만 개별내역취소 버튼을 출력 --%>
<c:if test="${paymentDetailDto.paymentDetailStatus == '완료'}">
<a href="cancel?paymentDetailNo=${paymentDetailDto.paymentDetailNo}">
개별내역취소
</a>
</c:if>
</div>
</c:forEach>
</div>
</div>
</c:forEach>
'Spring' 카테고리의 다른 글
결제03 - 수량추가 + 조회 + 단건취소 (0) | 2023.10.26 |
---|---|
결제02 - 단건결제 (0) | 2023.10.25 |
결제01 - (카카오페이 api) + 카멜케이스 변환 (0) | 2023.10.24 |
DM을 어떻게 보내는가 (0) | 2023.10.23 |
웹소켓 - 실시간 채팅, 반응형 디자인 (0) | 2023.10.23 |