SSH框架网上商城项目第21战之详解易宝支付的流程
这一节我们先写一个简单点的demo来测试易宝支付的流程,熟悉这个流程后,再做实际的开发,因为是一个demo,所以我没有考虑一些设计模式的东西,就是直接实现支付功能。实现支付功能需要易宝给我们提供的api。那么问题来了,使用第三方支付平台最主要的一件事就是获取该平台的api,我们首先得获取他们的api以及开发文档,然后才可以做进一步的开发。
1. 获取易宝的api
获取api的第一步,要在易宝上注册一个账号,这个账号是商家的账号,后面买家付款后,会将钱款存入该账号中,然后商家自己提取到银行卡,易宝在提取过程中收取一定的手续费。这就是易宝的盈利模式。但是注册成功需要前提,那就是自己得有一个网站,或者是一个公司,吧啦吧啦等东西,反正就是你得有资格申请,这点易宝会审核的,满足了才会允许你注册,才会给你提供他们的接口,不是所有人都可以注册的。我用的也是别人注册好的,我自己啥也没有……也没法注册……屌丝一个,大家懂的~但是一般在公司里开发的话,就不会存在这个问题,账号肯定都是有的,最重要的是要掌握开发流程和相关技术~
2. 测试支付流程
有了官方提供的api和技术文档后,就可以着手开发了,在这里主要写一个简单的demo来测试一下易宝支付的流程,demo的结构很简单,一个servlet,一个filter,两个jsp页面和一个加密的工具类。servlet与易宝服务器端打交道,我们做一些跟易宝接口相关的处理,filter是用来处理可能出现的中文乱码问题,两个jsp中一个是前台页面。
我们先来分析一下支付请求的过程,如下所示:
好了,下面我们具体分析一下demo中的相关代码:
2.1 前台测试页面
首先看一下前台页面index.jsp的具体代码
<%@ page language="java" pageencoding="utf-8"%> <!doctype html public "-//w3c//dtd html 4.01 transitional//en"> <html> <head> <title>前台首页</title> </head> <body> <h1>在线支付演示</h1> <form action="${pagecontext.request.contextpath }/servlet/payservlet" method="post"> 此次购物订单编号<input type="text" name="p2_order" /><br> money<input type="text" name="p3_amt" value="0.01"/><br> 工商银行<input type="radio" value="icbc-net" name="pd_frpid"> 建设银行<input type="radio" value="ccb-net" name="pd_frpid"><br> <input type="submit" value="submit" /> <input type="hidden" value="pay" name="status"/> </form> </body> </html>
从上面的jsp页面中可以看出,这些input标签中的name属性值都很奇怪,pi_功能(i=0,1,2,…,9),当然i还有其他的值,这得参照易宝的官方文档,这些name表示相对应的属性,到时候会传到sevlet处理,关于这些属性值,我截了个图,如下:
这些参数名有些在实际项目中是前台传进来的,比如上面写的订单号,要付多少钱,这些在订单确认的时候都会带过去,那么其他参数,必填的话,需要在servlet里指定好,非必填字段的话,就可以为空,这里的空不是null,而是”“,后面servlet中会提到。
再看看两个银行中对应的value值也是固定的,易宝会提供它所支持的所有银行的value值,这些都是固定的,不能修改的。这里就写两个银行测试一下效果。
最后那个隐藏字段是用来在servlet中做判断的,是支付还是支付成功后的返回,下面在sevlet中会说明。
2.2 servlet处理请求
servlet主要处理与易宝的相关请求,里面有两个部分的内容,一部分是向易宝发送明文和密文,另一部分是判断易宝发过来的明文和密文,我们看看demo中具体的实现代码:
public class payservlet extends httpservlet { public void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception { string status = request.getparameter("status"); if (status.equals("pay")) { //index.jsp中隐藏字段传来的是pay,所以处理支付这部分 // 加密的密钥,用在加密算法中,由支付中介提供,每个商家独一无二的 string keyvalue = "w0p75wmz203fr46r5i70v556whfa94j14yw5j6vuh4yo3nrl5jsqf3c41677"; // 1: 给参数赋值,这些参数(即明文)都是易宝官方提供的文档中所定义的,名字我们不能改 string p0_cmd = formatstring("buy"); string p1_merid = formatstring("10000940764"); string p2_order = formatstring(request.getparameter("p2_order")); string p3_amt = formatstring(request.getparameter("p3_amt")); string p4_cur = formatstring("cny"); string p5_pid = ""; string p6_pcat = ""; string p7_pdesc = ""; string p8_url = "http://www.tongji.edu.cn";//这是支付成功后跳转到的页面,可以设为商城首页,这个demo就用同济大学主页好了…… string p9_saf = "0"; string pa_mp = ""; string pd_frpid = formatstring(request.getparameter("pd_frpid")); pd_frpid = pd_frpid.touppercase(); string pr_needresponse = "0"; string hmac = formatstring("");//hmac是用来存储密文的 /*上面所有的明文都用都用formatstring方法包装了一下,该方法在下面,主要是将null转换成"" *因为null是无法转换成密文的*/ // 解决数据安全性问题: 把明文加密--->密文 然后把明文和密文都交给易宝 // 易宝拿到数据后,把传过来的明文加密, 和传过来密文比较, // 如果相等数据没有被篡改 (商家与易宝加密时都用的是相同key) // 把明文数据追加到stringbuffer,注意追加顺序不能改,否则生成的密文会不同的, // 要严格按照易宝的官方文档说名来写才行,因为易宝那边就是根据文档中的顺序追加的 stringbuffer infobuffer = new stringbuffer(); infobuffer.append(p0_cmd); infobuffer.append(p1_merid); infobuffer.append(p2_order); infobuffer.append(p3_amt); infobuffer.append(p4_cur); infobuffer.append(p5_pid); infobuffer.append(p6_pcat); infobuffer.append(p7_pdesc); infobuffer.append(p8_url); infobuffer.append(p9_saf); infobuffer.append(pa_mp); infobuffer.append(pd_frpid); infobuffer.append(pr_needresponse); // 加密后的密文存储到了hmac中,加密算法易宝会提供的,因为他那边也得用相同的算法 hmac = digestutil.hmacsign(infobuffer.tostring(), keyvalue); // 把明文和密文都存储到request.setattribute中 request.setattribute("p0_cmd", p0_cmd); request.setattribute("p1_merid", p1_merid); request.setattribute("p2_order", p2_order); request.setattribute("p3_amt", p3_amt); request.setattribute("p4_cur", p4_cur); request.setattribute("p5_pid", p5_pid); request.setattribute("p6_pcat", p6_pcat); request.setattribute("p7_pdesc", p7_pdesc); request.setattribute("p8_url", p8_url); request.setattribute("p9_saf", p9_saf); request.setattribute("pa_mp", pa_mp); request.setattribute("pd_frpid", pd_frpid); request.setattribute("pr_needresponse", pr_needresponse); request.setattribute("hmac", hmac); system.out.println("hmac-->" + hmac); //跳转到reqpay.jsp中,将这些信息提交到易宝 request.getrequestdispatcher("/reqpay.jsp").forward(request, response); } else if (status.equals("success")) {//易宝那边传来的是success,处理返回验证部分 printwriter out = response.getwriter(); string keyvalue = "w0p75wmz203fr46r5i70v556whfa94j14yw5j6vuh4yo3nrl5jsqf3c41677"; // 获取所有的明文 string r0_cmd = formatstring(request.getparameter("r0_cmd")); string p1_merid = request.getparameter("p1_merid"); string r1_code = formatstring(request.getparameter("r1_code")); string r2_trxid = formatstring(request.getparameter("r2_trxid")); string r3_amt = formatstring(request.getparameter("r3_amt")); string r4_cur = formatstring(request.getparameter("r4_cur")); string r5_pid = new string(formatstring( request.getparameter("r5_pid")).getbytes("iso-8859-1"), "utf-8"); string r6_order = formatstring(request.getparameter("r6_order")); string r7_uid = formatstring(request.getparameter("r7_uid")); string r8_mp = new string(formatstring( request.getparameter("r8_mp")).getbytes("iso-8859-1"), "utf-8"); string r9_btype = formatstring(request.getparameter("r9_btype")); // 对明文进行数据追加 string hmac = formatstring(request.getparameter("hmac")); stringbuffer infobuffer = new stringbuffer(); infobuffer.append(p1_merid); infobuffer.append(r0_cmd); infobuffer.append(r1_code); infobuffer.append(r2_trxid); infobuffer.append(r3_amt); infobuffer.append(r4_cur); infobuffer.append(r5_pid); infobuffer.append(r6_order); infobuffer.append(r7_uid); infobuffer.append(r8_mp); infobuffer.append(r9_btype); // 对返回的明文进行加密 string md5 = digestutil.hmacsign(infobuffer.tostring(), keyvalue); // 判断加密的密文与传过来的数据签名是否相等 boolean isok = md5.equals(hmac); if (isok && r1_code.equals("1")) {//r1_code为1表示成功 //把支付成功的订单状态改成已支付,并个给用户显示支付成功信息 //调用邮件服务接口,短信发送服务等 //这里就打印一句话呗~ out.println("订单编号为:" + r6_order + "支付金额为:" + r3_amt); } else { out.println("fail !!!!"); } } } public void dopost(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception { doget(request, response); } string formatstring(string text) { if (text == null) { return ""; } return text; } }
2.3 加密算法
明文转密文所用到的加密算法由易宝提供,我们只需要用它将明文转为密文即可,算法如下:
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) } }
加密算法也不去过多的研究了,好像是md5二代加密算法,反正把明文扔进去,肯定加密成密文就行了。下面再看一下reqpay.jsp页面:
<%@page language="java" contenttype="text/html;charset=gbk"%> <html> <head> <title>to yeepay page </title> </head> <body> <form name="yeepay" action='https://www.yeepay.com/app-merchant-proxy/node' method='post' target="_blank"> <input type='hidden' name='p0_cmd' value='${requestscope.p0_cmd}'> <input type='hidden' name='p1_merid' value='${requestscope.p1_merid}'> <input type='hidden' name='p2_order' value='${requestscope.p2_order}'> <input type='hidden' name='p3_amt' value='${requestscope.p3_amt}'> <input type='hidden' name='p4_cur' value='${requestscope.p4_cur}'> <input type='hidden' name='p5_pid' value='${requestscope.p5_pid}'> <input type='hidden' name='p6_pcat' value='${requestscope.p6_pcat}'> <input type='hidden' name='p7_pdesc' value='${requestscope.p7_pdesc}'> <input type='hidden' name='p8_url' value='${requestscope.p8_url}'> <input type='hidden' name='p9_saf' value='${requestscope.p9_saf}'> <input type='hidden' name='pa_mp' value='${requestscope.pa_mp}'> <input type='hidden' name='pd_frpid' value='${requestscope.pd_frpid}'> <input type="hidden" name="pr_needresponse" value="${requestscope.pr_needresponse}"> <input type='hidden' name='hmac' value='${requestscope.hmac}'> <input type='submit' /> </form> </body> </html>
其实该页面很简单,就是将明文和密文一起通过<form>
表单传到易宝,易宝的接收url为,这也是易宝官方提供的,我们写成这个就可以了。其实就一个
submit
按钮,点击submit
按钮就能将明文和密文提交过去了。我们看一下测试结果:
3. 测试支付结果
简陋的测试前台index.jsp~~~:
提交后会到reqpay,jsp,点击提交按钮后的效果如下,我们将工行和建行都测一下:
支付流程都没啥问题,本来准备去工行交个1分钱看一下支付完成后的结果,结果发现u盾过期了,因为现在用支付宝比较方便嘛……就没去更新u盾了,但是我开通过工行的e支付,所以上面那个界面中也可以使用e支付,于是我就很大方的付了1分钱~~结果如下:
然后会跳转到我们之前指定的页面,也就是同济大学咯……好了,测试完成了,整个支付流程结束!
这一节主要是通过一个简单的demo测试一下,看能否和银行的支付界面接上,现在测试是没问题的,已经接上了,后面只要照常支付即可。简单的demo就介绍到这吧,后面就真正继续我们之前的网上商城项目的在线支付模块的开发了。
原文地址:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
下一篇: Django 中 cookie的使用