javaweb实现在线支付功能
1、在线支付概述
什么是在线支付呢?没错,就是在网上花钱!大家一定有过这样的经历。但是你可能不太了解在线支付的“内情”,下面我们来了解一下!
如果你现在开始经营一个电子商务网站,用户买了东西一定要支付,你的网站一定要可以连接各大银行了,然后在各大银行支付完成后,再返回到你的网站上显示“支付成功”!
这就是今天我们要做的事情,连接银行的网银系统完成支付。说专业一点,我们称之为“开发在线支付的网关”
2、两种在线支付的方式
在线支付一共有两种方式:
*电商直接与银行对接
*电商通过第三方支付平台与银行对接
电商直接与银行对接,这也要银行同意才行,但可惜的是,银行很“牛”,不是谁想与它对接都可以的。如果你的电商每日的资金流量够大,那么银行会和你对接,因为客户支付给电商的钱都存到了银行的帐户中!但是如果资金流量小,银行不会理你的!
当小网站资金量不足时,不能与银行对接,那么它们会选择与第三方支付公司合作。大家也都明白这是些什么公司,例如:支付宝、易宝、财富通、快钱等公司是国内比较有名的。它们这些公司可以与银行对接(因为资金够多),然后小电商与它们对接!但是第三方是要求收费的!第三方一般会收取电商1%的费用,不过不会收客户的钱。
通过上图大家可以了解到,在银行的页面上会显示出商城名称、rmb订单号、订单时间。。。,这些东西银行是怎么知道的,当然是电商传递给银行的。当电商与银行对接后,电商要给银行的页面传递银行页面需要的参数,所以银行的页面才能显示这些数据!
但是,我们的商城不能只可以对接一家银行吧!怎么也要对接boc、ccb、abc、icbc四家吧!不同的银行需要的对接参数是不相同的,这说明我们在开发时要为不同的银行写不同的对接代码。这也是直接与银行对接的缺点!当然与银行直接对接也有好处,就是安全,没有手续费!
为不同的银行开发不同的代码(缺点);
安全(优点);
没有手续费(优点);
小电商银行不让对接(缺点)。
上图中已经说明,客户在电商的网站上点击确认支付后,会定向到第三方的网站,然后再由第三方与银行对接。这说明电商要传递给第三方参数!再由第三方把参数传递给银行。这种方式的好处是:只需要针对第三方开发即可,而不用再为每家银行提供参数。为每家银行提供参数的工作是第三方的任务了。但是,第三方不老可靠的,如果第三方倒闭了,人跑了,那你的钱就没了。因为客户支付的钱没有到你的银行帐户中,而是支付到了第三方的银行帐户中,而你是在第三方有一个帐户。而且第三方还要收手续费,一般是1%,这可不是小数字啊(真黑)。
3、通过第三方在线支付规则
电商想在第三方注册商户,需要向第三方提供icp认证。icp经营许可证是根据国家《互联网管理办法规定》,经营性网站必须办理的网站经营许可证,没有就属于非法经营。
我们不可能因为练习就去办理icp!所以我们无法在第三方注册商户。不过我们已经有现成的在易宝注册的商户,所以这一步就可以忽略了。
当你在易宝注册成功后,易宝会给你如下几样东西:
在易宝的开户账号(即商户编码):10001126856
易宝接入规范:一个chm文件
对称加密算法类:paymentutil.java
密钥:69cl522av6q613ii4w6u8k6xuw8vm1n6bfgyv769220iuye9u37n4y7ri4pl
在易宝接入规范中,我们可以查找到易宝的支付网关,其实就是一个url,用来与易宝对接的一个网址:
在易宝接入规范中,还可以查找到易宝要求的参数,在电商与易宝对接时需要给支付网关传递这些参数:
正式请求地址:
这些参数需要追加到url后面。
但是要注意,这些参数的值需要加密。加密的密钥和加密算法易宝都会提供!
其中p8_url表示当支付成功后,返回到电商的哪个页面。这说明我们需要写一个显示结果的页面。第三方在支付成功后,会重定向到我们指定的返回页面,而且还会带给我们一些参数,我们的页面需要获取这些参数,显示在页面中。下面是第三方返回的参数:
4、开发第三方在线支付系统
步骤:
index.jsp页面:一个表单,提交到buyservlet,表单项有:订单编号、付款金额、选择银行
buyservlet:获取表单数据,准备连接第三方网关。因为在index.jsp页面中只给出3个参数,而第三方需要的参数有n多,页面没有给出的参数由buyservlet补充。而且参数还需要加密,这也需要在buyservlet中完成
backservlet:当用户支付成功后,第三方会重定向到我们指定的返回页面,我们使用backservlet作为返回页面,它用来接收第三方传递的参数,显示在页面中
因为已经有了在易宝的注册商号,所以我们就不用自己去注册商号了。所以这里使用易宝做为第三方支付平台来测试。因为我本人没有电商(必须通过icp认证的电商),所以也不能在第三方注册商号。
我们现在使用的易宝商号是由传智播客提供的,巴巴运动网在易宝注册的商号。所以在测试时支付的钱都给了巴巴运动网在易宝注册的商号了。
第一步:index.jsp
<form action="" method="post"> 订单号:<input type="text" name="p2_order"/><br/> 金 额:<input type="text" name="p3_amt"/><br/> 选择银行:<br/> <input type="radio" name="pd_frpid" value="icbc-net-b2c"/>工商银行 <img src="bank_img/icbc.bmp" align="middle"/> <input type="radio" name="pd_frpid" value="boc-net-b2c"/>中国银行 <img src="bank_img/bc.bmp" align="middle"/><br/><br/> <input type="radio" name="pd_frpid" value="abc-net-b2c"/>农业银行 <img src="bank_img/abc.bmp" align="middle"/> <input type="radio" name="pd_frpid" value="ccb-net-b2c"/>建设银行 <img src="bank_img/ccb.bmp" align="middle"/><br/><br/> <input type="radio" name="pd_frpid" value="boco-net-b2c"/>交通银行 <img src="bank_img/bcc.bmp" align="middle"/><br/> <input type="submit" value="确认支付"/> </form>
每个银行对应的值:
第二步:buyservlet.java
public class buyservlet extends httpservlet { public void dopost(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception { request.setcharacterencoding("utf-8"); response.setcontenttype("text/html;charset=utf-8"); string p0_cmd = "buy";// 业务类型,固定值为buy,即“买” string p1_merid = "10001126856";// 在易宝注册的商号 string p2_order = request.getparameter("p2_order");// 订单编号 string p3_amt = request.getparameter("p3_amt");// 支付的金额 string p4_cur = "cny";// 交易种币,固定值为cny,表示人民币 string p5_pid = "";// 商品名称 string p6_pcat = "";// 商品各类 string p7_pdesc = "";// 商品描述 string p8_url = "http://localhost:8080/buy/backservlet";// 电商的返回页面,当支付成功后,易宝会重定向到这个页面 string p9_saf = "";// 送货地址 string pa_mp = "";// 商品扩展信息 string pd_frpid = request.getparameter("pd_frpid");// 支付通道,即选择银行 string pr_needresponse = "1";// 应答机制,固定值为1 // 密钥,由易宝提供,只有商户和易宝知道这个密钥。 string keyvalue = "69cl522av6q613ii4w6u8k6xuw8vm1n6bfgyv769220iuye9u37n4y7ri4pl"; // 通过上面的参数、密钥、加密算法,生成hmac值 // 参数的顺序是必须的,如果没有值也不能给出null,而应该给出空字符串。 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); // 把所有参数连接到网关地址后面 string url = "https://www.yeepay.com/app-merchant-proxy/node"; url += "?p0_cmd=" + p0_cmd + "&p1_merid=" + p1_merid + "&p2_order=" + p2_order + "&p3_amt=" + p3_amt + "&p4_cur=" + p4_cur + "&p5_pid=" + p5_pid + "&p6_pcat=" + p6_pcat + "&p7_pdesc=" + p7_pdesc + "&p8_url=" + p8_url + "&p9_saf=" + p9_saf + "&pa_mp=" + pa_mp + "&pd_frpid=" + pd_frpid + "&pr_needresponse=" + pr_needresponse + "&hmac=" + hmac; system.out.println(url); // 重定向到网关 response.sendredirect(url); } }
第三步:backservlet
public class backservlet extends httpservlet { public void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception { response.setcontenttype("text/html;charset=utf-8"); /* * 易宝会提供一系列的结果参数,我们获取其中需要的即可 * 获取支付结果:r1_code,1表示支付成功。 * 获取支付金额:r3_amt * 获取电商的订单号:r6_order * 获取结果返回类型:r9_btype,1表示重定向返回,2表示点对点返回, * 但点对点我们收不到,因为我们的ip都是局域网ip。 */ string r1_code = request.getparameter("r1_code"); string r3_amt = request.getparameter("r3_amt"); string r6_order = request.getparameter("r6_order"); string r9_btype = request.getparameter("r9_btype"); if(r1_code.equals("1")) { if(r9_btype.equals("1")) { response.getwriter().print("<h1>支付成功!</h1>");//其实支付不成功时根本易宝根本就不会返回到本servlet response.getwriter().print("支付金额为:" + r3_amt + "<br/>"); response.getwriter().print("订单号为:" + r6_order + "<br/>"); } } } }
易宝支付提供的获取hmac的工具类
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")); // } }
易宝回调
点对点:易宝直接访问电商,这里没有客户端什么事了
这种方式是必须要使用的,我们这种方式是收不到的!因为我们没有固定ip
易宝有一个重发机制,如果它访问你,你不给它回信息,它会一直重发!
电商需要返回一个以success开头的字符串即可!
引导客户端浏览器重定向到电商。是让客户端访问电商!
可以不使用的!
hmac:13参数值+keyvalue(密钥) + 算法(md5)
13参数值:自己设置的!
keyvalue:易宝在我们注册后发给我们的,这个东东只有我们和易宝知道!
底层为md5的算法:paymentutil.buildhmac(14个),它返回hmac
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。