SSH框架网上商城项目第21战之详解易宝支付的流程

时间:2022-07-01 00:34:53

这一节我们先写一个简单点的Demo来测试易宝支付的流程,熟悉这个流程后,再做实际的开发,因为是一个Demo,所以我没有考虑一些设计模式的东西,就是直接实现支付功能。实现支付功能需要易宝给我们提供的API。那么问题来了,使用第三方支付平台最主要的一件事就是获取该平台的API,我们首先得获取他们的API以及开发文档,然后才可以做进一步的开发。

1. 获取易宝的API

获取API的第一步,要在易宝上注册一个账号,这个账号是商家的账号,后面买家付款后,会将钱款存入该账号中,然后商家自己提取到银行卡,易宝在提取过程中收取一定的手续费。这就是易宝的盈利模式。但是注册成功需要前提,那就是自己得有一个网站,或者是一个公司,吧啦吧啦等东西,反正就是你得有资格申请,这点易宝会审核的,满足了才会允许你注册,才会给你提供他们的接口,不是所有人都可以注册的。我用的也是别人注册好的,我自己啥也没有……也没法注册……屌丝一个,大家懂的~但是一般在公司里开发的话,就不会存在这个问题,账号肯定都是有的,最重要的是要掌握开发流程和相关技术~

2. 测试支付流程

有了官方提供的API和技术文档后,就可以着手开发了,在这里主要写一个简单的demo来测试一下易宝支付的流程,demo的结构很简单,一个servlet,一个filter,两个jsp页面和一个加密的工具类。servlet与易宝服务器端打交道,我们做一些跟易宝接口相关的处理,filter是用来处理可能出现的中文乱码问题,两个jsp中一个是前台页面。
我们先来分析一下支付请求的过程,如下所示:

SSH框架网上商城项目第21战之详解易宝支付的流程

好了,下面我们具体分析一下demo中的相关代码:

2.1 前台测试页面

首先看一下前台页面index.jsp的具体代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<%@ 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处理,关于这些属性值,我截了个图,如下:

SSH框架网上商城项目第21战之详解易宝支付的流程  

这些参数名有些在实际项目中是前台传进来的,比如上面写的订单号,要付多少钱,这些在订单确认的时候都会带过去,那么其他参数,必填的话,需要在servlet里指定好,非必填字段的话,就可以为空,这里的空不是null,而是”“,后面servlet中会提到。
再看看两个银行中对应的value值也是固定的,易宝会提供它所支持的所有银行的value值,这些都是固定的,不能修改的。这里就写两个银行测试一下效果。
最后那个隐藏字段是用来在servlet中做判断的,是支付还是支付成功后的返回,下面在sevlet中会说明。

2.2 Servlet处理请求

servlet主要处理与易宝的相关请求,里面有两个部分的内容,一部分是向易宝发送明文和密文,另一部分是判断易宝发过来的明文和密文,我们看看demo中具体的实现代码:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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 加密算法

明文转密文所用到的加密算法由易宝提供,我们只需要用它将明文转为密文即可,算法如下:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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页面:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<%@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为https://www.yeepay.com/app-merchant-proxy/node,这也是易宝官方提供的,我们写成这个就可以了。其实就一个submit按钮,点击submit按钮就能将明文和密文提交过去了。我们看一下测试结果:

3. 测试支付结果

  简陋的测试前台index.jsp~~~:

SSH框架网上商城项目第21战之详解易宝支付的流程

提交后会到reqpay,jsp,点击提交按钮后的效果如下,我们将工行和建行都测一下:

SSH框架网上商城项目第21战之详解易宝支付的流程

SSH框架网上商城项目第21战之详解易宝支付的流程 

支付流程都没啥问题,本来准备去工行交个1分钱看一下支付完成后的结果,结果发现U盾过期了,因为现在用支付宝比较方便嘛……就没去更新U盾了,但是我开通过工行的e支付,所以上面那个界面中也可以使用e支付,于是我就很大方的付了1分钱~~结果如下:

SSH框架网上商城项目第21战之详解易宝支付的流程 

然后会跳转到我们之前指定的页面,也就是同济大学咯……好了,测试完成了,整个支付流程结束!
这一节主要是通过一个简单的demo测试一下,看能否和银行的支付界面接上,现在测试是没问题的,已经接上了,后面只要照常支付即可。简单的demo就介绍到这吧,后面就真正继续我们之前的网上商城项目的在线支付模块的开发了。

原文地址:http://blog.csdn.net/eson_15/article/details/51447492

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。