易宝支付
在线支付的两种形式:
1. 电商与银行直连!
安全
不收手续费
不与小电商合作!
2. 第三台支付平台
支付宝
易宝
财富通
好处:
不安全
收手续费(1%)
小电商可以与其合作!
需要在第三方注册账户
需要认证!
我们有一个易宝的测试账户
钱转过去就要不回来了!
易宝支付
1 去银行
易宝给了我们一个网址(支付网关),重定向到这个地址即可!
还需要给这个地址后添加13+1个参数!
https://www.yeepay.com/app-merchant-proxy/node?
p0_Cmd=Buy
&p1_MerId=10001126856
&p2_Order=123456
&p3_Amt=1234.56
&p4_Cur=CNY
&p5_Pid=
&p6_Pcat=&
p7_Pdesc=
&p8_Url=http://localhost:8080/bookstore/OrderServlet?method=back
&p9_SAF=
&pa_MP=
&pd_FrpId= ICBC-NET-B2C
&pr_NeedResponse=1
&hmac=dd17580a3ca176ba62d6d348583ba88b
易宝回调:
点对点:易宝直接访问电商,这里没有客户端什么事了。
• 这种方式是必须要使用的,我们这种方式是收不到的!因为我们没有固定IP
• 易宝有一个重发机制,如果它访问你,你不给它回信息,它会一直重发!
• 电商需要返回一个以SUCCESS开头的字符串即可!
引导客户端浏览器重定向到电商。是让客户端访问电商!
• 可以不使用的!
hmac:13参数值+keyValue(密钥) + 算法(md5)
13参数值:自己设置的!
keyValue:易宝在我们注册后发给我们的,这个东东只有我们和易宝知道!
底层为md5的算法:PaymentUtil.buildHmac(14个),它返回hmac
1) 支付(去银行)
重定向!
13+1个参数!
2) 支付(银行回馈)
校验访问者是否为易宝
修改订单的状态
servlet
/**
* 支付(去银行)
* @param request
* @param response
* @return
* @throws ServletException
* @throws IOException
*/
public String pay(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Properties props = new Properties();
InputStream input = this.getClass().getClassLoader()
.getResourceAsStream("merchantInfo.properties");
props.load(input);
/*
* 准备13参数
*/
String p0_Cmd = "Buy";
String p1_MerId = props.getProperty("p1_MerId");
String p2_Order = request.getParameter("oid");
String p3_Amt = "0.01";
String p4_Cur = "CNY";
String p5_Pid = "";
String p6_Pcat = "";
String p7_Pdesc = "";
String p8_Url = props.getProperty("p8_Url");
String p9_SAF = "";
String pa_MP = "";
String pd_FrpId = request.getParameter("pd_FrpId");
String pr_NeedResponse = "1";
//计算hmac
String keyValue = props.getProperty("keyValue");
String hmac = PaymentUtil.buildHmac(p0_Cmd, p1_MerId, p2_Order, p3_Amt,
p4_Cur, p5_Pid, p6_Pcat, p7_Pdesc, p8_Url, p9_SAF, pa_MP,
pd_FrpId, pr_NeedResponse, keyValue);
//连接易宝网址和 13+1 个参数
StringBuilder url = new StringBuilder(props.getProperty("url"));
url.append("?p0_Cmd=").append(p0_Cmd);
url.append("&p1_MerId=").append(p1_MerId);
url.append("&p2_Order=").append(p2_Order);
url.append("&p3_Amt=").append(p3_Amt);
url.append("&p4_Cur=").append(p4_Cur);
url.append("&p5_Pid=").append(p5_Pid);
url.append("&p6_Pcat=").append(p6_Pcat);
url.append("&p7_Pdesc=").append(p7_Pdesc);
url.append("&p8_Url=").append(p8_Url);
url.append("&p9_SAF=").append(p9_SAF);
url.append("&pa_MP=").append(pa_MP);
url.append("&pd_FrpId=").append(pd_FrpId);
url.append("&pr_NeedResponse=").append(pr_NeedResponse);
url.append("&hmac=").append(hmac);
//重定向到易宝
response.sendRedirect(url.toString());
return null;
}
/**
* 易宝回调方法
* 我们需要判断调用此方法的是否为易宝
* @param request
* @param response
* @return
* @throws ServletException
* @throws IOException
*/
public String back(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 1. 获取11 + 1
*/
String p1_MerId = request.getParameter("p1_MerId");
String r0_Cmd = request.getParameter("r0_Cmd");
String r1_Code = request.getParameter("r1_Code");
String r2_TrxId = request.getParameter("r2_TrxId");
String r3_Amt = request.getParameter("r3_Amt");
String r4_Cur = request.getParameter("r4_Cur");
String r5_Pid = request.getParameter("r5_Pid");
String r6_Order = request.getParameter("r6_Order");
String r7_Uid = request.getParameter("r7_Uid");
String r8_MP = request.getParameter("r8_MP");
String r9_BType = request.getParameter("r9_BType");
String hmac = request.getParameter("hmac");
/*
* 2. 校验访问者是否为易宝!
*/
Properties props = new Properties();
InputStream input = this.getClass().getClassLoader()
.getResourceAsStream("merchantInfo.properties");
props.load(input);
String keyValue = props.getProperty("keyValue");
boolean bool = PaymentUtil.verifyCallback(hmac, p1_MerId, r0_Cmd,
r1_Code, r2_TrxId, r3_Amt, r4_Cur, r5_Pid, r6_Order, r7_Uid,
r8_MP, r9_BType, keyValue);
if(!bool) {//如果校验失败
request.setAttribute("msg", "请走正常流程支付!");
return "f:/jsps/msg.jsp";
}
/*
* 3. 获取状态订单,确定是否要修改订单状态,以及添加积分等业务操作
*/
orderService.pay(r6_Order);//有可能对数据库进行操作,也可能不操作!
/*
* 4. 判断当前回调方式
* 如果为点对点,需要回馈以success开头的字符串
*/
if(r9_BType.equals("2")) {
response.getWriter().print("success");
}
/*
* 5. 保存成功信息,转发到msg.jsp
*/
request.setAttribute("msg", "支付成功!卖家发货中···");
return "f:/jsps/msg.jsp";
}
merchantInfo.properties
p1_MerId=10001126856
keyValue=69cl522AV6q613Ii4W6u8K6XuW8vM1N6bFgyv769220IuYe9u37N4y7rI4Pl
p8_Url=http\://localhost\:8080/bookstore/OrderServlet?method\=back
url=https\://www.yeepay.com/app-merchant-proxy/node
PaymentUtil
public class PaymentUtil {
private static String encodingCharset = "UTF-8";
/**
* 生成hmac方法
*
* @param p0_Cmd 业务类型
* @param p1_MerId 商户编号
* @param p2_Order 商户订单号
* @param p3_Amt 支付金额
* @param p4_Cur 交易币种
* @param p5_Pid 商品名称
* @param p6_Pcat 商品种类
* @param p7_Pdesc 商品描述
* @param p8_Url 商户接收支付成功数据的地址
* @param p9_SAF 送货地址
* @param pa_MP 商户扩展信息
* @param pd_FrpId 银行编码
* @param pr_NeedResponse 应答机制
* @param keyValue 商户密钥
* @return
*/
public static String buildHmac(String p0_Cmd,String p1_MerId,
String p2_Order, String p3_Amt, String p4_Cur,String p5_Pid, String p6_Pcat,
String p7_Pdesc,String p8_Url, String p9_SAF,String pa_MP,String pd_FrpId,
String pr_NeedResponse,String keyValue) {
StringBuilder sValue = new StringBuilder();
// 业务类型
sValue.append(p0_Cmd);
// 商户编号
sValue.append(p1_MerId);
// 商户订单号
sValue.append(p2_Order);
// 支付金额
sValue.append(p3_Amt);
// 交易币种
sValue.append(p4_Cur);
// 商品名称
sValue.append(p5_Pid);
// 商品种类
sValue.append(p6_Pcat);
// 商品描述
sValue.append(p7_Pdesc);
// 商户接收支付成功数据的地址
sValue.append(p8_Url);
// 送货地址
sValue.append(p9_SAF);
// 商户扩展信息
sValue.append(pa_MP);
// 银行编码
sValue.append(pd_FrpId);
// 应答机制
sValue.append(pr_NeedResponse);
return PaymentUtil.hmacSign(sValue.toString(), keyValue);
}
/**
* 返回校验hmac方法
*
* @param hmac 支付网关发来的加密验证码
* @param p1_MerId 商户编号
* @param r0_Cmd 业务类型
* @param r1_Code 支付结果
* @param r2_TrxId 易宝支付交易流水号
* @param r3_Amt 支付金额
* @param r4_Cur 交易币种
* @param r5_Pid 商品名称
* @param r6_Order 商户订单号
* @param r7_Uid 易宝支付会员ID
* @param r8_MP 商户扩展信息
* @param r9_BType 交易结果返回类型
* @param keyValue 密钥
* @return
*/
public static boolean verifyCallback(String hmac, String p1_MerId,
String r0_Cmd, String r1_Code, String r2_TrxId, String r3_Amt,
String r4_Cur, String r5_Pid, String r6_Order, String r7_Uid,
String r8_MP, String r9_BType, String keyValue) {
StringBuilder sValue = new StringBuilder();
// 商户编号
sValue.append(p1_MerId);
// 业务类型
sValue.append(r0_Cmd);
// 支付结果
sValue.append(r1_Code);
// 易宝支付交易流水号
sValue.append(r2_TrxId);
// 支付金额
sValue.append(r3_Amt);
// 交易币种
sValue.append(r4_Cur);
// 商品名称
sValue.append(r5_Pid);
// 商户订单号
sValue.append(r6_Order);
// 易宝支付会员ID
sValue.append(r7_Uid);
// 商户扩展信息
sValue.append(r8_MP);
// 交易结果返回类型
sValue.append(r9_BType);
String sNewString = PaymentUtil.hmacSign(sValue.toString(), keyValue);
return sNewString.equals(hmac);
}
/**
* @param aValue
* @param aKey
* @return
*/
public static String hmacSign(String aValue, String aKey) {
byte k_ipad[] = new byte[64];
byte k_opad[] = new byte[64];
byte keyb[];
byte value[];
try {
keyb = aKey.getBytes(encodingCharset);
value = aValue.getBytes(encodingCharset);
} catch (UnsupportedEncodingException e) {
keyb = aKey.getBytes();
value = aValue.getBytes();
}
Arrays.fill(k_ipad, keyb.length, 64, (byte) 54);
Arrays.fill(k_opad, keyb.length, 64, (byte) 92);
for (int i = 0; i < keyb.length; i++) {
k_ipad[i] = (byte) (keyb[i] ^ 0x36);
k_opad[i] = (byte) (keyb[i] ^ 0x5c);
}
MessageDigest md = null;
try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
return null;
}
md.update(k_ipad);
md.update(value);
byte dg[] = md.digest();
md.reset();
md.update(k_opad);
md.update(dg, 0, 16);
dg = md.digest();
return toHex(dg);
}
public static String toHex(byte input[]) {
if (input == null)
return null;
StringBuffer output = new StringBuffer(input.length * 2);
for (int i = 0; i < input.length; i++) {
int current = input[i] & 0xff;
if (current < 16)
output.append("0");
output.append(Integer.toString(current, 16));
}
return output.toString();
}
/**
*
* @param args
* @param key
* @return
*/
public static String getHmac(String[] args, String key) {
if (args == null || args.length == 0) {
return (null);
}
StringBuffer str = new StringBuffer();
for (int i = 0; i < args.length; i++) {
str.append(args[i]);
}
return (hmacSign(str.toString(), key));
}
/**
* @param aValue
* @return
*/
public static String digest(String aValue) {
aValue = aValue.trim();
byte value[];
try {
value = aValue.getBytes(encodingCharset);
} catch (UnsupportedEncodingException e) {
value = aValue.getBytes();
}
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
return toHex(md.digest(value));
}
// public static void main(String[] args) {
// System.out.println(hmacSign("AnnulCard1000043252120080620160450.0http://localhost/SZXpro/callback.asp杩?4564868265473632445648682654736324511","8UPp0KE8sq73zVP370vko7C39403rtK1YwX40Td6irH216036H27Eb12792t"));
// }
}
后台模块(部分示例)
后台的内容,必须要设置权限!
用户可以访问一个网站的哪些内容?
dao:不行
service:不行
servlet:能!
jsp:能!
用户可以访问的只有WEB层!
1 分类管理
分类管理:
添加分类
查看所有分类
删除分类
按id查询
修改分类
1.1 相关类创建
domain:Category
dao:CategoryDao
service:CategoryService
admin.web.servlet:AdminCategoryServlet(为管理员提供单独的Servlet,然后给这个Servlet添加过滤器!)
1.2 查看所有分类
left.jsp上的菜单修改一下指向:AdminCategoryServlet#findAll()
findAll():
调用service得到所有的分类List<Category>
保存到request域,转发到/adminjsps/admin/category/list.jsp
list.jsp:修改页面,显示所有分类!
1.3 添加分类
add.jsp(表单页面)
AdminCatetgoryServlet#add():
封装表单数据;
补全:cid
调用service方法完成添加工作
调用findAll()方法
service#add(Category c):略
dao#add(Catetgory c):略
1.4 删除分类
list.jsp(删除链接)
AdminCategoryServlet#delete()
获取参数:cid
调用service方法完成删除!
如果出现异常,保存异常信息,转发到msg.jsp显示
调用findAll()
service#delete(String cid):
通过cid查看该分类下的图书本数,如果大于0,抛出异常;
如果等于0,删除该分类;
1.5 修改分类
修改分为两步:1、加载分类, 2、修改分类
第一步:加载分类
list.jsp(修改链接)
AdminCategoryServlet#editPre()
获取cid
通过cid来调用service方法,得到Category对象
保存到request域中,转发到mod.jsp
mod.jsp:把当前的Category对象显示到表单中
第二步:修改分类
mod.jsp(提交表单)
AdminCategoryServlet#edit()
封装表单数据
调用service方法完成修改工作
调用findAll()
2 图书管理
图书管理(我的)
查看所有图书
按id查询
删除图书
修改图书
添加图书(上传图片)
2.1 相关类创建
web.servlet.admin:
AdminBookServlet;
AdminAddBookServlet(添加图书,包含上传):上传不能使用BaseServlet,因为BaseServlet中需要使用getParameter()方法,而上传getParameter()方法就不能再使用了。
2.2 查询所有图书
left.jsp(菜单项(查看图书链接))
AdminBookServlet#findAll()
查询所有图书,保存到request
转发到/adminjsps/admin/book/list.jsp
list.jsp:循环遍历所有图书
2.3 加载图书
list.jsp(点击图名或图片)
AdminBookServlet#load()
获取bid,通过bid调用BookService方法得到Book对象
保存到request中,转发到/adminjsps/admin/book/desc.jsp
desc.jsp:把当前book对象显示到表单中
2.4 添加图书
添加图书分两步:
1. 加载所有分类,到add.jsp中显示!
left.jsp(菜单项:添加图书)
AdminBookServlet#addPre():
查询所有分类,保存到request域,转发到add.jsp
在add.jsp中循环遍历所有分类,显示在<select>中
2. 添加图书
上传三步:
创建工厂
创建解析器
解析request得到表单字段!
把表单字段封装到Book对象中
保存上传文件,把保存的路径设置给Book的image属性。
调用service方法保存Book对象到数据库中
调用findAll()
2.5 删除图书
book表与orderitem有关联关系!
删除图书不是真的数据库表中删除记录,而是给book表添加一个del字段,它是booleanod类型,表示是否已删除!
没有被删除的图书,该列的值为false,否则为true
处理问题:
修改BookDao:所有与查询相关的方法,都需要添加where条件,即del=false
修改Book类,添加del属性!
删除图书:其实就是把表的del列修改为true!
desc.jsp(del按钮)
AdminBookServlet#del()
获取bid
调用service方法完成删除
返回列表,即调用findAll()
2.6 编辑图书
dsec.jsp(编辑按钮)
AdminBookServlet#edit()
封装表单数据(必须让页面保证把image传递过来)
要求页面必须添加一个隐藏字段,把原来的图片路径传递过来!
调用service方法完成删除
return findAll()
servet (上传技术添加图书)
public class AdminAddBookServlet extends HttpServlet {
private BookService bookService = new BookService();
private CategoryService categoryService = new CategoryService();
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 一.把表达那数据封装到Book对象中
* 上传三步:1.创建工厂、 2.得到解析器、 3. 使用解析器解析request对象得到List<FileItem>
*
*/
//1.创建工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//2.使用工厂得到解析器
ServletFileUpload sfu = new ServletFileUpload(factory);
try {
//3.使用解析器解析request,得到FileItem集合
List<FileItem> fileItemList = sfu.parseRequest(request);
/*
* 把fileItemList中的数据封装到Book对象中(包含上传内容,所以不能一键封装)
*
* 解决:将所有的普通文本表单遍历进map,然后对map一键封装
* 做法:
* 1.把所有的普通文本表单数据封装到map中
* 2.把map中数据封装到Book对象中
*/
//遍历:将所有普通文本字段数据放入map
Map<String,String> map = new HashMap<String, String>();
for (FileItem fileItem : fileItemList) {
if(fileItem.isFormField()){ //判断是否为普通文本字段数据
map.put(fileItem.getFieldName(), fileItem.getString("UTF-8"));
}
}
//一键封装map
Book book = CommonUtils.toBean(map, Book.class);
//对book设置uuid
book.setBid(CommonUtils.uuid());
//将Map中cid封装到Category对象中,再将category设置给book
Category category = CommonUtils.toBean(map, Category.class);
book.setCategory(category);
/*
* 二、保存上传的文件
* 1.得到保存目录路径、文件名
* 2.使用文件名和目录创建目标文件
* 3.保存上传文件到目标文件中
*/
//得到图片的保存路径
String savepath = this.getServletContext().getRealPath("/book_img");
//得到表单项集合中第一个元素的name,并添加uuid前缀避免文件名冲突
String fileName = CommonUtils.uuid()+"_"+fileItemList.get(1).getName();
/*
* 校验文件的扩展名
*/
if(!fileName.toLowerCase().endsWith("jpg")) {
request.setAttribute("msg", "您上传的图片不是JPG扩展名!");
request.setAttribute("categoryList", categoryService.findAllCategory());
request.getRequestDispatcher("/adminjsps/admin/book/add.jsp")
.forward(request, response);
return;
}
//2.使用路径和文件名 创建目标文件
File destFile = new File(savepath,fileName);
//3.保存上传文件到目标文件中
fileItemList.get(1).write(destFile);
//三、把图片的路径设置为Book的image
book.setImage("book_img/" + fileName);
//使用BookService完成保存
bookService.add(book);
//转发请求另一个servlet,调用其中findAll,对添加图书进行显示
request.getRequestDispatcher("/admin/AdminBookServlet?method=findAllBook").forward(request, response);
} catch (Exception e) {
}
}
}
jsp
手风琴式下拉菜单使用
<script language="javascript">
/*
* 手风琴式下拉菜单脚本使用规定:
* bar1:参数名必须与对象名相同
* ITCAST网络图书商城:大标题
*/
var bar1 = new Q6MenuBar("bar1", "ywnxbx网络图书商城");
function load() {
//设置配色方案(四种):0、1、2、3 (内部为数组下标)
bar1.colorStyle = 2;
//设置图片所在目录 (手风琴式下拉菜单加减号图片)
bar1.config.imgDir = "<c:url value='/menu/img/'/>";
/*
* 菜单间是否相互排斥
* false(不排斥):扩展菜单可以全部打开、
* true(排斥):只能打开一项扩展菜单,已打开的会被自动关闭)
*/
bar1.config.radioButton=false;
/*
add方法:
第一个参数:被添加的菜单
第二个参数:添加项
第三个参数:添加项的内容(请求地址)
第四个参数:显示内容的框架名称
*/
bar1.add("分类管理", "查看分类", "<c:url value='/admin/AdminCategoryServlet?method=findAll'/>", "body");
bar1.add("分类管理", "添加分类", "<c:url value='/adminjsps/admin/category/add.jsp'/>", "body");
bar1.add("图书管理", "查看图书", "<c:url value='/admin/AdminBookServlet?method=findAllBook'/>", "body");
bar1.add("图书管理", "添加图书", "<c:url value='/admin/AdminBookServlet?method=addPre'/>", "body");
bar1.add("订单管理", "所有订单", "<c:url value='/adminjsps/admin/order/list.jsp'/>", "body");
bar1.add("订单管理", "未付款订单", "<c:url value='/adminjsps/admin/order/list.jsp'/>", "body");
bar1.add("订单管理", "已付款订单", "<c:url value='/adminjsps/admin/order/list.jsp'/>", "body");
bar1.add("订单管理", "未收货订单", "<c:url value='/adminjsps/admin/order/list.jsp'/>", "body");
bar1.add("订单管理", "已完成订单", "<c:url value='/adminjsps/admin/order/list.jsp'/>", "body");
//获取div元素
var d = document.getElementById("menu");
//将菜单对象转换成字符串对象 赋给div
d.innerHTML = bar1.toString();
}
</script>
</head>
<body "load()" style="margin: 0px; background: rgb(254,238,189);">
<div id="menu"></div>
</body>