Java在线支付(利用易宝支付的接口)
随着现在电商等平台如雨后春笋般的发展,在线支付越来越火,各种移动端的支付也是层出不穷,什么微信支付、微博支付等等,其实万变不离其宗,今天大桥就给大家讲解一个Java利用易宝支付在线支付的例子,当然首先要感谢一些传智播客的黎活明老师。
- 在线支付的第一步,也就是用户在线支付看到的第一个页面,这个页面里主要包含三项:订单号、金额、所选银行,这三个缺一不可。
<%@ page language="java" import="java.util.*" pageEncoding="GBK"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>支付第一步,选择支付银行</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <table width="960" border="0" align="center"> <tr> <td width="536" valign="top"> <form action="${pageContext.request.contextPath}/servlet/yeepay/PaymentRequestServlet" method="post" name="paymentform"> <table width="100%" border="0"> <tr> <td height="30" colspan="4"><table width="100%" height="50" border="0" cellpadding="0" cellspacing="1" bgcolor="#A2E0FF"> <tr> <td align="center" bgcolor="#F7FEFF"><h3>订单号:<INPUT TYPE="text" NAME="orderid"> 应付金额:¥<INPUT TYPE="text" NAME="amount" size="6">元</h3></td> </tr> </table></td> </tr> <tr> <td colspan="4"> </td> </tr> <tr> <td height="30" colspan="4" bgcolor="#F4F8FF"><span class="STYLE3">请您选择在线支付银行</span> </td> </tr> <tr> <td width="26%" height="25"><INPUT TYPE="radio" NAME="pd_FrpId" value="CMBCHINA-NET">招商银行 </td> <td width="25%" height="25"><INPUT TYPE="radio" NAME="pd_FrpId" value="ICBC-NET">工商银行</td> <td width="25%" height="25"><INPUT TYPE="radio" NAME="pd_FrpId" value="ABC-NET">农业银行</td> <td width="24%" height="25"><INPUT TYPE="radio" NAME="pd_FrpId" value="CCB-NET">建设银行 </td> </tr> <tr> <td height="25"><INPUT TYPE="radio" NAME="pd_FrpId" value="CMBC-NET">中国民生银行总行</td> <td height="25"><INPUT TYPE="radio" NAME="pd_FrpId" value="CEB-NET" >光大银行 </td> <td height="25"><INPUT TYPE="radio" NAME="pd_FrpId" value="BOCO-NET">交通银行</td> <td height="25"><INPUT TYPE="radio" NAME="pd_FrpId" value="SDB-NET">深圳发展银行</td> </tr> <tr> <td height="25"><INPUT TYPE="radio" NAME="pd_FrpId" value="BCCB-NET">北京银行</td> <td height="25"><INPUT TYPE="radio" NAME="pd_FrpId" value="CIB-NET">兴业银行 </td> <td height="25"><INPUT TYPE="radio" NAME="pd_FrpId" value="SPDB-NET">上海浦东发展银行 </td> <td ><INPUT TYPE="radio" NAME="pd_FrpId" value="ECITIC-NET">中信银行</td> </tr> <tr> <td colspan="4"> </td> </tr> <tr> <td colspan="4" align="center"><input type="submit" value=" 确认支付 " /></td> </tr> </table> </form> </td> <td colspan="2" valign="top"><div class="divts"><table width="400" border="0" align="center" cellpadding="5" cellspacing="0"> </table> </div> <div id="blankmessage"></div> </td> </tr> <tr> <td> </td> <td width="290"> </td> <td width="120"> </td> </tr> </table> </body> </html>
- 取得前台提交的三个参数,并封装易宝支付所需的其他参数,并把这些参数放到Request对象中。
package cn.bridgeli.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 cn.bridgeli.utils.ConfigInfo; import cn.bridgeli.utils.PanymentUtil; /** * 发起支付请求 */ public class PaymentRequestServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("GBK"); String orderid = request.getParameter("orderid");//订单号 String amount = request.getParameter("amount");//支付金额 String pd_FrpId = request.getParameter("pd_FrpId");//选择的支付银行 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"; // 应答机制 String md5hmac = PanymentUtil.buildHmac(messageType, p1_MerId, orderid, amount, currency, productId, productCat, productDesc, merchantCallbackURL, addressFlag, sMctProperties, pd_FrpId, pr_NeedResponse, keyValue); request.setAttribute("messageType", messageType); request.setAttribute("merchantID", p1_MerId); request.setAttribute("orderId", orderid); request.setAttribute("amount", amount); request.setAttribute("currency", currency); request.setAttribute("productId", productId); request.setAttribute("productCat", productCat); request.setAttribute("productDesc", productDesc); request.setAttribute("merchantCallbackURL", merchantCallbackURL); request.setAttribute("addressFlag", addressFlag); request.setAttribute("sMctProperties", sMctProperties); request.setAttribute("frpId", pd_FrpId); request.setAttribute("pr_NeedResponse", pr_NeedResponse); request.setAttribute("hmac", md5hmac); request.getRequestDispatcher("/WEB-INF/page/connection.jsp").forward(request, response); } }
- 因为易宝支付所需的数据都是通过表单提交的,所以取得上一步封装的各个参数,通过一个表单提交到易宝支付提供的接口。所以表单的action应该是易宝支付的接口。
<%@ page language="java" pageEncoding="GBK"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>发起支付请求</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body onload="javascript:document.forms[0].submit()"> <!-- http://tech.yeepay.com:8080/robot/debug.action --> <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="${merchantID}"> <!-- 商家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="${frpId}"> <!-- 银行ID --> <!-- 应答机制 为“1”: 需要应答机制;为“0”: 不需要应答机制 --> <input type="hidden" name="pr_NeedResponse" value="0"> <input type='hidden' name='hmac' value="${hmac}"><!-- MD5-hmac验证码 --> </form> </body> </html>
- 编写易宝支付处理上一步处理数据完成后所返回的后台处理类(这个处理类必须在公网上,即使是测试程序,即可以访问的),并根据易宝支付所返回的数据,并完成一下几件事(也就是接收易宝支付处理结果的类):
(1). 判断支付的结果
(2). 把支付结果保存到数据库(写这个方法的时候一定要注意,防止用户网络比较卡时,用户刷新页面,导致表单重复提交的问题)
(3). 根据支付结果来控制前台跳转的页面。
package cn.bridgeli.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 cn.bridgeli.utils.ConfigInfo; import cn.bridgeli.utils.PanymentUtil; /** * 响应银行支付结果请求 */ public class PaymentResutlResponseServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("GBK"); String merchantID = ConfigInfo.getValue("p1_MerId"); // 商家ID String keyValue = ConfigInfo.getValue("keyValue"); // 商家密钥 String sCmd = request.getParameter("r0_Cmd"); //业务类型 String sResultCode = request.getParameter("r1_Code"); //扣款结果,该字段值为1时表示扣款成功. String sTrxId = request.getParameter("r2_TrxId"); //YeePay易宝交易订单号 String amount = request.getParameter("r3_Amt");//扣款金额,交易结束后,YeePay易宝交易系统将实际扣款金额返回给商户 String currency = request.getParameter("r4_Cur");//交易币种,人民币为CNY String productId = request.getParameter("r5_Pid");//商品ID String orderId = request.getParameter("r6_Order");//商户订单号 String userId = request.getParameter("r7_Uid");//YeePay易宝会员ID String mp = request.getParameter("r8_MP");//商户扩展信息,可以任意填写1K 的字符串,交易返回时将原样返回 String bType = request.getParameter("r9_BType");//交易结果通知类型,1: 交易成功回调(浏览器重定向)2: 交易成功主动通知(服务器点对点通讯) String rb_BankId = request.getParameter("rb_BankId");//支付银行 String rp_PayDate = request.getParameter("rp_PayDate");//在银行支付时的时间 String hmac = request.getParameter("hmac");//MD5交易签名 boolean result = PanymentUtil.verifyCallback(hmac, merchantID, sCmd, sResultCode, sTrxId, amount, currency, productId, orderId, userId, mp, bType, keyValue); if(result){ if("1".equals(sResultCode)){ //你们这个地方应该把数据库中订单的支付状态设置成已经支付. String message = "订单号为:"+ orderId+ "的订单支付成功了"; message += ",用户支付了"+ amount +"元"; message +=",交易结果通知类型:"; if("1".equals(bType)){ message += "浏览器重定向"; }else if("2".equals(bType)){ message += "易宝支付网关后台程序通知"; } message += ",易宝订单系统中的订单号为:"+ sTrxId; request.setAttribute("message", message); }else{ request.setAttribute("message", "用户支付失败"); } }else{ request.setAttribute("message", "数据来源不合法"); } request.getRequestDispatcher("/WEB-INF/page/paymentResult.jsp").forward(request, response); } }
- 编写前台页面,告诉用户支付结果。
<%@ page language="java" pageEncoding="GBK"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>支付结果</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body > <center><h3><font color="red"> ${message } </font></h3></center> </body> </html>
另外配置文件的信息如下:
p1_MerId=10000326625 keyValue=0acqgug6x57m0wrsiod6clpn1ezh47r2ot5h1zkq5dztiic8y5xkm5g0p0ek merchantCallbackURL=http://localhost:8080/payment/servlet/yeepay/PaymentResutlResponse
这p1_MerId和keyValue是由易宝支付提供的,申请的时候易宝支付会提供,merchantCallbackURL就是易宝支付处理数据完成后,接收易宝支付处理结果的类。
根据如上代码,就能完成在线支付了,如果想了解更深更多的细节,可以查看易宝支付的官方文档。
补记:今天(2015 01 07 18:01)偶然翻这篇文章,发现三个很重要的文件竟然没有,这里加上
ConfigInfo 读取配置文件,这个很简单
package cn.bridgeli.utils; import java.util.Properties; /** * 读取配置文件 * */ public class ConfigInfo { private static Properties cache = new Properties(); static{ try { cache.load(ConfigInfo.class.getClassLoader().getResourceAsStream("merchantInfo.properties")); } catch (Exception e) { e.printStackTrace(); } } /** * 获取指定key的值 * @param key * @return */ public static String getValue(String key){ return cache.getProperty(key); } }
PanymentUtil 生成易宝所需要的MD5校验码,和易宝返回的数据校验
package cn.bridgeli.utils; public class PanymentUtil { /** * 生成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) { StringBuffer sValue = new StringBuffer(); // 业务类型 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); String sNewString = DigestUtil.hmacSign(sValue.toString(), keyValue); return sNewString; } /** * 返回校验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) { StringBuffer sValue = new StringBuffer(); // 商户编号 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 = DigestUtil.hmacSign(sValue.toString(), keyValue); if (hmac.equals(sNewString)) { return true; } return false; } }
DigestUtil 生成校验码时,所用的工具类
package cn.bridgeli.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"; /** * @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")); // } }
本文算是支付的一个入门级的例子吧,代码组织的并不好,但是真正的完成支付是没有问题的
全文完,如果本文对您有所帮助,请花 1 秒钟帮忙点击一下广告,谢谢。
作 者: BridgeLi,https://www.bridgeli.cn
原文链接:http://www.bridgeli.cn/archives/56
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
作 者: BridgeLi,https://www.bridgeli.cn
原文链接:http://www.bridgeli.cn/archives/56
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
分类: Java
拜读。 希望这样的好贴多些,再多些!!
谢谢,这只是我学习的个人笔记,还请多多指教