코딩 노트
결제03 - 수량추가 + 조회 + 단건취소 본문
구매하기를 누르면 form을 만들어서 체크된 상품번호와 그 번호의 수량을 찾아서 넘어가게 만들 것이다.
pay3 폴더 생성 후 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>
<div class="product-list">
<c:forEach var ="productDto" items="${list}">
<div class="product-item">
<input type="checkbox" name="productNo" value="${productDto.productNo}">
[${productDto.productNo}] ${productDto.productName} (${productDto.productPrice}원)
<input type="number" name="qty" value ="1" min="1">
</div>
</c:forEach>
<button type="button" class="purchase-btn">구매하기</button>
</div>
<script src ="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
$(function(){
//버튼을 누르면 폼을 만들어서 체크된 항목의 "상품번호"와 "구매수랑"을 입력창으로 만들어 추가
$(".purchase-btn").click(function(){
var form = $("<form>").attr("action", "test3/purchase").attr("method","post");
var count = 0;
$(".product-item").each(function(index, tag){
//현재 위치의 체크박스가 체크되어 있다면 상품번호와 상품수량을 불러와서 form에 추가하겠다
var checked = $(this).find("[name=productNo]").prop("checked"); //체크 여부를 알아내는 명령
if(checked == false) return; //조건이 false면 차단해
var productNo = $(this).find("[name=productNo]").val();
var qty = $(this).find("[name=qty]").val();
//console.log(productNo, qty);
//이렇게 하지 않으면 수량은 체크 된 것만 넘어오는 것이 아니라 다 넘어온다.
$("<input>").attr("name", "product["+count+"].No")
.attr("type", "hidden")
.val(productNo)
.appendTo(form);
$("<input>").attr("name", "product["+count+"].qty")
.attr("type", "hidden")
.val(qty)
.appendTo(form);
count++; //count를 세는 것이 아닌 카운트처럼 만들어서 쓰는 것
});
$("body").append(form);
form.submit(); //form 전송해라!
});
});
</script>
Network -> All -> Name 클릭 후 -> payload
PurchaseVO 생성
//낱개(번호, 수량)
@Data
public class PurchaseVO {
private int productNo;
private int qty;
}
PurchaseListVO 생성
//묶음
@Data
public class PurchaseListVO {
private List<PurchaseVO> product;
}
KakaoPayController 에 구문 추가
////////////////////////////////////////////////////////////////////////////////
@RequestMapping("/test3")
public String test3(Model model) {
model.addAttribute("list", productDao.selectList());
return "pay3/home";
}
@PostMapping("/test3/purchase")
public String test3Purchase(@ModelAttribute PurchaseListVO listVO) {
log.debug("listVO = {}", listVO);
return null;
}
구매하기 버튼을 누르면 console에 이렇게 나온다.
KakaoPayService에 구문 추가
KakaoPayReadyRequestVO convert(PurchaseListVO listVO);
~ 외 몇 개 라고 쓸 거기 때문에 이름은 첫 번째거 하나만 필요하다
처음에 이름은 null이지만 조건이 걸리면서 이름이 한 번 들어갈 거고 그 다음부턴 조건에 맞지 않으니 이름은 한 번만 들어간다.
KakaoPayServiceImpl에 구문 추가
@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)+"건";
}
return KakaoPayReadyRequestVO.builder()
.partnerOrderId(UUID.randomUUID().toString())
.itemName(name)
.itemPrice(total)
.build();
}
KakaoPayController에 구문을 추가
@PostMapping("/test3/purchase")
public String test3Purchase(@ModelAttribute PurchaseListVO listVO) throws URISyntaxException {
log.debug("listVO = {}", listVO);
//listVO에 들어있는 product 항목들을 이용해서 결제 준비 요청 처리 후 결제 페이지로 안내
//- 결제이름은 대표 상품명 외 ?개와 같이 작성
//- 결제 금액은 모든 상품의 가격과 수량의 총합계
//- 결론적으로 만들어야 하는 데이터는 KakaoPayRequestVO
KakaoPayReadyRequestVO request = kakaoPayService.convert(listVO);
request.setPartnerUserId("testuser1");
KakaoPayReadyResponseVO response = kakaoPayService.ready(request);
return "redirect:" + response.getNextRedirectPcUrl();
}
수량을 체크하고 구매하기를 눌렀을 때 결과화면
KakaoPayController 구문 추가
@PostMapping("/test3/purchase")
public String test3Purchase(@ModelAttribute PurchaseListVO listVO, HttpSession session) throws URISyntaxException {
// log.debug("listVO = {}", listVO);
//listVO에 들어있는 product 항목들을 이용해서 결제 준비 요청 처리 후 결제 페이지로 안내
//- 결제이름은 대표 상품명 외 ?개와 같이 작성
//- 결제 금액은 모든 상품의 가격과 수량의 총합계
//- 결론적으로 만들어야 하는 데이터는 KakaoPayRequestVO
KakaoPayReadyRequestVO request = kakaoPayService.convert(listVO);
request.setPartnerUserId("testuser1");
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); //구매한 상품의 번호와 수량 목록
return "redirect:" + response.getNextRedirectPcUrl();
}
@GetMapping("/test3/purchase/success")
public String test3Success(@RequestParam String pg_token, HttpSession session) 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 작업
return "redirect:successResult";
}
payment 테이블 삭제 후 다르게 다시 생성
CREATE TABLE payment(
payment_no NUMBER PRIMARY key,
payment_member varchar2(20) NOT null,
payment_tid varchar2(20) NOT null,
payment_name varchar2(300) NOT null,
payment_price NUMBER NOT null,
payment_remain NUMBER NOT NULL, --잔여금액
payment_time date DEFAULT sysdate NOT null
);
payment-mapper.xml 수정
<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>
paymentDto 수정
@Data @Builder @NoArgsConstructor @AllArgsConstructor
public class PaymentDto {
private int paymentNo, paymentPrice; //PG사 결제 가격
private String paymentMember; //구매회원ID
private String paymentTid; //PG사 결제 거래번호
private String paymentName; //PG사 결제 상품명
private int paymentRemain; //잔여 결제 금액(취소 가능 금액)
private Date paymentTime;
}
payment_detail, payment_detail_seq 테이블 생성
-- 결제 상세 테이블(상품과 수량이 저장될 테이블)
CREATE TABLE payment_detail (
payment_detail_no NUMBER PRIMARY KEY,
payment_detail_origin REFERENCES payment(payment_no) ON DELETE CASCADE, --payment가 지워질때 삭제되는 게 맞음
payment_detail_product NUMBER NOT NULL, --외래키 안 씀, 결제내역은 변해도 남아있어야 하기 때문
payment_detail_product_name varchar2(90) NOT NULL,
payment_detail_product_price NUMBER NOT NULL,
payment_detail_product_qty NUMBER NOT NULL,
payment_detail_status char(6) NOT NULL,
check(payment_detail_status IN ('완료','취소'))
);
CREATE SEQUENCE payment_detail_seq;
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; //구매상품상태(완료/취소)
}
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>
</mapper>
PaymentDao 구문 추가
void insertDetail(PaymentDetailDto paymentDetailDto);
PaymentDaoImpl 구문 추가
@Override
public void insertDetail(PaymentDetailDto paymentDetailDto) {
sqlSession.insert("payment.saveDetail",paymentDetailDto);
}
}
KakaoPayController 구문 추가
@GetMapping("/test3/purchase/success")
public String test3Success(@RequestParam String pg_token, HttpSession session) 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();
//[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";
}
PurchaseConfirmVO 생성
@Data @Builder @NoArgsConstructor @AllArgsConstructor
public class PurchaseConfirmVO {
private PurchaseVO purchaseVO;
private ProductDto productDto;
}
KakaoPayController 구문 추가
//결제 확인 화면
@GetMapping("/test3/purchase")
public String test3Purchase(@ModelAttribute PurchaseListVO listVO, Model model) {
List<PurchaseVO> purchaseList = listVO.getProduct();
List<PurchaseConfirmVO> confirmList = new ArrayList<>(); //옮겨 담을 리스트
for(PurchaseVO vo : purchaseList) { //사용자가 선택한 번호와 수량의 목록을 반복하며
ProductDto productDto = productDao.selectOne(vo.getProductNo()); //상품정보를 구한다.
confirmList.add(PurchaseConfirmVO.builder() //vo와 dto를 신규객체로 만들어 추가
.purchaseVO(vo).productDto(productDto)
.build());
}
model.addAttribute("list", confirmList);
return "pay3/purchase";
}
PurchaseConfirmVO
@Data @Builder @NoArgsConstructor @AllArgsConstructor
public class PurchaseConfirmVO {
private PurchaseVO purchaseVO;
private ProductDto productDto;
public int getTotal() {
return purchaseVO.getQty() * productDto.getProductPrice();
}
}
purchase.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="confirmVO" items="${list}">
<div class="purchase-item">
[${confirmVO.productDto.productNo}]
${confirmVO.productDto.productName}
-
${confirmVO.productDto.productPrice}원
(구매수량 : ${confirmVO.purchaseVO.qty}개)
-
//총 ${confirmVO.productDto.productPrice * confirmVO.purchaseVO.qty}원
총 ${confirmVO.total} 원 //만든 게터메소드
</div>
</c:forEach>
<!-- 전송되는 부분 -->
home.jsp에서 post를 get으로 수정
<%@ 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>
<div class="product-list">
<c:forEach var ="productDto" items="${list}">
<div class="product-item">
<input type="checkbox" name="productNo" value="${productDto.productNo}">
[${productDto.productNo}] ${productDto.productName} (${productDto.productPrice}원)
<input type="number" name="qty" value ="1" min="1">
</div>
</c:forEach>
<button type="button" class="purchase-btn">구매하기</button>
</div>
<script src ="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
$(function(){
//버튼을 누르면 폼을 만들어서 체크된 항목의 "상품번호"와 "구매수랑"을 입력창으로 만들어 추가
$(".purchase-btn").click(function(){
var form = $("<form>").attr("action", "test3/purchase").attr("method","get");
var count = 0;
$(".product-item").each(function(index, tag){
//현재 위치의 체크박스가 체크되어 있다면 상품번호와 상품수량을 불러와서 form에 추가하겠다
var checked = $(this).find("[name=productNo]").prop("checked"); //체크 여부를 알아내는 명령
if(checked == false) return; //조건이 false면 차단해
var productNo = $(this).find("[name=productNo]").val();
var qty = $(this).find("[name=qty]").val();
//console.log(productNo, qty);
//이렇게 하지 않으면 수량은 체크 된 것만 넘어오는 것이 아니라 다 넘어온다.
$("<input>").attr("name", "product["+count+"].productNo")
.attr("type", "hidden")
.val(productNo)
.appendTo(form);
$("<input>").attr("name", "product["+count+"].qty")
.attr("type", "hidden")
.val(qty)
.appendTo(form);
count++; //count를 세는 것이 아닌 카운트처럼 만들어서 쓰는 것
});
$("body").append(form);
form.submit(); //form 전송해라!
});
});
</script>
상태변수 varStatus
purchase.jsp 구문 추가
<!-- 전송되는 부분 -->
<form method ="post">
<c:forEach var="confirmVO" items="${list}" varStatus="stat">
<input type="hidden" name="product[${stat.index}].productNo" value="${confirmVO.purchaseVO.productNo}">
<input type="hidden" name="product[${stat.index}].qty" value = "${confirmVO.purchaseVO.qty}">
</c:forEach>
<button type="submit">구매하기</button>
</form>
KakaoPayController에 구문 추가
@RequestMapping("/test3/purchase/successResult")
public String test3SuccessResult() {
return "pay3/successResult";
}
pay3 폴더 아래에 successResult.jsp 생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<h1>결제가 완료되었습니다.</h1>
날짜별로 조회하거나 항목별로 조회할 때 계층형을 사용하는데 계층형은 조인이 아니다.
<!-- 결제 대표정보만 조회(사용자별/전체) -->
<select id="list" resultType="PaymentDto">
select * from payment order by payment_no asc
</select>
PaymentDao에 구문 추가
List<PaymentDto> selectList();
PaymentDaoImpl에 구문 추가
@Override
public List<PaymentDto> selectList() {
return sqlSession.selectList("payment.list");
}
KakaoPayController에 구문 추가
@RequestMapping("/test3/list")
public String test3list(Model model) {
model.addAttribute("list",paymentDao.selectList());
return "pay3/list";
}
list.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="paymentDto" items="${list}">
<div style="border:1px solid black; margin:30px 0px;">
${paymentDto}
</div>
</c:forEach>
n+1을 어떻게 처리하는가?
PaymentListVO 생성
@Data
public class PaymentListVO {
private PaymentDto paymentDto;
private List<PaymentDetailDto> paymentDetailList;
}
resultType은 자동매핑 resultMap은 수동매핑이다.
payment-mapper.xml에 구문 추가
<!-- 결제 대표정보만 조회(사용자별/전체) -->
<select id="list" resultType="PaymentDto">
select * from payment order by payment_no asc
</select>
<!-- 결제 상세정보를 조회(결제 대표번호별) -->
<select id="listDetail" resultType="PaymentDetailDto">
select * from payment_detail
where payment_detail_origin = #{paymentDetailOrigin} <!-- paymentNo라고 써도 됨 -->
order by payment_detail_no asc
</select>
<!--
결제 대표정보와 상세정보를 게층형으로 조회
- resultType은 필드명으로 데이터베이스 조회 결과를 매핑하여 처리한다.
- 지금과 같이 복잡한 구조는 처리가 불가능하다.
- resultMap 항목을 선언하여 수동으로 매핑하도록 처리한다.
-->
<resultMap type="PaymentListVO" id="paymentListVO">
<!-- 일단 조회 결과는 객체의 내가 지정한 객체의 필드에 맞게 배치해라 -->
<!-- List가 하나 있는데 구문 알려줄테니까 니가 알아서 조회해서 채워라 -->
</resultMap>
<select id ="listAll" resultMap="paymentListVO">
select * from payment order by payment_no asc
</select>
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>
<!-- 결제 상세정보를 조회(결제 대표번호별) -->
<select id="listDetail" resultType="PaymentDetailDto">
select * from payment_detail
where payment_detail_origin = #{paymentDetailOrigin} <!-- paymentNo라고 써도 됨 -->
order by payment_detail_no asc
</select>
<!--
결제 대표정보와 상세정보를 게층형으로 조회
- resultType은 필드명으로 데이터베이스 조회 결과를 매핑하여 처리한다.
- 지금과 같이 복잡한 구조는 처리가 불가능하다.
- resultMap 항목을 선언하여 수동으로 매핑하도록 처리한다.
- association 은 객체를 의미
- collection 은 리스트를 의미
- property 는 항목명을 의미
- coulmn 은 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="paymentDetailProductStatus"/>
</collection>
</resultMap>
<select id ="listAll" resultMap="paymentListVO">
select * from payment order by payment_no asc
</select>
<!-- 결제 상세정보를 조회(결제 대표번호별) -->
<select id="listDetail" resultType="PaymentDetailDto">
select * from payment_detail
where payment_detail_origin = #{paymentDetailOrigin} <!-- paymentNo라고 써도 됨 -->
order by payment_detail_no asc
</select>
</mapper>
PaymentDao에 구문추가
List<PaymentListVO> selectTotalList();
properties에 설정 구문 추가
#logging setting
logging.level.root=warn
logging.level.com.kh=debug
logging.level.payment=debug
logging.level.paymentDetail=debug
PaymentDaoImpl에 구문 추가
@RequestMapping("/test3/list")
public String test3list(Model model) {
model.addAttribute("list",paymentDao.selectList());
return "pay3/list";
}
@RequestMapping("/test3/list2")
public String test3list2(Model model) {
model.addAttribute("list", paymentDao.selectTotalList());
return "pay3/list2";
}
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 는 항목명을 의미
- coulmn 은 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="paymentDetailProductStatus"/>
</collection>
</resultMap>
<select id ="listAll" resultMap="paymentListVO">
select * from payment order by payment_no asc
</select>
<!-- 결제 상세정보를 조회(결제 대표번호별) -->
<select id="listDetail" resultType="PaymentDetailDto">
select * from payment_detail
where payment_detail_origin = #{paymentDetailOrigin} <!-- paymentNo라고 써도 됨 -->
order by payment_detail_no asc
</select>
</mapper>
list2.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;">
${paymentListVO}
</div>
</c:forEach>
list2.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}</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>
<a href="cancel?paymentDetailNo=${paymentDetailDto.paymentDetailNo}">
개별내역취소
</a>
</div>
</c:forEach>
</div>
</div>
</c:forEach>
paymentDao 구문 추가
package com.kh.spring21.dao;
import java.util.List;
import com.kh.spring21.dto.PaymentDetailDto;
import com.kh.spring21.dto.PaymentDto;
import com.kh.spring21.vo.PaymentListVO;
public interface PaymentDao {
int sequence();
void insert(PaymentDto paymentDto);
List<PaymentDto> selectList();
PaymentDto selectOne(int paymentNo);
void insertDetail(PaymentDetailDto paymentDetailDto);
List<PaymentListVO> selectTotalList();
PaymentDetailDto selectDetail(int paymentDetailNo);
void cancelDetail(int paymentDetailNo);
void cancel(PaymentDto paymentDto);
}
paymentDaoImpl 구문 추가
package com.kh.spring21.dao;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.kh.spring21.dto.PaymentDetailDto;
import com.kh.spring21.dto.PaymentDto;
import com.kh.spring21.vo.PaymentListVO;
@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);
}
}
KakaoPayController 구문 추가
package com.kh.spring21.controller;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.servlet.http.HttpSession;
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.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.kh.spring21.dao.PaymentDao;
import com.kh.spring21.dao.ProductDao;
import com.kh.spring21.dto.PaymentDetailDto;
import com.kh.spring21.dto.PaymentDto;
import com.kh.spring21.dto.ProductDto;
import com.kh.spring21.service.KakaoPayService;
import com.kh.spring21.vo.KakaoPayApproveRequestVO;
import com.kh.spring21.vo.KakaoPayApproveResponseVO;
import com.kh.spring21.vo.KakaoPayCancelRequestVO;
import com.kh.spring21.vo.KakaoPayCancelResponseVO;
import com.kh.spring21.vo.KakaoPayDetailRequestVO;
import com.kh.spring21.vo.KakaoPayDetailResponseVO;
import com.kh.spring21.vo.KakaoPayReadyRequestVO;
import com.kh.spring21.vo.KakaoPayReadyResponseVO;
import com.kh.spring21.vo.PurchaseConfirmVO;
import com.kh.spring21.vo.PurchaseListVO;
import com.kh.spring21.vo.PurchaseVO;
import lombok.extern.slf4j.Slf4j;
@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("partnerOrderId", request.getPartnerOrderId());
// session.setAttribute("partnerUserId",request.getPartnerUserId());s
// session.setAttribute("tid", response.getTid());
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()) //상품 번호 지우고 remain 추가
.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() //vo와 dto를 신규객체로 만들어 추가
.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 test3Purchase(@ModelAttribute PurchaseListVO listVO, HttpSession session) throws URISyntaxException {
// log.debug("listVO = {}", listVO);
//listVO에 들어있는 product 항목들을 이용해서 결제 준비 요청 처리 후 결제 페이지로 안내
//- 결제이름은 대표 상품명 외 ?개와 같이 작성
//- 결제 금액은 모든 상품의 가격과 수량의 총합계
//- 결론적으로 만들어야 하는 데이터는 KakaoPayRequestVO
KakaoPayReadyRequestVO request = kakaoPayService.convert(listVO);
request.setPartnerUserId("testuser1");
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); //구매한 상품의 번호와 수량 목록
return "redirect:" + response.getNextRedirectPcUrl();
}
@GetMapping("/test3/purchase/success")
public String test3Success(@RequestParam String pg_token, HttpSession session) 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();
//[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(Model model) {
model.addAttribute("list", paymentDao.selectTotalList());
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
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";
}
}
payment-mapper 구문 추가
<?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 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>
</mapper>
'Spring' 카테고리의 다른 글
결제04 - 마무리 (0) | 2023.10.27 |
---|---|
결제02 - 단건결제 (0) | 2023.10.25 |
결제01 - (카카오페이 api) + 카멜케이스 변환 (0) | 2023.10.24 |
DM을 어떻게 보내는가 (0) | 2023.10.23 |
웹소켓 - 실시간 채팅, 반응형 디자인 (0) | 2023.10.23 |