当前位置: 移动技术网 > IT编程>开发语言>Java > SSH框架网上商城项目第21战之详解易宝支付的流程

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

2019年07月22日  | 移动技术网IT编程  | 我要评论
这一节我们先写一个简单点的demo来测试易宝支付的流程,熟悉这个流程后,再做实际的开发,因为是一个demo,所以我没有考虑一些设计模式的东西,就是直接实现支付功能。实现支付

这一节我们先写一个简单点的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就介绍到这吧,后面就真正继续我们之前的网上商城项目的在线支付模块的开发了。

原文地址:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网