02-易宝支付

网上支付流程:

 

工程结构:

 

代码:

index.jsp

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>网上支付</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>

<body>
	<form action="${pageContext.request.contextPath }/servlet/yeepay/request" method="post">
		<table cellpadding="3px;">
			<tr>
				<td style="text-align: right;">订单号:</td>
				<td><input type="text" name="orderId"/></td>
			</tr>
			<tr>
				<td style="text-align: right;">应付金额:</td>
				<td><input type="text" name="amount"/> 元</td>
			</tr>
			<tr>
				<td style="text-align: right;">请选择支付银行:</td>
				<td>
					<input type="radio" name="pd_FrpId" value="CMBCHINA-NET"/> 招商银行
					<input type="radio" name="pd_FrpId" value="ICBC-NET"/> 工商银行 
					<input type="radio" name="pd_FrpId" value="ABC-NET"/> 农业银行 
					<input type="radio" name="pd_FrpId" value="CCB-NET"/> 建设银行 
				</td>
			</tr>
			<tr>
				<td colspan="2" align="right"><input type="submit" value="确认支付"/></td>
			</tr>
		</table>
	</form>
</body>
</html>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">
	
	<display-name>paymentDemo01</display-name>
	<welcome-file-list>
		<welcome-file>login.jsp</welcome-file>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
	</welcome-file-list>
	
	<servlet>
		<servlet-name>PayServlet</servlet-name>
		<servlet-class>servlet.PaymentServlet</servlet-class>
	</servlet>
	<servlet>
		<servlet-name>PaymentResultResponseServlet</servlet-name>
		<servlet-class>servlet.PaymentResultResponseServlet</servlet-class>
	</servlet>
	
	<servlet-mapping>
		<servlet-name>PayServlet</servlet-name>
		<url-pattern>/servlet/yeepay/request</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>PaymentResultResponseServlet</servlet-name>
		<url-pattern>/servlet/yeepay/response</url-pattern>
	</servlet-mapping>
</web-app>

PaymentServlet.java

package servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import utils.ConfigInfo;
import utils.PaymentUtil;

public class PaymentServlet extends HttpServlet {
	
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		this.doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		
		// 设置编码
		req.setCharacterEncoding("utf-8");
		
		// 接收参数
		String orderId = req.getParameter("orderId");		// 订单号
		String amount = req.getParameter("amount");			// 支付金额
		String pd_FrpId = req.getParameter("pd_FrpId").toUpperCase();		// 选择的支付银行
		// 读取配置文件信息
		String p1_MerId = ConfigInfo.getValue("p1_MerId");	// 商户编号
		String keyValue = ConfigInfo.getValue("keyValue");	// 密钥
		String merchantCallbackURL = ConfigInfo.getValue("merchantCallbackURL");	// 商户接收支付成功数据的地址
		// 其他信息
		String messageType = "Buy";		// 业务类型,请求命令,在线支付固定为Buy
		String currency = "CNY";		// 货币单位
		String productDesc = "";		// 商品描述(无需填写)
		String productCat = "";			// 商品种类(无需填写)
		String productId = "";			// 商品ID(无需填写)
		String addressFlag = "0";		// 是否需要填写送货信息,0:不需要	1:需要
		String sMctProperties = "";		// 商家扩展信息(无需填写)
		String pr_NeedResponse = "0";	// 应答机制
		
		// MD5加密
		String hmac = PaymentUtil.buildHmac(messageType, p1_MerId, orderId, amount, currency, productId, productCat, 
				productDesc, merchantCallbackURL, addressFlag, sMctProperties, pd_FrpId, pr_NeedResponse, keyValue);
		
		// 将支付信息放入request域
		req.setAttribute("messageType", messageType);
		req.setAttribute("p1_MerId", p1_MerId);
		req.setAttribute("orderId", orderId);
		req.setAttribute("amount", amount);
		req.setAttribute("currency", currency);
		req.setAttribute("productId", productId);
		req.setAttribute("productCat", productCat);
		req.setAttribute("productDesc", productDesc);
		req.setAttribute("merchantCallbackURL", merchantCallbackURL);
		req.setAttribute("addressFlag", addressFlag);
		req.setAttribute("sMctProperties", sMctProperties);
		req.setAttribute("pd_FrpId", pd_FrpId);
		req.setAttribute("pr_NeedResponse", pr_NeedResponse);
		req.setAttribute("hmac", hmac);
		
		// 转发请求
		req.getRequestDispatcher("/WEB-INF/page/connection.jsp").forward(req, resp);
	}
	
}

工具类:

DigestUtil.java

 

package utils;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

public class DigestUtil {

	private static String encodingCharset = "UTF-8";

	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();
	}

	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) {
		// 参数1: 明文(要加密的数据) 参数2: 密钥
		System.out.println(DigestUtil.hmacSign("11111", "abc"));
		System.out.println(DigestUtil.hmacSign("11111", "abd"));
		// 解决数据安全性问题: 把明文加密--->密文 然后把明文和密文都交给易宝
		// 易宝拿到数据后,把传过来的明文加密, 和传过来密文比较,如果相等数据没有被篡改 (商家与易宝加密时都用的是相同key)
	}
}

PaymentUtil.java

package utils;

/**
 * 根据传入的商品信息或支付信息,生成md5字符串
 */
public class PaymentUtil {
	/**
	 * 生成md5加密字串的方法:
	 * 
	 * @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 经过md5加密后的字符串
	 */
	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) {
		
		StringBuffer value = new StringBuffer();
		// 业务类型
		value.append(p0_Cmd);
		// 商户编号
		value.append(p1_MerId);
		// 商户订单号
		value.append(p2_Order);
		// 支付金额
		value.append(p3_Amt);
		// 交易币种
		value.append(p4_Cur);
		// 商品名称
		value.append(p5_Pid);
		// 商品种类
		value.append(p6_Pcat);
		// 商品描述
		value.append(p7_Pdesc);
		// 商户接收支付成功数据的地址
		value.append(p8_Url);
		// 送货地址
		value.append(p9_SAF);
		// 商户扩展信息
		value.append(pa_MP);
		// 银行编号
		value.append(pd_FrpId);
		// 应答机制
		value.append(pr_NeedResponse);
		
		// MD5加密
		String md5Str = DigestUtil.hmacSign(value.toString(), keyValue);
		return md5Str;
	}
	
	/**
	 * 校验易宝支付网关返回的数据是否合法(有可能被人篡改),还是加密后比较
	 * @param hmac			易宝网关经过MD5加密后发送过来的加密字串	
	 * @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				true:合法		false:不合法
	 */
	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) {
		
		StringBuffer value = new StringBuffer();
		// 商户编号
		value.append(p1_MerId);
		// 业务类型
		value.append(r0_Cmd);
		// 支付结果
		value.append(r1_Code);
		// 易宝支付交易流水号
		value.append(r2_TrxId);
		// 支付金额
		value.append(r3_Amt);
		// 交易币种
		value.append(r4_Cur);
		// 商品名称
		value.append(r5_Pid);
		// 商户订单号
		value.append(r6_Order);
		// 易宝支付会员ID
		value.append(r7_Uid);
		// 商户扩展信息
		value.append(r8_MP);
		// 交易结果返回类型
		value.append(r9_BType);
		
		// MD5加密
		String yeepayCallbackMd5Str = DigestUtil.hmacSign(value.toString(), keyValue);
		// 校验是否合法
		if (hmac.equals(yeepayCallbackMd5Str)) {
			return true;
		}
		return false;
	}
}

ConfigInfo.java

package utils;

import java.util.ResourceBundle;

/**
 * 读取配置文件信息
 */
public class ConfigInfo {
	private static ResourceBundle cache = null;
	static {
		try {
			cache = ResourceBundle.getBundle("merchantInfo");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static String getValue(String key) {
		return cache.getString(key);
	}
}

merchantInfo.properties

# customeID
p1_MerId=10001126856
# key
keyValue=69cl522AV6q613Ii4W6u8K6XuW8vM1N6bFgyv769220IuYe9u37N4y7rI4Pl
# response url from yeepay
merchantCallbackURL=http\://localhost\:8080/paymentDemo01/servlet/yeepay/response

connection.jsp

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>发起支付请求</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>

<body onload="javascript:document.forms[0].submit();">	<!-- 在生产环境中要自动提交表单 -->
	<!-- 测试网关:http://tech.yeepay.com:8080/robot/debug.action -->
	<!-- 正式环境:https://www.yeepay.com/app-merchant-proxy/node -->
	<form name="yeepay" action="https://www.yeepay.com/app-merchant-proxy/node" method="post">
		<input type="hidden" name="p0_Cmd" value="${messageType }"/>				<!-- 请求命令,在线支付固定为Buy -->
		<input type="hidden" name="p1_MerId" value="${p1_MerId }"/>					<!-- 商户ID -->
		<input type="hidden" name="p2_Order" value="${orderId }"/>					<!-- 商家的交易订单号 -->
		<input type="hidden" name="p3_Amt" value="${amount }"/>						<!-- 支付金额 -->
		<input type="hidden" name="p4_Cur" value="${currency }"/>					<!-- 货币单位 -->
		<input type="hidden" name="p5_Pid" value="${productId }"/>					<!-- 商品ID -->
		<input type="hidden" name="p6_Pcat" value="${productCat }"/>				<!-- 商品种类 -->
		<input type="hidden" name="p7_Pdesc" value="${productDesc }"/>				<!-- 商品描述 -->
		<input type="hidden" name="p8_Url" value="${merchantCallbackURL }"/>		<!-- 交易结果通知地址 -->
		<input type="hidden" name="p9_SAF" value="${addressFlag }"/>				<!-- 是否需要填写送货信息,0:不需要,1:需要 -->
		<input type="hidden" name="pa_MP" value="${sMctProperties }"/>				<!-- 商家扩展信息 -->
		<input type="hidden" name="pd_FrpId" value="${pd_FrpId }"/>					<!-- 银行代号 -->
		<input type="hidden" name="pr_NeedResponse" value="${pr_NeedResponse }"/>	<!-- 应答机制,0:不需要应答,1:需要应答 -->
		<input type="hidden" name="hmac" value="${hmac }"/>							<!-- 所有交易信息经过MD5加密后的字符串 -->
		
		<!-- 为了演示方便,这里添加一个触发按钮(注明:在实际开发中不需要触发,由Servlet转发过来之后自动触发重定向到第三方支付网关的对应地址) -->
		<%-- 您将支付: <span>${amount }</span> 元,订单号为:<span>${orderId }</span>
		<br><br>
		<input type="submit" value="确认支付"/> --%>
	</form>
</body>
</html>

PaymentResultResponseServlet.java

package servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import utils.ConfigInfo;
import utils.PaymentUtil;

public class PaymentResultResponseServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		this.doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		
		req.setCharacterEncoding("utf-8");
		
		// 从配置文件中获取参数
		String p1_MerId = ConfigInfo.getValue("p1_MerId");
		String keyValue = ConfigInfo.getValue("keyValue");
		
		// 接收易宝支付网关响应的参数
		String r0_Cmd = req.getParameter("r0_Cmd");			// 业务类型
		String r1_Code = req.getParameter("r1_Code");		// 支付结果,1:扣款成功
		String r2_TrxId = req.getParameter("r2_TrxId");		// 易宝支付交易流水号
		String r3_Amt = req.getParameter("r3_Amt");			// 支付金额
		String r4_Cur = req.getParameter("r4_Cur");			// 交易币种
		String r5_Pid = req.getParameter("r5_Pid");			// 商品名称
		String r6_Order = req.getParameter("r6_Order");		// 商户订单号
		String r7_Uid = req.getParameter("r7_Uid");			// 易宝支付会员ID
		String r8_MP = req.getParameter("r8_MP");			// 商户扩展信息
		String r9_BType = req.getParameter("r9_BType");		// 交易结果返回类型,1:浏览器重定向; 2:服务器点对点通讯
		//String rb_BankId = req.getParameter("rb_BankId");	// 支付银行
		//String rp_PayDate = req.getParameter("rp_PayDate");	// 在银行支付时的时间
		String hmac = req.getParameter("hmac");				// MD5交易签名
		
		// 加密
		boolean flag = 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 (flag) {		// 数据来源合法
			// 判断是否扣款成功
			if ("1".equals(r1_Code)) {
				// 如果支付成功,这里要把数据库中对应的订单记录的状态改成"已支付"
				String message = "订单号为:" + r6_Order + " 的订单支付成功!用户支付了" + r3_Amt + "元,交易结果通知类型:";
				if ("1".equals(r9_BType)) {
					message += "浏览器重定向";
				} else {
					message += "易宝支付网关后台程序通知";
				}
				message += ",易宝订单系统中的订单号为:" + r2_TrxId;
				req.setAttribute("message", message);
			} else {
				req.setAttribute("message", "支付失败!");
			}
		} else {
			req.setAttribute("message", "数据来源不合法");
		}
		
		// 转发
		req.getRequestDispatcher("/WEB-INF/page/paymentResult.jsp").forward(req, resp);
	}
	
}

paymentResult.jsp

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>响应支付结果</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>

<body>	
	<h2>支付结果</h2>
	<hr>
	${message }
</body>
</html>

 

运行:

 

 这里输入银行卡号和密码就能支付,就不演示了(完整过程请看http://blog.csdn.net/eson_15/article/details/51447492),支付完之后

会返回信息

 

posted @ 2017-08-23 14:55  半生戎马,共话桑麻、  阅读(231)  评论(0)    收藏  举报
levels of contents