코딩 노트

결제03 - 수량추가 + 조회 + 단건취소 본문

Spring

결제03 - 수량추가 + 조회 + 단건취소

newbyeol 2023. 10. 26. 10:33

구매하기를 누르면 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;

}

둘의 관계
초코파이 1개를 취소하면 상태가 완료에서 취소로 바뀌고 돈이 3700원이 된다.

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() //vodto를 신규객체로 만들어 추가

.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()); //상품정보를 구한다.

 

//vodto를 신규객체로 만들어 추가

PurchaseConfirmVO confirmVO = PurchaseConfirmVO.builder() //vodto를 신규객체로 만들어 추가

.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