作者微信 bishe2022

代码功能演示视频在页面下方,请先观看;如需定制开发,联系页面右侧客服

项目环境概述   工具集下载

Eclipse:

版本: eclipse-mars

下载地址:  https://pan.baidu.com/s/1ci4Nim

Tomcat:

版本: apache-tomcat-7.0.59

下载地址:  https://pan.baidu.com/s/1eS6JlrO

Maven:

版本: apache-maven-3.0.2

下载地址:  https://pan.baidu.com/s/1i4Ud35F

JDK:

版本: JDK1.7

下载地址: https://pan.baidu.com/s/1jHWJSdK


1. 项目准备

    项目运行前准备: 本项目为maven工程, 工程所有依赖的jar包需要根据pom.xml进行下载

没有使用过maven工程的小伙伴请阅读下 【eclipse如何导入和配置maven工程】,看完之后,你就可以将项目跑起来了,

本项目分为二个工程,一个是 ZhiFuBaoClient(商家A),另一个是 ZhiFuBaoServer(支付宝服务B)

商家A与支付宝B的交互需要使用到加密算法RSA, 如果不了解RSA, 请参照  RSA实例详解

商家A 需要配备 RSA加密算法的 私钥与公钥,     支付宝服务B 需要配备RSA加密算法的 私钥与公钥,

同时将A的公钥交给B, 将B的公钥交给A, 这样A,B两端就互相拥有对方的公钥

A拥有的密钥:   A自己的公钥,私钥,B的公钥(对B的数据进行验签, 验签通过,则表明数据未被篡改)

B拥有的密钥:   B自己的公钥,私钥,A的公钥(对A的数据进行验签,验签通过,则表明数据未被篡改);

由于A需要使用HttpUtils.sendPost将数据发送给B,在A中进行B接口路径的配置,如下图:

blob.png

本demo实现类似 支付的功能, A将购买的产品数据进行组装加密 发送给 B, B进行数据校验,如果数据正确

则将支付结果信息返回给A

blob.png


2. 项目运行

   2.1 启动 项目 ZhiFuBaoClient 和 ZhiFuBaoServer 

       启动完成后,ZhiFuBaoClient是有页面的, 在浏览器中输入  http://localhost:8080/ZhiFuBaoClient/pay/getPayPage.htm, 见下图

       blob.png

   2.2 输入产品编号,产品名称,购买数量, 产品总价后(四个编辑框必填,使用BootstrapValid进行校验)

       点击【支付】, 如下 图B 所示,则支付成功说明项目运行正常

       blob.png

                                             图 A

       blob.png

                                              图B

   


3. 项目详解

   3.1 客户端前台代码, 代码实现图B的效果,点击【支付】通过ajax方式向后台发送数据

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%String contextPath = request.getContextPath();%>
<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <title>测试页面</title>
  
   <link rel="stylesheet" href="<%=contextPath%>/jsLib/bootstrap3/css/bootstrap.min.css">
   
</head>
<body>
    <!-- 添加商品 -->
    <div id="myModal">
   <div>
       <div>
           <form id="goodsInfoForm" role="form" style="margin-left: 30px; margin:30px;">
    <div>
       <label for="goodsNo" class="col-sm-3 control-label"><span>*</span>产品编号:</label>
       <div>
          <input ng-model="goods.goodsNo" type="text" id="goodsNo" name="goodsNo" 
                        value='' placeholder="请输入产品编号(必填)"/>
       </div>
     </div>

    <div>
       <label for="goodsName" class="col-sm-3 control-label"><span>*</span>产品名称:</label>
       <div>
          <input ng-model="goods.goodsName" type="text" id="goodsName"  name="goodsName" 
                        value='' placeholder="请输入产品名称(必填)">
       </div>
     </div>
     
     <div>
       <label for="goodsNum" class="col-sm-3 control-label"><span>*</span>购买数量:</label>
       <div>
          <input ng-model="goods.goodsNum" type="text" id="goodsNum"  name="goodsNum" 
                        value='' placeholder="请输入购买数量(必填)">
       </div>
     </div>
     
     <div>
       <label for="goodsMoney" class="col-sm-3 control-label"><span>*</span>产品总价:</label>
       <div>
          <input ng-model="goods.goodsPrice" type="text" id="goodsMoney" name="goodsMoney" 
                        value='' placeholder="请输入产品总价(总价)">
       </div>
     </div>
     
     <div style="text-align:center;">
       <button type="button" class="btn btn-primary" id="pay">支付<tton>
       <button type="button" class="btn btn-default closeModel">关闭<tton>
                      </div>
</form>
       </div>
   </div>
</div>
</body>
<script type="text/javascript" src="<%=contextPath%>/jsLib/jquery/jquery.js"></script>
<script type="text/javascript" src="<%=contextPath%>/jsLib/bootstrap3/js/bootstrap.min.js"></script>
<script type="text/javascript" src="<%=contextPath %>/jsLib/bootstrapValid/bootstrapValidator.js"></script>
 
<script>
$(function () {
 
$('#pay').click(function(){
var bSuccess = $('#goodsInfoForm').data("bootstrapValidator").isValid();
if(bSuccess){
var goodsData = $('#goodsInfoForm').serialize();
$.ajax({
        url: '<%=contextPath%>/pay/payOrder.htm',
        type: 'post',
        data: goodsData,
        success: function (data, status) {
        data = JSON.parse(data); 
        if(data.stateCode == 0){
       alert(data.desc);
        }else{
      alert(data.desc);
        }
        },
        fail: function (err, status) {
          console.log(err)
        }
      });
}else{
alert('信息填写不完整,不能支付!');
}

});
 
 
 
 
//校验form表单
//校验bootstrap form表单
  $('#goodsInfoForm')
   .bootstrapValidator({
       message: 'This value is not valid',
       feedbackIcons: {
           valid: 'glyphicon glyphicon-ok',
           invalid: 'glyphicon glyphicon-remove',
           validating: 'glyphicon glyphicon-refresh'
       },
       fields: {
           'goodsNo': {
               validators: {
                   notEmpty: {
                       message: '产品编号不能为空'
                   },
                   stringLength: {
                       min: 4,
                       message: '产品编号最少4位'
                   },
                   numeric: {
                    message: '税额只能输入数字'
                   } 
               }
           },
           'goodsName': {
               validators: {
                notEmpty: {
                        message: '产品名称不能为空'
                    }
               }
           },
           'goodsNum': {
               validators: {
                notEmpty: {
                        message: '购买数量不能为空'
                    },
                    numeric: {
                     message: '税额只能输入数字'
                    } 
               }
           },
           'goodsMoney': {
               validators: {
                notEmpty: {
                        message: '产品总价不能为空'
                    },
                    numeric: {
                     message: '税额只能输入数字'
                    } 
               }
           }
       }
   }).on('success.form.bv', function(e) {
    alert('aaaaa');
   });

});
</script>
</html>


   3.2 客户端后台代码, 将前台的数据使用 商户RSA密钥进行加密, 将加密信息调用 HttpUtils.sendPost给支付宝,

       支付宝返回结果后,客户端使用 RSAUtils.verify校验响应结果, 将结果返回给页面

/**
	 * 支付订单
	 * @param map
	 * @param request
	 * @return
	 */
	@RequestMapping("/payOrder.htm")
	@ResponseBody
    public Result payOrder(PayOrderRequestDTO payOrderRequest, ModelMap map, HttpServletRequest request)  throws Exception
    {
		Result result = new Result();
		
		PayCommonDTO payCommonDto = PayUtil.getPayCommon();
		String timeStamp = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss:SSS");
		payCommonDto.setTimeStamp(timeStamp);
		payCommonDto.setService("payOrder");
		
		PayOrderDTO payOrderDto = new PayOrderDTO();
		BeanUtils.copyProperties(payCommonDto, payOrderDto);
		payOrderDto.setReqData(payOrderRequest);
		PayUtil.addSign(payOrderDto);
		
		String strSendContent = PayUtil.convertToXml(payOrderDto, "message");
		System.out.println("请求消息:" + strSendContent);
		String strRecvContent = HttpUtils.sendPost(payOrderDto.getUrl(), strSendContent);
		System.out.println("接收消息:" + strRecvContent);
		
		PayOrderDTO payOrderRes = null;
		String resData = StringUtil.getXmlTagValue(strRecvContent, "resData");
		payOrderRes = PayUtil.converyToJavaBean(strRecvContent);
		String signcoent = payOrderRes.getResCode() + resData + payOrderRes.getResMsg();
		//临时把验签去掉
		boolean bVerify = RSAUtils.verify(signcoent, payOrderDto.getZhiFuBaoPublicKey(), 
		                                            payOrderRes.getSign());
		if(bVerify){
			System.out.println("验签成功!!");
		}else{
			System.out.println("验签不成功!!");
		}
		
		if(payOrderRes.getResCode().equals("success")){
			result.setStateCode("0");
			result.setDesc(payOrderRes.getResMsg());
		}else if (payOrderRes.getResCode().equals("false")){
			result.setStateCode("-1");
			result.setDesc(payOrderRes.getResMsg());
		}
		System.out.println(payOrderRes.getResMsg());
    	return result;
    }


 3.3 模拟支付宝的后台代码

     支付宝后台获取客户端数据后,根据与客户端约定的规则,获取需要加密的数据和加密的密文,使用商户公钥检验数据是否被篡改,如果数据完整,将支付结果返回给客户端, 至此 客户端与支付宝的交易流程结束

    //支付宝服务端代码  --支付
    @RequestMapping("/payOrder.htm")
    public void payOrder(ModelMap map, HttpServletRequest request, HttpServletResponse reponse) throws Exception
    {
		String strRecvContent = HttpUtils.getRequestData(request);
		System.out.println("接收消息:" + strRecvContent);
		
		PayOrderDTO payOrderReq = null;
		PayOrderDTO payOrderRes = new PayOrderDTO();
		PayCommonDTO payCommonDto = PayUtil.getPayCommon();
		String timeStamp = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss:SSS");
		payCommonDto.setTimeStamp(timeStamp);
		payCommonDto.setService("payOrder");
		BeanUtils.copyProperties(payCommonDto, payOrderRes);
		
		String reqData = StringUtil.getXmlTagValue(strRecvContent, "reqData");
		payOrderReq = PayUtil.converyToJavaBean(strRecvContent);
		
		PayOrderResponseDTO payOrderResponse = new PayOrderResponseDTO();
		payOrderResponse.setTradeNo(payOrderReq.getReqData().getTradeNo());
		//支付订单
		if (payOrderReq.getService().equals("payOrder")){
			reqData = payOrderReq.getAppkey() + payOrderReq.getCharset() + reqData 
			          + payOrderReq.getService()
			          + payOrderReq.getSignType() + payOrderReq.getTimeStamp();
		
			//使用 商家公钥 对商家数据进行验证
			boolean bVerify = RSAUtils.verify(reqData, PayUtil.getPayCommon().getMerchantPublicKey(), 
			                        payOrderReq.getSign());
			if(bVerify){
				payOrderResponse.setOutTradeNo("33320183793273203972379203082373");
				payOrderRes.setResCode("success");
				payOrderRes.setResMsg("支付成功");
			}else{
				payOrderRes.setResCode("false");
				payOrderRes.setResMsg("数据验签不通过!");
			}
		}else{
			payOrderRes.setResCode("false");
			payOrderRes.setResMsg("调用接口不正确");
		}
		
		payOrderRes.setResData(payOrderResponse);
		PayUtil.addSign(payOrderRes);
		String strSendContent = PayUtil.convertToXml(payOrderRes, "message");
		HttpUtils.writeRespToPage(strSendContent, reponse);
		System.out.println("发送消息:" + strSendContent);
		return;
    }


4. 数据报文

   支付交易过程中,客户端与服务端的数据都有相应的数据格式, 使用私钥加密 与 公钥验密 都需要根据这些数据进行,下面详细介绍一下数据格式 与 加密验密 (数据以XML格式进行传输)

   4.1  下面的为客户端的报文数据,加密前<sign>为空

<?xml version="1.0" encoding="utf-8"?>

<message>
  <service>payOrder</service>
  <appkey>azzfefg84d5d33fd7fd3d26d433</appkey>
  <charset>UTF-8</charset>
  <sign>kbP+CGHmNodKp+Vo+JG62EnETPMq90CqzGlI5bUErU6FarunEnJPAXOa3BMrU7gH4KvQXpT8B0YFw81A8YN7hL0v/fpPaufuyzny+tB+JKadUw+WIieOBfb6nQuMaY/uGvKa/i1/AFwTwaUOlILyCA3b7M7KbK/zERc3sxbZXTo=</sign>
  <signType>RSA</signType>
  <timeStamp>2017-12-17 14:18:01:091</timeStamp>
  <reqData>
    <goodsNo>2332323</goodsNo>
    <goodsName>IPhone8</goodsName>
    <goodsNum>2</goodsNum>
    <goodsMoney>18899</goodsMoney>
  </reqData>
</message>

加密后<sign>为如上图所示

加密字段为:    appKey + 编码格式(UTF-8) + 请求数据体 + serviceName + 加密方式(RSA) + 时间戳, 数据如下:

azzfefg84d5d33fd7fd3d26d433UTF-8<reqData><goodsNo>2332323</goodsNo><goodsName>IPhone8</goodsName><goodsNum>2</goodsNum><goodsMoney>18899</goodsMoney></reqData>payOrderRSA2017-12-17 15:30:21:468

将上面数据使用商户私钥进行加密

商户私钥: 

MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKfeAsDG9JYp0hU5K7Q8XGcwhV1ybuRmRNQL7QYXo6QigzjOPLrV6fVVYx/pbj8wxBIbFzyqZLQiEHm/pixVPucbeK1jke/R26EpENV4kRy3JagbdsqVQv9DPweYcSAmiL7/FAl0WCfEJkXkCagYm1CEtSEJ4ilBHxXp2kKwSqQ7AgMBAAECgYEAnYQ9oRcPuzhS4Ydb8ywQqONmwWD3nWo5e6AVMXpNG18nMs6TPd4sQwF0miU4RiNEWJkDHPHmvQCZ5SRokYEMG9q8rYbajrTzCVnCLWUwQL3YDwUaJchUsH8cREMwvvpoqratgIMEGy89E2MPtRO+ZVWZITHnDa0siHMMUxV+/cECQQDZI+QPrlbDd+gegnql4xm1fxRkX2gxWaGQJZH3IvnCAzHJpFDtU5ulyzbeMxB+5n26g37PfPsMEaU5jAFd6IfHAkEAxei3mIaxVgzgL4H4Nyoj+/xg6BvEsLs0Y1ovurpXond59lwTTyZB59ZvbzIswAYLfPh9phiV3J+3fhklCj2H7QJABz1MGD2+xMuVoJbHEgrNS6DOBD6uEZ8kZNLr1+qBmzdSDJ/+1rrH4LIyxRu8vA5hOLuzmaVYFWHtOUryrLfY9wJAOpvAQxsgSStm+Kq0pyGDpowG5rXSecP2r7V1jQbCDQr0w1BhJ39c5RtLxNJHDla78DZmf1moh72EyYMIxQ+TwQJBAIxW98my6iLXdZrNntIBNTEHp9l9Cr5EKYU0dlbBm+uiDbmukK5STkVw8BGW9rpanWS5xNgeHnK36zJmEz16KM4=


4.2 客户端验密

验密前数据:

<?xml version="1.0" encoding="utf-8"?>
<message>
  <service>payOrder</service>
  <appkey>azzfefg84d5d33fd7fd3d26d433</appkey>
  <charset>UTF-8</charset>     <sign>eJjw7OUpj9FfPdqG1htV+1zc32DI79IcoCCU1TD2j7SsuG7UJz5sc3kn5D6T5GgyGeYnu5cV9e21Xbgxg03E86+HJryWHyoA9QzGwhRGKbd+a5+PEToHJvWchVzzY0C1FrLq5JU+KsPIH+POsYB7nE+L4z15RaHr1uTx4FZC1bk=</sign>
  <signType>RSA</signType>
  <timeStamp>2017-12-17 15:36:31:256</timeStamp>
  <resCode>success</resCode>
  <resMsg>支付成功</resMsg>
  <resData>
    <outTradeNo>33320183793273203972379203082373</outTradeNo>
  </resData>
</message>

使用支付宝公钥进行验密:

验密数据:  结果标志(true, false) + 服务端响应数据  + 结果描述

success<resData><outTradeNo>33320183793273203972379203082373</outTradeNo></resData>支付成功

支付宝公钥:

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCPKkTQG8oCHhcu5ubKk5oGsvp9OpzTbgkGH/Ls6v5KRZcBNGb+RTWFsAkllP+CCG7OhvqPrwaWIolBr6Eymi3ISzezkVSnqqMurH6gjf90zaPHEcpYzdjSlXcpLE+lCCf121q7QqpxkEoJ6QIgMeynRKFyw8CwF43R4NSsyJUyxQIDAQAB



4.3 RSA密钥,公钥加密工具(1024位 与 2048位 两种)

下载地址: https://pan.baidu.com/s/1ge84MgN

blob.png



具体可参见代码, 如果有疑问,请加q: 549710689


Home