Project/Semi (20.07.08-20.08.11)

[Academy Semi Project] 부트페이 API

myspace 2020. 8. 28. 11:55

 

https://www.bootpay.co.kr/

 

무료 결제 연동 API 서비스

개발자를 위한 PG 연동 서비스로, 빠른 결제 연동, 더 높은 결제성공율, 무료 통계 서비스를 제공합니다.

www.bootpay.co.kr

1. 부트페이에 가입한 뒤, 프로젝트 설정에 프로젝트명/판매자 노출명 등 필요한 정보 기입.

2. [결제수단 활성화] 탭에서 PG사, 결제수단 선택.

3. AGENDA 사이트에 로그인한 후 후원 버튼을 누르면 결제창이 뜨고, 결제 완료 후에는 개인후원내역 페이지로 넘어가는 기능. 로그인하지 않으면 후원할 수 없도록 함.

 

main.jsp

// main.jsp

<%@page import="com.agenda.login.LoginDto"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>

<% request.setCharacterEncoding("UTF-8"); %>
<% response.setContentType("text/html; charset=UTF-8"); %>

// jstl을 사용하여 AGENDA 홈페이지에 로그인하지 않으면 후원이 불가능한 기능 구현
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Agenda</title>

// 부트페이 홈페이지에서 제공하는 CDN
// CDN : html이나 jsp 파일에서 결제창(js) 띄울 수 있도록 웹에 연동하는 것
<script src="https://cdn.bootpay.co.kr/js/bootpay-3.2.6.min.js" type="application/javascript"></script>
<script src="http://code.jquery.com/jquery-3.5.1.js"></script>

// body 태그 안에 들어가는 후원 박스(부트페이 결제 API)
// JSTL의 c:choose문에서 후원 버튼을 눌렀을 때
// logindto가 null이 ne(아닐)일 때 결제창이 실행되고(c:when),
// null이라면 '로그인해주세요'라는 alert창이 뜸(c:otherwise)

	<section class="section third area">
	
		<div class="donation-wrapper">
			<span></span><span></span><span></span>
			<div class="donation-content-wrapper">
				<p>A Cup of Coffee </p>
				<c:choose>
					<c:when test="${logindto ne null }">
						<div class="donation-content" onclick="pay01()" id="donaClick01" title="${logindto.member_id }">donate</div>
					</c:when>
					<c:otherwise>
						<div class="donation-content" onclick="alert('로그인해주세요')" id="donaClick01">donate</div>
					</c:otherwise>
				</c:choose>
			</div>
			<span></span><span></span><span></span>
		</div>
		
		<div class="donation-wrapper">
			<span></span><span></span><span></span>
			<div class="donation-content-wrapper">
				<p>A piece of cake</p>
				<c:choose>
					<c:when test="${logindto ne null }">
						<div class="donation-content" onclick="pay02()" id="donaClick02" title="${logindto.member_id }">donate</div>
					</c:when>
					<c:otherwise>
						<div class="donation-content" onclick="alert('로그인해주세요')" id="donaClick02">donate</div>
					</c:otherwise>
				</c:choose>
			</div>
		</div>
		
		<div class="donation-wrapper">
			<span></span><span></span><span></span>
			<div class="donation-content-wrapper">
				<p>Coffee</p> <p>with a Piece of Cake</p>
				<c:choose>
					<c:when test="${logindto ne null }">
						<div class="donation-content" onclick="pay03()" id="donaClick03" title="${logindto.member_id }">donate</div>
					</c:when>
					<c:otherwise>
						<div class="donation-content" onclick="alert('로그인해주세요')" id="donaClick03">donate</div>
					</c:otherwise>
				</c:choose>
			</div>
		</div>
		
		<div class="donation-wrapper">
			<span></span><span></span><span></span>
			<div class="donation-content-wrapper">
				<p>Whole Cake set</p>
				<c:choose>
					<c:when test="${logindto ne null }">
						<div class="donation-content" onclick="pay04()" id="donaClick04" title="${logindto.member_id }">donate</div>
					</c:when>
					<c:otherwise>
						<div class="donation-content" onclick="alert('로그인해주세요')" id="donaClick04">donate</div>
					</c:otherwise>
				</c:choose>
			</div>
		</div>
	</section>
    
// body 끝나기 직전에 js(javascript) 호출 코드
<script type="text/javascript" src="${pageContext.request.contextPath}/main/mainpay.js"></script>

mainpay.js

// mainpay.js
// main.jsp의 onclick="pay01()","pay02()","pay03()","pay04()"가 작동하도록 함

/* function pay01~04(){} 안에 있는 코드는
 * 부트페이 공식홈페이지에서 제공.
 * 
 * main.jsp에서 온클릭 버튼을 눌렀을 때 결제창이 띄워지고
 * 결제한 결과값(이하 '결제값')이 JSON 형태로 {key:value} 나타남.
 * 
 * .error(function (data) {})에는 결제값이 error가 날 경우
 * .cancel에는 결제를 취소할 경우 등등 처리할 코드를 써주면 되는데,
 * 우리는 결제가 끝났을 경우(done)만 사용하면 됨.
 * 
 * */

function pay01() {
	
BootPay.request({
	price: '1000',
	application_id: "5f17cf9f8f0751002136c6b9",
	name: '커피 한 잔을 후원하시겠습니까?',
	pg: '',
	method: 'card',
	show_agree_window: 0,
	items: [
		{
			item_name:'A Cup of Coffee',
			qty: 1,
			unique: '123',
			price: 1000,
		}
	],
	user_info: {
		username: '사용자 이름',
		email: '사용자 이메일',
		addr: '사용자 주소',
		phone: '010-1234-4567'
	},
	order_id: '고유order_id_1234',
	params: {callback1: '그대로 콜백받을 변수 1', callback2: '그대로 콜백받을 변수 2', customvar1234: '변수명도 마음대로'},
	account_expire_at: '2018-05-25',
	extra: {
	    start_at: '2019-05-10',
		end_at: '2022-05-10',
        vbank_result: 1,
        quota: '0,2,3',
		theme: 'purple',
		custom_background: '#00a086',
		custom_font_color: '#ffffff'
	}
}).error(function (data) {
	console.log(data);
}).cancel(function (data) {
	console.log(data);
}).ready(function (data) {
	console.log(data);
	/* .confirm 코드는 기본제공.
	 * 결제값이 Bootpay 공식홈페이지의 결제내역으로 넘어가도록 함.*/
}).confirm(function (data) {
	console.log(data);
	var enable = true;
	if (enable) {
		BootPay.transactionConfirm(data);
	} else {
		BootPay.removePaymentWindow();
	}
}).close(function (data) {
    console.log(data);
    /* 결제가 완료(done) 되면 실행될 함수*/
}).done(function (data) {
		console.log(data);
		
		/* main.jsp 후원 부분의 c:when 하위 div에 id와 title이 있음.
		 * var(변수) member_id는 #donaClick01이라는 id를 가진 div에서 title 값을 attr(잘라서 가져오기)한 것 */	
		var member_id = $("#donaClick01").attr("title");
		console.log("컨트롤러 넘어가기전 아이디" + member_id)
		console.log("끝 : "+data);
		
		/* ajax를 통해서 url에 json형태의 결과값인 data를 string형태로 바꾼 obj와, 위에서 만든 변수인 member_id를 같이 보냄
		 * (여기까지 읽고 dnController의 donation으로 가세요)
		 * dnController의 "donation"으로 간 json 형태의 값은 다시 이곳으로 돌아와서
		 * success니까 member_id와 같이 dnController의 "dnlist"로 이동함.
		 * */
		$.ajax({
			url: "./dnController?command=donation",
			method: "post",
			data: {"obj" : JSON.stringify(data), member_id : member_id},
			success: function(msg){
				location.href="./dnController?command=dnlist&member_id="+member_id;
			},
			error:function(){
				alert("통신 실패");
			}
		});
	});
}



function pay02(){

	BootPay.request({
		price: '5000',
		application_id: "5f17cf9f8f0751002136c6b9",
		name: '케이크 한 조각을 후원하시겠습니까?',
		pg: '',
		method: 'card',
		show_agree_window: 0,
		items: [
			{
				item_name: 'A Piece of Cake',
				qty: 1,
				unique: '123',
				price: 5000,
			}
		],
		user_info: {
			username: '사용자 이름',
			email: '사용자 이메일',
			addr: '사용자 주소',
			phone: '010-1234-4567'
		},
		order_id: '고유order_id_1234',
		params: {callback1: '그대로 콜백받을 변수 1', callback2: '그대로 콜백받을 변수 2', customvar1234: '변수명도 마음대로'},
		account_expire_at: '2018-05-25',
		extra: {
		    start_at: '2019-05-10',
			end_at: '2022-05-10',
	        vbank_result: 1,
	        quota: '0,2,3',
			theme: 'purple',
			custom_background: '#00a086',
			custom_font_color: '#ffffff'
		}
	}).error(function (data) {
		console.log("에러 : " + data);
	}).cancel(function (data) {
		console.log("취소 : " + data);
	}).ready(function (data) {
		console.log("준비 : " + data);
	}).confirm(function (data) {
		console.log("확인 : " + data);
		var enable = true;
		if (enable) {
			BootPay.transactionConfirm(data);
		} else {
			BootPay.removePaymentWindow();
		}
	}).close(function (data) {
	    console.log(data);
	}).done(function (data) {
		console.log(data);
		
		var member_id = $("#donaClick02").attr("title");
		console.log("컨트롤러 넘어가기전 아이디" + member_id)
		console.log("끝 : "+data);
		
		$.ajax({
			url: "./dnController?command=donation",
			method: "post",
			data: {"obj" : JSON.stringify(data), member_id : member_id},
			success: function(msg){
				location.href="./dnController?command=dnlist&member_id="+member_id;
			},
			error:function(){
				alert("통신 실패");
			}
		});
	});
}




function pay03(){

	BootPay.request({
		price: '10000',
		application_id: "5f17cf9f8f0751002136c6b9",
		name: '커피와 조각케이크를 후원하시겠습니까?',
		pg: '',
		method: 'card',
		show_agree_window: 0,
		items: [
			{
				item_name: 'Coffee With a Piece of Cake',
				qty: 1,
				unique: '123',
				price: 10000,
			}
		],
		user_info: {
			username: '사용자 이름',
			email: '사용자 이메일',
			addr: '사용자 주소',
			phone: '010-1234-4567'
		},
		order_id: '고유order_id_1234',
		params: {callback1: '그대로 콜백받을 변수 1', callback2: '그대로 콜백받을 변수 2', customvar1234: '변수명도 마음대로'},
		account_expire_at: '2018-05-25',
		extra: {
		    start_at: '2019-05-10',
			end_at: '2022-05-10',
	        vbank_result: 1,
	        quota: '0,2,3',
			theme: 'purple',
			custom_background: '#00a086',
			custom_font_color: '#ffffff'
		}
	}).error(function (data) {
		console.log(data);
	}).cancel(function (data) {
		console.log(data);
	}).ready(function (data) {
		console.log(data);
	}).confirm(function (data) {
		console.log(data);
		var enable = true;
		if (enable) {
			BootPay.transactionConfirm(data);
		} else {
			BootPay.removePaymentWindow();
		}
	}).close(function (data) {
	    console.log(data);
	}).done(function (data) {
		console.log(data);
		
		var member_id = $("#donaClick03").attr("title");
		console.log("컨트롤러 넘어가기전 아이디" + member_id)
		console.log("끝 : "+data);
		
		console.log(data);
		$.ajax({
			url: "./dnController?command=donation",
			method: "post",
			data: {"obj" : JSON.stringify(data), member_id : member_id},
			success: function(msg){
				location.href="./dnController?command=dnlist&member_id="+member_id;
			},
			error:function(){
				alert("통신 실패");
			}
		});
	});
	}



function pay04(){

	BootPay.request({
		price: '50000',
		application_id: "5f17cf9f8f0751002136c6b9",
		name: '케이크 세트를 후원하시겠습니까?',
		pg: '',
		method: 'card',
		show_agree_window: 0,
		items: [
			{
				item_name: 'Whole Cake Set',
				qty: 1,
				unique: '123',
				price: 50000,
			}
		],
		user_info: {
			username: '사용자 이름',
			email: '사용자 이메일',
			addr: '사용자 주소',
			phone: '010-1234-4567'
		},
		order_id: '고유order_id_1234',
		params: {callback1: '그대로 콜백받을 변수 1', callback2: '그대로 콜백받을 변수 2', customvar1234: '변수명도 마음대로'},
		account_expire_at: '2018-05-25',
		extra: {
		    start_at: '2019-05-10',
			end_at: '2022-05-10',
	        vbank_result: 1,
	        quota: '0,2,3',
			theme: 'purple',
			custom_background: '#00a086',
			custom_font_color: '#ffffff'
		}
	}).error(function (data) {
		console.log(data);
	}).cancel(function (data) {
		console.log(data);
	}).ready(function (data) {
		console.log(data);
	}).confirm(function (data) {
		console.log(data);
		var enable = true;
		if (enable) {
			BootPay.transactionConfirm(data);
		} else {
			BootPay.removePaymentWindow();
		}
	}).close(function (data) {
	    console.log(data);
	}).done(function (data) {
		console.log(data);
		
		var member_id = $("#donaClick04").attr("title");
		console.log("컨트롤러 넘어가기전 아이디" + member_id)
		console.log("끝 : "+data);
		
		$.ajax({
			url: "./dnController?command=donation",
			method: "post",
			data: {"obj" : JSON.stringify(data), member_id : member_id},
			success: function(msg){
				location.href="./dnController?command=dnlist&member_id="+member_id;
			},
			error:function(){
				alert("통신 실패");
			}
		});
	});
}

dnController.java (서블릿)

package com.agenda.donation;

import java.io.IOException;
import java.util.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.agenda.login.LoginDto;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;


@WebServlet("/dnController")
public class dnController extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}

	// 결제 통지 받기
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html; charset=UTF-8");
		
		dnDao dao = new dnDao();
		
		String command = request.getParameter("command");
		System.out.println(command);
		
		if (command.equals("donation")) {
		
		// mainpay.js에서 보낸 member_id를 받음
		String member_id = request.getParameter("member_id");
		System.out.println("도네이션 컨트롤러 : " + member_id);
		
		// mainpay.js에서 보낸 obj(JSON문자열)를 받아서 string 변수에 담음
		String obj = request.getParameter("obj");
		System.out.println(obj);
		
		// mainpay.js의 JSON형태의 결제 결과값들을 파싱
		JsonElement element = JsonParser.parseString(obj);
		
		// JSON element 값을 JSON object 형태로 바꿔서 꺼냄
		JsonObject tmp = element.getAsJsonObject();
		
		// json 형태인 {"key":"value"}에서 key("price", "pruchased_at")를 호출한 뒤 변수에 담아줌
		int dona_bill = tmp.get("price").getAsInt();
		String dona_date2 = tmp.get("purchased_at").getAsString();
		
		// String형태의 dano_date2라는 변수를 Date로 형변환
		SimpleDateFormat transFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date dona_date = null;
		try {
			dona_date = (Date) transFormat.parse(dona_date2);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		System.out.println(dona_bill);
		System.out.println(dona_date);
		
		// dto에 세팅
		dnDto dto = new dnDto();
		dto.setDona_bill(dona_bill);
		dto.setDona_date(dona_date);
		dto.setMember_id(member_id);
		
		// db에 넣기
		int dona_db = dao.insert(dto);
		System.out.println(dona_db); //dona_db:1 즉, db에 저장 성공
		
		// db에 있는 값 꺼내서 후원내역 테이블에서 보여주려면
		// db에 저장된 값(dona_db>0)을 mainpay.js의 msg에 응답 시키기
		// 아래 코드를 통해서 다시 mainpay.js ajax의 success로 돌아감
		response.getWriter().print(dona_db);
		
		}
		
		// 개인 후원내역조회 - 메인페이지에서 후원하고 나면 결제창 꺼지면서 자동으로 바로 넘어감
		else if (command.equals("dnlist")) {
			
			String member_id = request.getParameter("member_id");
			System.out.println("리스트 아이디 : " + member_id);
		
			List<dnDto> list = dao.selectOne(member_id);
			request.setAttribute("donationList", list);
			System.out.println(list);
				
			RequestDispatcher dispatch = request.getRequestDispatcher("myinfo/myreceipt.jsp");
			dispatch.forward(request, response);
				
		}
		
		// 전체 후원내역 조회 - 관리자 계정으로 로그인해서 관리자페이지에서만 볼 수 있음
		else if (command.equals("alldnlist")) {
			
			List <dnDto> list = dao.selectList();
			request.setAttribute("list", list);
				
			RequestDispatcher dispatch = request.getRequestDispatcher("admin/adreceipt.jsp");
			dispatch.forward(request, response);
		}
		
		}
	
	public void dispatch(String url, HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		RequestDispatcher dispatch = request.getRequestDispatcher(url);
		dispatch.forward(request, response);
	}
	
}

dnDao.java

package com.agenda.donation;

import java.util.List;

import org.apache.ibatis.session.SqlSession;

public class dnDao extends dnSqlMapConfig {
	
	private String namespace="com.agenda.donation.dnMapper.";
	
	public List<dnDto> selectList() {
		
		SqlSession session = null;
		List<dnDto> list = null;
		
		try {
			session = getSqlSessionFactory().openSession(false);
			list = session.selectList("com.agenda.donation.dnMapper.selectList");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			session.close();
		}
		return list;
	}
	
	public List<dnDto> selectOne(String member_id) {
		
		SqlSession session = null;
		List<dnDto> list = null;

		try {
			session = getSqlSessionFactory().openSession(false);
			list = session.selectList(namespace+"selectOne", member_id);
			System.out.println("dao>>" + list);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			session.close();
		}
	return list;
	}
	
	public int insert(dnDto dto) {
		
		SqlSession session = null;
		int res = 0;
		
		try {
			session = getSqlSessionFactory().openSession(false);
			res = session.insert(namespace+"insert", dto);
			if(res>0) {
				session.commit();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			session.close();
		}
		return res;
	}

}

dnDto.java

package com.agenda.donation;

import java.util.Date;

public class dnDto {
	
	private int dona_no;
	private int dona_bill;
	private Date dona_date;
	private String member_id;
	
	public dnDto() {
		
	}
	
	public dnDto(int dona_no, int dona_bill, Date dona_date, String member_id) {
		this.dona_no = dona_no;
		this.dona_bill = dona_bill;
		this.dona_date = dona_date;
		this.member_id = member_id;
	}
	
	public int getDona_no() {
		return dona_no;
	}

	public void setDona_no(int dona_no) {
		this.dona_no = dona_no;
	}

	public int getDona_bill() {
		return dona_bill;
	}

	public void setDona_bill(int dona_bill) {
		this.dona_bill = dona_bill;
	}

	public Date getDona_date() {
		return dona_date;
	}

	public void setDona_date(Date dona_date) {
		this.dona_date = dona_date;
	}

	public String getMember_id() {
		return member_id;
	}

	public void setMember_id(String member_id) {
		this.member_id = member_id;
	}
	

}

 

dnMapper.sql

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.agenda.donation.dnMapper">
  
  	<resultMap type="dnDto" id="dnResultMap">
		<result property="dona_no" column="DONA_NO"/>
		<result property="dona_bill" column="DONA_BILL"/>
		<result property="dona_date" column="DONA_DATE"/>
		<result property="member_id" column="MEMBER_ID"/>	
	</resultMap>
	
	<select id="selectList" resultMap="dnResultMap">
        SELECT DONA_NO, DONA_BILL, DONA_DATE, MEMBER_ID
        FROM AG_DONATION
        ORDER BY DONA_NO
    </select>
    
    <select id="selectOne" resultMap="dnResultMap">
		SELECT DONA_NO, MEMBER_ID, DONA_BILL, DONA_DATE
 		FROM AG_DONATION
		WHERE MEMBER_ID = #{member_id}
 	</select>
 	
	<insert id="insert" parameterType="dnDto">
		INSERT INTO AG_DONATION
		(DONA_NO, MEMBER_ID, DONA_BILL, DONA_DATE)
		VALUES
		(AG_DONATIONSEQ.NEXTVAL, #{member_id}, #{dona_bill}, #{dona_date})
	</insert>

</mapper>

dnConfig.java

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" >
<configuration>

	<properties resource="com/agenda/common/db.Properties"/>

	<typeAliases>
		<typeAlias type="com.agenda.donation.dnDto" alias="dnDto"/>
	</typeAliases>
	
	<environments default="development">
		<environment id="development">
      	<transactionManager type="JDBC"/>
      	<dataSource type="POOLED">
        	<property name="driver" value="${driver}"/>
        	<property name="url" value="${url}"/>
        	<property name="username" value="${username}"/>
        	<property name="password" value="${password}"/>
      	</dataSource>
    	</environment>
	</environments>

	<mappers>
		<mapper resource="com/agenda/donation/dnMapper.xml" />
	</mappers>

</configuration>

dnSqlMapConfig.java

package com.agenda.donation;

import java.io.IOException;
import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class dnSqlMapConfig {
	
	private SqlSessionFactory sqlSessionFactory;
	
	public SqlSessionFactory getSqlSessionFactory() {
		
		String resource = "com/agenda/donation/dnConfig.xml";
		Reader reader = null;
		
		try {
			reader = Resources.getResourceAsReader(resource);
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			
		}
			try {
				reader.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			return sqlSessionFactory;
	}
}

dnDB.sql

CREATE TABLE AG_DONATION(
	DONA_NO NUMBER PRIMARY KEY,
	DONA_BILL NUMBER NOT NULL,
	DONA_DATE DATE NOT NULL,
	MEMBER_ID VARCHAR2(50),
	CONSTRAINT DONA_FK FOREIGN KEY(MEMBER_ID) REFERENCES AG_MEMBER(MEMBER_ID) 
);

SELECT * FROM AG_DONATION;

SELECT AG_DONATIONSEQ.CURRVAL FROM AG_DONATION;
SELECT AG_DONATIONSEQ.NEXTVAL FROM DUAL;

DELETE * FROM AG_DONATION ORDER BY DONA_NO;

SELECT DONA_NO, MEMBER_ID, DONA_BILL, DONA_DATE
FROM AG_DONATION
WHERE MEMBER_ID = 'test2';

 

4. AGENDA 사이트에 관리자 계정으로 로그인 했을 때, 전체후원내역조회 하는 기능.

adlayout.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Admin Page</title>
<script src="http://code.jquery.com/jquery-3.5.1.js"></script>
<style type="text/css">
@charset "UTF-8";
@font-face{font-family:'Nanum Gothic'; src:url('../font/NanumBarunGothic.ttf')}
@font-face{font-family:'Nanum Gothic Light'; src:url('../font/NanumBarunGothicLight.ttf')}
@font-face{font-family:'Nanum Gothic Bold'; src:url('../font/NanumBarunGothicBold.ttf')}
@font-face{font-family:'Nanum Gothic Ultra Light'; src:url('../font/NanumBarunGothicUltraLight.ttf')}
	
	body {
 		margin: 0 0;
 		padding: 0 0;
	}

	#admin {
		display: flex;
		flex-direction: column;
		align-items: center;
		width: 100vw;
		height: 100vh;
	}
	
	#ad_top {
		display: flex;
		justify-content: center;
		width: 100vw;
		height: 150px;
	}
	
	#ad_title {
		display: flex;
		justify-content: center;
		align-items: center;
		font-family:'Nanum Gothic Bold';
		font-size: 44px;
		line-height: 72px;
		color: #000000;		
	}
	
	#ad_bottom {
		width: 100vw;
		top: 100vh;
		display: flex;
		align-items: center;
		justify-content: space-around;
		font-family:'Nanum Gothic';
	}
	
	.ad_box {
		width: 240px;
		height: 400px;
		top: 373px;
		bottom: 40px;
		background: #FAFAFA;
		cursor: pointer;
		text-align: center;
		font-size: 16pt;
		line-height: 200px;
		margin-top: 150px;
		margin-bottom: 150px;
	}
	
	#ad_box span {
		display: teble-cell;
	}
	
	.icon {
		width: 100px;
		height: 100px;
		display: block;
		position: relative;
		margin: 0px auto;
	}
	
	.btn {
		position: absolute;
		background-color: #CED8F6;
		top: 200px;
	}
	
	#ad_footer {
		position: relative;
		width: 100vw;
		height: 300px;
		bottom: 0;
		background: #E5E5E5;
	}
	
</style>

</head>
<body>

<div id="admin">

	<c:choose>
    	<c:when test="${empty logindto }">
    		<jsp:include page="../header/header.jsp" />
    	</c:when>
    	<c:otherwise>
    		<jsp:include page="../header/loginMain.jsp" />
    	</c:otherwise>
	</c:choose>

	<div id="ad_top">
		<div id="ad_title">관리자 페이지</div>
	</div>
	
	<div id="ad_bottom">
			<div class="ad_box" onclick="location.href='${pageContext.request.contextPath}/adController?command=admyinfo&member_id=${logindto.member_id }'">
			<span>내 정보 수정</span>
			<img class="icon" src="image/info.png" />
			</div>
			<div class="ad_box" onclick="location.href='${pageContext.request.contextPath}/adController?command=allMember'">
			<span>회원 정보 관리</span>
			<img class="icon" src="image/search.png" />
			</div>
			<div class="ad_box" onclick="location.href='${pageContext.request.contextPath}/dnController?command=alldnlist'">
			<span>전체 후원기록</span>
			<img class="icon" src="image/qr.png" />
			</div>
	</div>	

	<footer id="ad_footer">
		<jsp:include page="../footer/mainFooter.jsp"></jsp:include>
	</footer>
</div>

</body>
</html>

adreceipt.jsp

<%@page import="java.util.Date"%>
<%@page import="com.agenda.donation.dnDto"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c"  uri="http://java.sun.com/jsp/jstl/core"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>전체 후원내역 조회</title>

<style type="text/css">

	* {
		margin: 0px;
		padding: 0px;
	}
	
	#allreceipt {
		width: 100vw;
		height: 100vh;
		background-color: whitesmoke;
		display: flex;
		justify-content: center;
		align-items: center;
	}
	
	#allreceipt table{
		width: 1000px;
		heigth: 100%;
		padding: 30px;
		margin: 80px auto;
		font-family: 'Nanum Gothic Light';
	}
	
	#title {
		font-family: 'Nanum Gothic Bold';
	}
	
	table{
		border-collapse: collapse;
		width: 100%;
	}
	
	tr,td{
		padding : 5px;
		text-align: center;
	}
	
	td {
		border-top: 1px solid black;
		border-bottom: 1px solid black;
		
		padding: 8px;
		font-size : 1em;
	}
	
	thead tr td {
		font-family: 'Nanum Gothic Bold';
	}
	
	/* 컬럼크기 */
	td:first-child {
	width: 30px;
	}
	
	td:nth-child(2) {
		width: 300px;
	}
	
	td:nth-child(3) {
		width: 30px;
	}
	
	td:nth-child(4) {
		width: 100px;
	}
	
	td:nth-child(5) {
		width: 30px;
	}
	/* 컬럼크기 끝 */
	
	#title {
		font-size : 1.5em;
		padding: 8px
	}
	
	#back {
		background-color: black;
		color: white;
		cursor: pointer;
	}
	
	#back {
		padding : 5px 5px;
		border : none;
		cursor: pointer;
		background-color: gray;
		color : white;
	}
	
</style>

</head>
<body>

	<c:choose>
    	<c:when test="${empty logindto }">
    		<jsp:include page="../header/header.jsp" />
    	</c:when>
    	<c:otherwise>
    		<jsp:include page="../header/loginMain.jsp" />
    	</c:otherwise>
	</c:choose>

<div id="allreceipt">
	<table border="0">
		<caption id="title">전체 후원내역</caption>
		<thead>
			<tr>
			<td>후원 번호</td>
			<td>후원 아이디</td>
			<td>금액</td>	
			<td>후원 날짜</td>
			</tr>
		</thead>
		<tbody>
				<c:choose>
					<c:when test="${empty list }">
						<div>후원내역이 없습니다.</div>
					</c:when>
					<c:otherwise>
						<c:forEach items="${list }" var="dto">
							<tr>
								<td>${dto.dona_no }</td>
								<td>${dto.member_id }</td>
								<td>${dto.dona_bill }</td>
								<td>${dto.dona_date }</td>
							</tr>
						</c:forEach>
					</c:otherwise>
				</c:choose>
		</tbody>
		<tfoot>
			<tr>
				<td colspan="10"><input id="back" type="button" onclick="location.href='admin/adlayout.jsp'" value="돌아가기"></td>
			</tr>
		</tfoot>
	</table>
</div>

	<!-- 풋터-->
	<jsp:include page="../footer/mainFooter.jsp" />
	
</body>

이외에도 위의 dnController 등 사용.