** 支付解决方案_微信扫码支付(附详细代码,流程图)*

课程目标

  • 掌握二维码生成插件qrious的使用
  • 能够说出微信支付开发的整体思路
  • 能够调用微信支付接口(统一下单)生成支付二维码
  • 能够调用微信支付接口(查询订单)查询支付状态
  • 实现支付日志的生成与订单状态的修改

一. 二维码

1.1 什么是二维码

  • 二维码又称QR Code,QR全称Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的Bar Code条形码能存更多的信息,也能表示更多的数据类型。

  • 二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设备自动识读以实现信息自动处理:它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化点。

  • 图示:

      ![](https://i.imgur.com/Ofjah5h.png)
    

1.2 二维码的优势:

  • 信息容量大, 可以容纳多达1850个大写字母或2710个数字或500多个汉字
  • 应用范围广, 支持文字,声音,图片,指纹等等…
  • 容错能力强, 即使图片出现部分破损也能使用
  • 成本低, 容易制作

1.3 二维码的容错级别:

  • L级(低) 7%的码字可以被恢复。
  • M级(中) 的码字的15%可以被恢复。
  • Q级(四分)的码字的25%可以被恢复。
  • H级(高) 的码字的30%可以被恢复。

二. 二维码生成插件qrious:

2.1 qrious是一款基于HTML5 Canvas的纯JS二维码生成插件。通过qrious.js可以快速生成各种二维码,你可以控制二维码的尺寸颜色,还可以将生成的二维码进行Base64编码。

2.2 qrious.js二维码插件的可用配置参数如下:

方块确定方位,黑色代表1,白色代表0

三. 微信扫码支付

3.1 介绍:

  • 微信扫码支付是商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于 PC 网站支付、实体店单品或订单支付、媒体广告支付等场景。

3.2 申请步骤:(了解)

  • 第一步:注册公众号(类型须为:服务号)
    请根据营业执照类型选择以下主体注册:个体工商户| 企业/公司| 政府| 媒体| 其他类型。

  • 第二步:认证公众号
    公众号认证后才可申请微信支付,认证费:300 元/次。

  • 第三步:提交资料申请微信支付
    登录公众平台,点击左侧菜单【微信支付】,开始填写资料等待审核,审核时间为 1-5个工作日内。

  • 第四步:开户成功,登录商户平台进行验证
    资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填写财付通备付金打的小额资金数额,完成账户验证。

  • 第五步:在线签署协议
    本协议为线上电子协议,签署后方可进行交易及资金结算,签署完立即生效。

四. 微信支付SDK

4.1 微信支付接口调用的整体思路:

  • 按 API 要求组装参数,以 XML 方式发送(POST)给微信支付接口(URL),微信支付接口也是以 XML 方式给予响应。程序根据返回的结果(其中包括支付 URL)生成二维码或判断订单状态
  • 简单来讲:通过URL将一些参数告诉给微信,然后接受返回值生成二维码

4.2 释义:

  • appid:微信公众账号或开放平台 APP 的唯一标识
  • mch_id:商户号 (配置文件中的 partner)
  • partnerkey:商户密钥
  • sign:数字签名, 根据微信官方提供的密钥和一套算法生成的一个加密信息, 就是为了保证交易的安全性【每次都不同】

4.3 微信支付SDK

  • 微信支付提供了 SDK, 下载后打开源码,install 到本地仓库;
  • 导入依赖:
    		<dependency>
    		<groupId>com.github.wxpay</groupId>
    		<artifactId>wxpay-sdk</artifactId>
    		<version>0.0.3</version>
    		</dependency>

4.4 我们主要会用到微信支付 SDK 的以下功能:

  • 获取随机字符串
    WXPayUtil.generateNonceStr()

  • MAP 转换为 XML 字符串(自动添加签名)
    WXPayUtil.generateSignedXml(param, partnerkey)

  • XML 字符串转换为 MAP
    WXPayUtil.xmlToMap(result)

4.5 HttpClient工具类

  • 介绍:

    • HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目Cactus 和 HTMLUnit 都使用了 HttpClient。HttpClient
    • 通俗的讲就是模拟了浏览器的行为,如果我们需要在后端向某一地址提交数据获取结果,就可以使用 HttpClient.
    • 关于 HttpClient(原生)具体的使用不属于我们本章的学习内容,我们这里这里为了简化 HttpClient 的使用,提供了工具类 HttpClient(对原生 HttpClient 进行了封装)
  • HttpClient工具类使用的步骤:

    			HttpClient client=new HttpClient(请求的 url 地址);
    			client.setHttps(true);//是否是 https 协议
    			client.setXmlParam(xmlParam);//发送的 xml 数据
    			client.post();//执行 post 请求
    			String result = client.getContent(); //获取结果

五. 查询状态

5.1 思路:

  • 前端循环调用后端
  • 后端循环调用微信支付查询结果,前端调用后端

5.2 交易状态类型:

  • SUCCESS: 支付成功
  • REFUND : 转入退款
  • NOTPAY: 未支付
  • CLOSED: 已关闭
  • REVOKED: 已撤销
  • USERPAYING: 用户支付中
  • PAYERROR: 支付失败(其他原因,银行返回失败)
  • 支付状态机等可以查看相关文档

六. 支付日志需求

6.1 思路:

  • 在用户下订单时,判断如果为微信支付,就向支付日志表添加一条记录,信息包括支付总金额,订单id(多个),用户id,下单时间等信息,支付状态为0(未支付);

  • 生成的支付日志对象放入reids中,以用户id作为key,这样在生成支付二维码时就可以从redis中提取支付日志对象中的金额和订单号;

  • 当用户支付成功后,修改支付日志的支付状态为1(已支付),并记录微信传递给我们的交易流水号。根据订单id(多个)修改订单的状态为2(已付款).

七. 微信支付准备:

7.1 业务流程:

  • 选择商品以及相应的规格和数量后点击"加入购物车"。
  • 当加入购物车后跳转到购物车列表页面,对收货地址,商品添加或者删除等操作后点击"结算"
  • 当带着参数进入结算页的时候,首先对使用微信支付和货到付款等方式进行判断,然后跳转到相应的页面
  • 页面通过与微信官方交互然后自动生成二维码,用户扫码之后显示"付款成功"或者"付款失败"等.

7.2 技术分析[准备阶段]:

  • 微信支付功能必须为服务号才能申请微信付款功能,这个由公司完成并提供给我们相关的账户和密码;
  • 查看微信支付SDK文档,获取大概的流程;
  • 首先应该在本地仓库中配好微信支付SDK的jar包,并引入微信支付SDK的依赖
  • 使用HttpClient工具类,方便向微信官方发送请求和接收请求;
  • 搭建工程,分为消费方和服务方,消费方应该直接放入用户加入购物车的消费项目里,服务方加入服务方,服务方分成interface和service解耦合;
  • 应该引入HttpClient.java工具类和添加依赖,添加properties配置文件[这里的配置文件应该为商户的信息,如appid,partner,partnerkey…等]

八. 支付流程图

九. 流程及代码

9.1 首先生成微信二维码:

  • 准备:

    • 通过HttpClient工具类实现对远程支付接口的调用
    • 接口连接:https://api.mch.weixin.qq.com/pay/unifiedorder
    • 参考文档:统一下单API
  • 开始:

    • interface:接口服务层应该建立一个生成微信二维码的接口
    • serviceimpl:注入properties中的配置文件,创建一个生成二维码的方法createNative
    • 创建一个map集合,将需要发送给微信的数据存入map集合内
    • 通过WXPayUtil工具类将集合内的数据转为XML格式数据
    • 通过HttpClient发送给微信
    • 获取XML结果后调用WXPayUtil工具类将它转为Map格式的数据
    • 创建一个新的Map集合,将获取的结果存入新建的map集合内 【这里的数据是要展现给前端的,所以只存一些需要展示的,不必所有都存入,否则安全性有影响】
    • 将集合返回
  • Controller:

    • 通过@Reference注解注入Service的类:WeixinPayService

    • 建立一个生成微信二维码的方法,调用雪花算法将订单号和支付类型作为方法参数调用Serivec的createNative,将得到的map集合返回上去;

    • payService.js:
      建立一个本地支付的方法,指向pay/createNative.do [Controller.java类的生成二维码方法]

    • payController.js:
      建立一个本地生成二维码的方法,前端页面调用此方法进行查询后端,获取结果后得到订单编号和金额大小,同时在这里生成二维码,将订单编号和金额大小都返回到前端页面;

    • pay.html:

      首先引入js相关文件,qrious.min.js创建二维码的js文件,以及绑定ng-app,ng-controller,初始调用ng-init生成二维码功能
      将放置二维码图片的地方
      显示订单:订单号:{{out_trade_no}} 显示金额:¥{{money}}

9.2 然后,检测支付状态:

  • 准备:

    • 需求:
    • 当用户支付成功的时候我们应该跳转到成功页面,提示:恭喜您,付款成功啦!,支付方式:微信支付 支付金额:xxxx元
    • 当用户支付失败的时候我们应该跳转到失败页面,提示:支付失败,请稍后再试! 失败原因:… 提供一个重新支付按钮以及跳转到品优购首页的功能
  • 实现思路:

    • 我们可以通过HttpClient工具类实现对远程支付接口的调用
    • 微信接口链接:
      https://api.mch.weixin.qq.com/pay/orderquery
    • 参考文档: 查询订单API
    • 思路:
      • 我们可以在后端建立查询状态方法,方法内通过循环查询,如果获取到结果则进行判断:
        • 如果结果为Null,则返回空map集合,
        • 如果有值,则返回给controller,再返回给前端
      • 前端进行循环调用查询状态方法。前端controller.js对返回的结果进行判断:
        • 如果map==null,则提示支付出错,跳出循环判断,
        • 如果map.get(“trade_state”).equals(“SUCCESS”),那么则返回支付成功,
        • 同时程序可以使用Thread进行睡眠,Thread.sleep(3000),此方法可以使循环查询每三秒执行一次
  • 开始:

    • interface:
      • 创建查询状态方法,接收参数订单编号
    • serviceImpl:
      • 实现查询状态方法,新建一个HashMap,然后存入公众号id,商户号id,订单号,随机字符串,以及查询订单状态的url地址;【URL地址是微信提供的,其他通过配置文件注入和参数接收】
      • 通过WXPayUtil将Map集合转为XML格式的字符串,然后通过HttpClient传入url地址发送获取状态;
      • 接收状态并将其转为Map集合,然后返回结果,如果获取失败则打印异常;
  • controller:

    • 创建queryPayStatus
      1. 先定义一个实体类Result resutl=null;
      2. 循环调用Service层的查询订单状态方法并获取返回值;
      3. 对返回值进行判断,如果==null,则result:false,出错
      4. 如果map.get(“trade_state”).equals(“SUCCESS”),则result:true,“支付成功”
      5. 线程等待每3秒执行一次
  • payService.js:

    • 创建查询状态方法并指向查询状态的地址和方法,传给它订单编号;
  • payController.js:

    • controller.js中创建查询状态方法,方法中对返回结果进行判断,
      - 如果返回结果为true,则跳转到支付成功页面
      - 如果返回结果为false,则跳转到失败页面

      将该方法在createNative生成二维码的时候进行调用,这样在二维码生成的时候就完成了查询订单状态的功能;因为二维码是初始化init就进行调用,所以生成二维码开始起就可以对二维码状态进行查询;

9.3再然后,我们需要加一个查询时间限制

  • 准备:
    • 需求:

      如果用户到了二维码页面一直未支付,或是关掉了支付页面,我们的代码会一直循环调用微信接口,这样会对程序造成很大的压力。所以我们要加一个时间限制或是循环次数限制,当超过时间或次数时,跳出循环。

  • 实现思路:
    • 可以在Contrller.java中的查询状态方法内定义一个计时器,int x=0; 然后再while(true){}循环查询状态的时候每次x++;如果x>=100时[可以自定义时间],然后result=new Result(false,“二维码超时”);

    • 在 前端中payController.js中再添加一个判断,如果成功则跳转,如果失败则进行判断,如果返回的结果是二维码超时,则重新生成二维码,否则就跳转到失败页面;

9.4 再然后,支付成功页面我们应该显示金额

  • 准备:
    • 需求:现在我们支付成功页面显示的是固定的值,怎么显示真正的支付金额呢?我们这里可以使用 angularJS 的页面传参来解决
    • 实现思路:
      • payController.js中的查询支付状态方法内,如果返回的result是true,支付成功,那么我们在跳转到支付成功页面的时候应该传入参数:$scope.money [付款金额]
  • 开始:
    • payController.js:
      - 跳转页面传参,当支付成功后跳转到成功页面的时候传入付款金额
      				* if(response.success){
      						location.href="paysuccess.html#?money="+$scope.money;}
      	- 引入$location服务,新增获取金额的方法;
      				return $location.search()['money'];
      
    • paysuccess.html:
      - 引入相关的js,并在body添加指令:ng-app,ng-controller
      - 用表达式显示金额:

      支付金额:¥{{getMoney()}}元

9.5 最后,我们应该添加一个支付日志的功能

  • 准备:

    • 需求:
      - 系统中无法查询到支付记录
      - 支付后订单状态没有改变
    • 实现思路:
      • 在用户下订单时,判断如果为微信支付,就想支付日志表添加一条记录,信息包括支付总金额、订单 ID(多个)、用户 ID 、下单时间等信息,支付状态为 0(未支付)
      • 生成的支付日志对象放入 redis 中,以用户 ID 作为 key,这样在生成支付二维码时就可以从 redis 中提取支付日志对象中的金额和订单号。
      • 当用户支付成功后,修改支付日志的支付状态为 1(已支付),并记录微信传递给我们的交易流水号。根据订单 ID(多个)修改订单的状态为 2(已付款)。
      • 分析表结构:根据需求我们应该独立建立一个订单支付日志表,它应该包含:支付订单号,创建事件,支付完成时间,支付金额,交易流水,交易状态,支付类型,订单表id串
  • 具体思路[解决系统中无法查询到支付记录]:

    • interface:
      * 首先我们在接口中创建一个通过用户id查询redis中订单的办法。
    • service:
      • 然后在实现类中的生成二维码方法[createNative]中先获取当前用户,
      • 通过当前用户userid查询redis返回一个payLog日志对象,
      • 然后对其进行判断如果该日志不存在则返回一个new HashMap(); 【即这个值为null】
      • 如果该日志存在,则将日志中的订单编号和商品金额作为参数调用weixinPayService.createNative(…)返回;
  • 具体思路[解决支付后订单状态没有改变]

    • interface:

      • 创建一个修改订单状态的方法,并传入订单编号,和交易号的的方法;
    • serviceimpl:

      • 首先要明确做的三件事情:

        • 修改支付日志状态
        • 修改关联的订单的状态
        • 清除缓存中的支付日志对象
      • 实现接口新建的修改订单状态方法updateOrderStatus(String out_trade_no,String transaction_id):

        • 通过日志操作类通过订单编号查询到payLog对象,然后修改payLog对象内的属性,如交易状态,交易号等
        • 通过日志类payLog.getOrderList()获取订单号列表然后查询到orderList值,
        • orderList的值为"xxx,xxx,xxx",我们可以通过spilt(",")方法获取到订单号数组。
        • 遍历订单编号,通过订单操作类orderMapper.selectByPrimaryKey方法查询id值获得订单,如果订单存在则修改状态,并orderMapper.updateByPrimaryKey(order)覆盖订单;
        • 通过redisTemplate查找"payLog".delete删除日志id;
      • PayController.java:

        • 在微信支付接口有成功返回状态时,调用修改状态的方法
							if(map.get("trade_state").equals("SUCCESS")){//如果成功
							result=new Result(true, "支付成功");
							//修改订单状态
							orderService.updateOrderStatus(out_trade_no,
							map.get("transaction_id"));
							break;
							}

十. interface:

10.1 pom.xml:

				<?xml version="1.0" encoding="UTF-8"?>
				<project xmlns="http://maven.apache.org/POM/4.0.0"
				         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
				         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
				    <parent>
				        <artifactId>pinyougou-parent</artifactId>
				        <groupId>com.pinyougou</groupId>
				        <version>1.0-SNAPSHOT</version>
				    </parent>
				    <modelVersion>4.0.0</modelVersion>
				    <packaging>jar</packaging>
				
				    <artifactId>pinyougou-pay-interface</artifactId>
				
				
				</project>

10.2 WeixinPayService.java [接口]

							package com.pinyougou.pay.service;
					
					import java.util.Map;
					
					public interface WeixinPayService {
					
					    /*
					    *
					    * 生成二维码
					    * */
					    public Map createNative(String out_trade_no ,String total_fee);
					
					
					    /*
					    *
					    * 查询支付订单状态
					    * */
					    public  Map queryPayStatus(String out_trade_no);
					}

10.3 serviceImpl

1. pom.xml
			
			<?xml version="1.0" encoding="UTF-8"?>
			<project xmlns="http://maven.apache.org/POM/4.0.0"
			         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
			         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
			    <parent>
			        <artifactId>pinyougou-parent</artifactId>
			        <groupId>com.pinyougou</groupId>
			        <version>1.0-SNAPSHOT</version>
			    </parent>
			    <modelVersion>4.0.0</modelVersion>
			    <packaging>war</packaging>
			    <artifactId>pinyougou-pay-service</artifactId>
			
			    <properties>
			        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
			        <maven.compiler.source>1.7</maven.compiler.source>
			        <maven.compiler.target>1.7</maven.compiler.target>
			    </properties>
			
			    <dependencies>
			
			        <!-- Spring -->
			        <dependency>
			            <groupId>org.springframework</groupId>
			            <artifactId>spring-context</artifactId>
			        </dependency>
			        <dependency>
			            <groupId>org.springframework</groupId>
			            <artifactId>spring-beans</artifactId>
			        </dependency>
			        <dependency>
			            <groupId>org.springframework</groupId>
			            <artifactId>spring-webmvc</artifactId>
			        </dependency>
			        <dependency>
			            <groupId>org.springframework</groupId>
			            <artifactId>spring-jdbc</artifactId>
			        </dependency>
			        <dependency>
			            <groupId>org.springframework</groupId>
			            <artifactId>spring-aspects</artifactId>
			        </dependency>
			        <dependency>
			            <groupId>org.springframework</groupId>
			            <artifactId>spring-jms</artifactId>
			        </dependency>
			        <dependency>
			            <groupId>org.springframework</groupId>
			            <artifactId>spring-context-support</artifactId>
			        </dependency>
			        <dependency>
			            <groupId>org.springframework</groupId>
			            <artifactId>spring-test</artifactId>
			        </dependency>
			        <!-- dubbo相关 -->
			        <dependency>
			            <groupId>com.alibaba</groupId>
			            <artifactId>dubbo</artifactId>
			        </dependency>
			        <dependency>
			            <groupId>org.apache.zookeeper</groupId>
			            <artifactId>zookeeper</artifactId>
			        </dependency>
			        <dependency>
			            <groupId>com.github.sgroschupf</groupId>
			            <artifactId>zkclient</artifactId>
			        </dependency>
			        <dependency>
			            <groupId>junit</groupId>
			            <artifactId>junit</artifactId>
			        </dependency>
			        <dependency>
			            <groupId>com.alibaba</groupId>
			            <artifactId>fastjson</artifactId>
			        </dependency>
			        <dependency>
			            <groupId>javassist</groupId>
			            <artifactId>javassist</artifactId>
			        </dependency>
			        <dependency>
			            <groupId>commons-codec</groupId>
			            <artifactId>commons-codec</artifactId>
			        </dependency>
			        <dependency>
			            <groupId>javax.servlet</groupId>
			            <artifactId>servlet-api</artifactId>
			            <scope>provided</scope>
			        </dependency>
			
			        <dependency>
			            <groupId>com.pinyougou</groupId>
			            <artifactId>pinyougou-pay-interface</artifactId>
			            <version>1.0-SNAPSHOT</version>
			        </dependency>
			        <dependency>
			            <groupId>com.pinyougou</groupId>
			            <artifactId>pinyougou-common</artifactId>
			            <version>1.0-SNAPSHOT</version>
			        </dependency>
			        <dependency>
			            <groupId>com.github.wxpay</groupId>
			            <artifactId>wxpay-sdk</artifactId>
			            <version>0.0.3</version>
			        </dependency>
			
			    </dependencies>
			
			    <build>
			        <plugins>
			            <plugin>
			                <groupId>org.apache.tomcat.maven</groupId>
			                <artifactId>tomcat7-maven-plugin</artifactId>
			                <version>2.2</version>
			                <configuration>
			                    <!-- 指定端口 -->
			                    <port>9000</port>
			                    <!-- 请求路径 -->
			                    <path>/</path>
			                </configuration>
			            </plugin>
			        </plugins>
			    </build>
			
			</project>

10.4 WeinxinPayServiceImpl.java:


						package com.pinyougou.pay.service.impl;
						
						import com.alibaba.dubbo.config.annotation.Service;
						import com.github.wxpay.sdk.WXPayUtil;
						import com.pinyougou.pay.service.WeixinPayService;
						import org.springframework.beans.factory.annotation.Value;
						import util.HttpClient;
						
						import java.util.HashMap;
						import java.util.Map;
						
						/**
						 * WeixinPayServiceImpl
						 * hasee
						 * 2018/12/14
						 * 15:13
						 *
						 * @Version 1.0
						 **/
						
						@Service
						public class WeixinPayServiceImpl implements WeixinPayService {
						
						    @Value("${appid}")
						    private String appid;
						
						    @Value("${partner}")
						    private String partner;
						
						    @Value("${partnerkey}")
						    private String partnerkey;
						
						    @Override
						    public Map createNative(String out_trade_no, String total_fee) {
						        //1. 参数封装
						        Map param=new HashMap();
						        param.put("appid",appid);       //公众号id
						        param.put("mch_id",partner);    //商户key值
						        param.put("nonce_str", WXPayUtil.generateNonceStr());       //商户密码,随机字符串
						        param.put("body","品优购");    //主体
						        param.put("out_trade_no",out_trade_no);     //交易订单
						        param.put("total_fee",total_fee);       //金额(分)
						        param.put("spbill_create_ip","127.0.0.1");      //终端ip
						        param.put("notify_url","http://www.itcast.cn");     //回调地址
						        param.put("trade_type","NATIVE");           //交易类型
						
						        try {
						            String xmlParam=WXPayUtil.generateSignedXml(param,partnerkey);
						            System.out.println("请求的参数:"+xmlParam);
						
						            //2. 发送请求
						            HttpClient httpClient=new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
						            httpClient.setHttps(true);
						            httpClient.setXmlParam(xmlParam);
						            httpClient.post();
						
						
						            //获取结果
						            String xmlResult=httpClient.getContent();
						            Map<String, String> mapResult=WXPayUtil.xmlToMap(xmlResult);
						            System.out.println("接收到的结果"+mapResult);
						            Map map=new HashMap();
						            map.put("code_url",mapResult.get("code_url"));          //生成支付二维码连接
						            map.put("out_trade_no",mapResult.get("out_trade_no"));
						            map.put("total_fee",mapResult.get("total_fee"));
						            return  map;
						        } catch (Exception e) {
						            e.printStackTrace();
						            return new HashMap();
						        }
						    }
						
						    @Override
						    public Map queryPayStatus(String out_trade_no) {
						        //1. 封装参数
						        Map param=new HashMap();
						        param.put("appid",appid);
						        param.put("mch_id",partner);
						        param.put("out_trade_no",out_trade_no);
						        param.put("nonce_str",WXPayUtil.generateNonceStr());
						        try {
						            String xmlParam=WXPayUtil.generateSignedXml(param,partnerkey);
						            //2. 发送请求
						           HttpClient httpClient=new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
						           httpClient.setHttps(true);
						           httpClient.setXmlParam(xmlParam);
						            httpClient.post();
						
						            //3. 获取结果
						            String xmlResult=httpClient.getContent();
						            Map<String, String> map=WXPayUtil.xmlToMap(xmlResult);
						            return map;
						
						        } catch (Exception e) {
						            e.printStackTrace();
						            return new HashMap();
						        }
						
						    }
						}

10.5 resources/sping/applicationContext-service.xml

			<?xml version="1.0" encoding="UTF-8"?>
			<beans xmlns="http://www.springframework.org/schema/beans"
				xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
				xmlns:context="http://www.springframework.org/schema/context"
				xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc"
				xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
			        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
			        http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
			        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
			
			    <dubbo:protocol name="dubbo" port="20880"></dubbo:protocol>
				<dubbo:application name="pinyougou-pay-service"/>
			    <dubbo:registry address="zookeeper://192.168.25.128:2181"/>
			    <dubbo:annotation package="com.pinyougou.pay.service.impl" />		 
			</beans>

10.6 webapp/WEB-INF/web.xml:


				<?xml version="1.0" encoding="UTF-8"?>
				<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
				         xmlns="http://java.sun.com/xml/ns/javaee"
				         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
				         version="2.5">
				
				  <!-- 加载spring容器 -->
				  <context-param>
				    <param-name>contextConfigLocation</param-name>
				    <param-value>classpath*:spring/applicationContext*.xml</param-value>
				  </context-param>
				  <listener>
				    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
				  </listener>
				
				
				</web-app>

十一. Controller:

11.1 controller:


		package com.pinyougou.cart.controller;
			import com.alibaba.dubbo.config.annotation.Reference;
			import com.pinyougou.order.service.OrderService;
			import com.pinyougou.pay.service.WeixinPayService;
			import com.pinyougou.pojo.TbPayLog;
			import entity.Result;
			import org.springframework.security.core.context.SecurityContextHolder;
			import org.springframework.web.bind.annotation.RequestMapping;
			import org.springframework.web.bind.annotation.RestController;
			
			import java.util.HashMap;
			import java.util.Map;
			
			/**
			 * PayController
			 * hasee
			 * 2018/12/14
			 * 15:55
			 *
			 * @Version 1.0
			 **/
			@RestController
			@RequestMapping("/pay")
			public class PayController {
			
			    @Reference
			    private WeixinPayService weixinPayService;
			
			    @Reference
			    private OrderService orderService;
			
			    @RequestMapping("/createNative")
			    public Map createNative(){
			       //获取当前登录用户
			        String username=SecurityContextHolder.getContext().getAuthentication().getName();
			        //从日志中提取缓存
			        TbPayLog payLog=orderService.searchPayLogFromRedis(username);
			        //调用微信支付接口
			        if (payLog!=null){
			            return weixinPayService.createNative(payLog.getOutTradeNo(),payLog.getTotalFee()+"");
			        }else{
			            return new HashMap();
			        }
			
			    }
			
			
			    @RequestMapping("/queryPayStatus")
			    public Result queryPayStatus(String out_trade_no){
			        Result result=null;
			        int x=0;
			        while (true){
			            Map<String,String> map=weixinPayService.queryPayStatus(out_trade_no);       //调用查询
			            if (map==null){
			                result=new Result(false,"支付发生错误!");
			                break;
			            }
			            if (map.get("trade_state").equals("SUCCESS")){
			                result=new Result(true,"支付成功");
			                orderService.updateOrderStatus(out_trade_no,map.get("transaction_id"));         //修改订单状态
			                break;
			            }
			            try {
			                Thread.sleep(3000);
			            } catch (InterruptedException e) {
			                e.printStackTrace();
			            }
			            x++;
			            //平时x>100[三秒执行一次,每分钟执行20次,五分钟执行100次,大概5分钟左右二维码超时],这里方便测试我们可以直接大于4
			            if (x>=4){
			                result=new Result(false,"二维码超时");
			                break;
			            }
			
			        }
			        return result;
			    }
			}

11.2 pom.xml:

					
					<?xml version="1.0" encoding="UTF-8"?>
					<project xmlns="http://maven.apache.org/POM/4.0.0"
					         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
					         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
					    <parent>
					        <artifactId>pinyougou-parent</artifactId>
					        <groupId>com.pinyougou</groupId>
					        <version>1.0-SNAPSHOT</version>
					    </parent>
					    <modelVersion>4.0.0</modelVersion>
					    <packaging>war</packaging>
					    <artifactId>pinyougou-cart-web</artifactId>
					
					    <dependencies>
					        <!-- Spring -->
					        <dependency>
					            <groupId>org.springframework</groupId>
					            <artifactId>spring-context</artifactId>
					        </dependency>
					        <dependency>
					            <groupId>org.springframework</groupId>
					            <artifactId>spring-beans</artifactId>
					        </dependency>
					        <dependency>
					            <groupId>org.springframework</groupId>
					            <artifactId>spring-webmvc</artifactId>
					        </dependency>
					        <dependency>
					            <groupId>org.springframework</groupId>
					            <artifactId>spring-jdbc</artifactId>
					        </dependency>
					        <dependency>
					            <groupId>org.springframework</groupId>
					            <artifactId>spring-aspects</artifactId>
					        </dependency>
					        <dependency>
					            <groupId>org.springframework</groupId>
					            <artifactId>spring-jms</artifactId>
					        </dependency>
					        <dependency>
					            <groupId>org.springframework</groupId>
					            <artifactId>spring-context-support</artifactId>
					        </dependency>
					        <dependency>
					            <groupId>org.springframework</groupId>
					            <artifactId>spring-test</artifactId>
					        </dependency>
					        <!-- dubbo相关 -->
					        <dependency>
					            <groupId>com.alibaba</groupId>
					            <artifactId>dubbo</artifactId>
					        </dependency>
					        <dependency>
					            <groupId>org.apache.zookeeper</groupId>
					            <artifactId>zookeeper</artifactId>
					        </dependency>
					        <dependency>
					            <groupId>com.github.sgroschupf</groupId>
					            <artifactId>zkclient</artifactId>
					        </dependency>
					        <dependency>
					            <groupId>junit</groupId>
					            <artifactId>junit</artifactId>
					        </dependency>
					        <dependency>
					            <groupId>com.alibaba</groupId>
					            <artifactId>fastjson</artifactId>
					        </dependency>
					        <dependency>
					            <groupId>javassist</groupId>
					            <artifactId>javassist</artifactId>
					        </dependency>
					        <dependency>
					            <groupId>commons-codec</groupId>
					            <artifactId>commons-codec</artifactId>
					        </dependency>
					        <dependency>
					            <groupId>javax.servlet</groupId>
					            <artifactId>servlet-api</artifactId>
					            <scope>provided</scope>
					        </dependency>
					
					
					        <dependency>
					            <groupId>org.springframework.security</groupId>
					            <artifactId>spring-security-web</artifactId>
					            <version>4.1.0.RELEASE</version>
					        </dependency>
					
					        <dependency>
					            <groupId>org.springframework.security</groupId>
					            <artifactId>spring-security-config</artifactId>
					            <version>4.1.0.RELEASE</version>
					        </dependency>
					
					
					
					        <dependency>
					            <groupId>org.springframework.security</groupId>
					            <artifactId>spring-security-cas</artifactId>
					            <version>4.1.0.RELEASE</version>
					        </dependency>
					        <dependency>
					            <groupId>org.jasig.cas.client</groupId>
					            <artifactId>cas-client-core</artifactId>
					            <version>3.3.3</version>
					            <exclusions>
					                <exclusion>
					                    <groupId>org.slf4j</groupId>
					                    <artifactId>log4j-over-slf4j</artifactId>
					                </exclusion>
					            </exclusions>
					        </dependency>
					
					        <dependency>
					            <groupId>com.pinyougou</groupId>
					            <artifactId>pinyougou-common</artifactId>
					            <version>1.0-SNAPSHOT</version>
					        </dependency>
					        <dependency>
					            <groupId>com.pinyougou</groupId>
					            <artifactId>pinyougou-cart-interface</artifactId>
					            <version>1.0-SNAPSHOT</version>
					        </dependency>
					        <dependency>
					            <groupId>com.pinyougou</groupId>
					            <artifactId>pinyougou-user-interface</artifactId>
					            <version>1.0-SNAPSHOT</version>
					        </dependency>
					        <dependency>
					            <groupId>com.pinyougou</groupId>
					            <artifactId>pinyougou-order-interface</artifactId>
					            <version>1.0-SNAPSHOT</version>
					        </dependency>
					        <dependency>
					            <groupId>com.pinyougou</groupId>
					            <artifactId>pinyougou-pay-interface</artifactId>
					            <version>1.0-SNAPSHOT</version>
					        </dependency>
					    </dependencies>
					
					
					    <build>
					        <plugins>
					            <plugin>
					                <groupId>org.apache.tomcat.maven</groupId>
					                <artifactId>tomcat7-maven-plugin</artifactId>
					                <version>2.2</version>
					                <configuration>
					                    <!-- 指定端口 -->
					                    <port>9107</port>
					                    <!-- 请求路径 -->
					                    <path>/</path>
					                </configuration>
					            </plugin>
					        </plugins>
					    </build>
					
					</project>

11.3 resources/sping/spring-security.xml


				<?xml version="1.0" encoding="UTF-8"?>
				<beans:beans xmlns="http://www.springframework.org/schema/security"
					xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
					xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
										http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
				
				    <http pattern="/css/**" security="none"></http>
				    <http pattern="/img/**" security="none"></http>
				    <http pattern="/js/**" security="none"></http>
				    <http pattern="/plugins/**" security="none"></http>
				
				    <http pattern="/cart.html" security="none"></http>
				
				
				
					<!--   entry-point-ref  入口点引用 -->
				    <!--登录不在自己本身的系统中进行登录,所以需要设置入口点-->
					<http use-expressions="false" entry-point-ref="casProcessingFilterEntryPoint">
				        <!--匿名角色IS_AUTHENTICATED_ANONYMOUSLY-->
				        <!--如果用户登录了,则显示登录名,如果未登录则登录名:anonymousUser,必须要放在ROLE_USER前面-->
				        <intercept-url pattern="/cart/*.do" access="IS_AUTHENTICATED_ANONYMOUSLY"></intercept-url>
				        <intercept-url pattern="/**" access="ROLE_USER"/>   
				        <csrf disabled="true"/>  
				        <!-- custom-filter为过滤器, position 表示将过滤器放在指定的位置上,before表示放在指定位置之前  ,after表示放在指定的位置之后  -->           
				        <custom-filter ref="casAuthenticationFilter"  position="CAS_FILTER" />      
				        <custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/>  
				        <custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/>  
				    </http>
				
				
				    <!--CAS只是单点登录的一种解决方案,所以这里不一定一定要接入CAS,也可以接入其他的;-->
				  	<!-- CAS入口点 开始 -->
				    <beans:bean id="casProcessingFilterEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">  
				        <!-- 单点登录服务器登录URL -->  
				        <beans:property name="loginUrl" value="http://localhost:9100/cas/login"/>  
				        <beans:property name="serviceProperties" ref="serviceProperties"/>  
				    </beans:bean>      
				    <beans:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">  
				        <!--service 配置自身工程的根地址+/login/cas   -->  
				        <beans:property name="service" value="http://localhost:9107/login/cas"/>
				    </beans:bean>  
				    <!-- CAS入口点 结束 -->
				
				    
				    <!-- 认证过滤器 开始 -->
				    <beans:bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">  
				        <beans:property name="authenticationManager" ref="authenticationManager"/>  
				    </beans:bean>  
						<!-- 认证管理器 -->
					<authentication-manager alias="authenticationManager">
						<authentication-provider  ref="casAuthenticationProvider">
						</authentication-provider>
					</authentication-manager>
						<!-- 认证提供者 -->
					<beans:bean id="casAuthenticationProvider"     class="org.springframework.security.cas.authentication.CasAuthenticationProvider">  
				        <beans:property name="authenticationUserDetailsService">  
				            <beans:bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">  
				                <beans:constructor-arg ref="userDetailsService" />  
				            </beans:bean>  
				        </beans:property>  
				        <beans:property name="serviceProperties" ref="serviceProperties"/>  
				        <!-- ticketValidator 为票据验证器 -->
				        <beans:property name="ticketValidator">  
				            <beans:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">  
				                <beans:constructor-arg index="0" value="http://localhost:9100/cas"/>  
				            </beans:bean>  
				        </beans:property>  
				        <beans:property name="key" value="an_id_for_this_auth_provider_only"/> 
				    </beans:bean>        
				   		 <!-- 认证类 -->
					<beans:bean id="userDetailsService" class="com.pinyougou.user.service.UserDetailServiceImpl"/>
					
					<!-- 认证过滤器 结束 -->
					
					
					<!-- 单点登出  开始  -->     
				    <beans:bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>    
				    <!-- 经过此配置,当用户在地址栏输入本地工程 /logout/cas  -->      
				    <beans:bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">  
				        <beans:constructor-arg value="http://localhost:9100/cas/logout?service=http://localhost:9103"/>
				        <beans:constructor-arg>  
				            <beans:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>  
				        </beans:constructor-arg>  
				        <beans:property name="filterProcessesUrl" value="/logout/cas"/>  
				    </beans:bean>  
				    <!-- 单点登出  结束 -->  
					
				</beans:beans>

11.4 resources/springmvc.xml


				<?xml version="1.0" encoding="UTF-8"?>
				<beans xmlns="http://www.springframework.org/schema/beans"
					xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
					xmlns:context="http://www.springframework.org/schema/context"
					xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc"
					xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
				        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
				        http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
				        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
				    <context:property-placeholder location="classpath:config/application.properties" />
					
					<mvc:annotation-driven>
					  <mvc:message-converters register-defaults="true">
					    <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">  
					      <property name="supportedMediaTypes" value="application/json"/>
					      <property name="features">
					        <array>
					          <value>WriteMapNullValue</value>
					          <value>WriteDateUseDateFormat</value>
					        </array>
					      </property>
					    </bean>
					  </mvc:message-converters>  
					</mvc:annotation-driven>
				
					<!-- 引用dubbo 服务 -->
					<dubbo:application name="pinyougou-cart-web" />
					<dubbo:registry address="zookeeper://192.168.25.128:2181"/>
					<dubbo:annotation package="com.pinyougou.cart.controller" />
				
				</beans>

11.5 js/controller.js

				app.controller('payController',function ($scope,$location,payService) {
				
				
				   $scope.createNative=function () {
				       payService.createNative().success(
				           function (response) {
				                //显示订单号和金额
				               //toFixed是保留两位小数,total_fee单位是分除以100变为元
				               $scope.money=(response.total_fee/100).toFixed(2);
				               $scope.out_trade_no=response.out_trade_no;
				
				               //生成二维码
				               var qr=new QRious({
				                   element:document.getElementById('qrious'),
				                   size:250,
				                   value:response.code_url,
				                   level:'H'
				               });
				                queryPayStatus();       //调用查询
				
				
				
				       })
				   };
				
				
				   //调用查询
				    queryPayStatus=function () {
				        payService.queryPayStatus($scope.out_trade_no).success(
				            function (response) {
				                if (response.success){
				                    location.href="paysuccess.html#?money="+$scope.money;
				                }else{
				                    if (response.message=='二维码超时'){
				                        //这里调用重新生成二维码功能,当关闭浏览器,后端循环遍历查询订单状态即停止,如果不关闭浏览器和本支付页面的话,就可以实现每隔几秒重新生成一次二维码功能;
				                        $scope.createNative();      //重新生成二维码
				                    }else{
				                        location.href="payfail.html";
				                    }
				                    }})}
				    // 获取金额
				    $scope.getMoney=function () {
				        return $location.search()['money'];
				    }
				
				});

11.6 js/base.js:

			var app=angular.module('pinyougou',[]);

11.7 js/service.js:

			app.controller('payController',function ($scope,$location,payService) {
		
		
		   $scope.createNative=function () {
		       payService.createNative().success(
		           function (response) {
		                //显示订单号和金额
		               //toFixed是保留两位小数,total_fee单位是分除以100变为元
		               $scope.money=(response.total_fee/100).toFixed(2);
		               $scope.out_trade_no=response.out_trade_no;
		
		               //生成二维码
		               var qr=new QRious({
		                   element:document.getElementById('qrious'),
		                   size:250,
		                   value:response.code_url,
		                   level:'H'
		               });
		                queryPayStatus();       //调用查询
		
		
		
		       })
		   };
		
		
		   //调用查询
		    queryPayStatus=function () {
		        payService.queryPayStatus($scope.out_trade_no).success(
		            function (response) {
		                if (response.success){
		                    location.href="paysuccess.html#?money="+$scope.money;
		                }else{
		                    if (response.message=='二维码超时'){
		                        //这里调用重新生成二维码功能,当关闭浏览器,后端循环遍历查询订单状态即停止,如果不关闭浏览器和本支付页面的话,就可以实现每隔几秒重新生成一次二维码功能;
		                        $scope.createNative();      //重新生成二维码
		                    }else{
		                        location.href="payfail.html";
		                    }
		                    }})}
		    // 获取金额
		    $scope.getMoney=function () {
		        return $location.search()['money'];
		    }
		
		});
~~~java

### 11.8 webapp/WEB-INF/web.xml
~~~java

				<?xml version="1.0" encoding="UTF-8"?>
				<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
				         xmlns="http://java.sun.com/xml/ns/javaee"
				         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
				         version="2.5">
				    <!-- 解决post乱码 -->
				    <filter>
				        <filter-name>CharacterEncodingFilter</filter-name>
				        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
				        <init-param>
				            <param-name>encoding</param-name>
				            <param-value>utf-8</param-value>
				        </init-param>
				        <init-param>
				            <param-name>forceEncoding</param-name>
				            <param-value>true</param-value>
				        </init-param>
				    </filter>
				    <filter-mapping>
				        <filter-name>CharacterEncodingFilter</filter-name>
				        <url-pattern>/*
				    
				
				    
				        contextConfigLocation
				        classpath:spring/spring-security.xml
				    
				    
				        
				            org.springframework.web.context.ContextLoaderListener
				        
				    
				
				    
				        springSecurityFilterChain
				        org.springframework.web.filter.DelegatingFilterProxy
				    
				    
				        springSecurityFilterChain
				        /*
				    
				    
				        springmvc
				        org.springframework.web.servlet.DispatcherServlet
				        
				        
				            contextConfigLocation
				            classpath:spring/springmvc.xml
				        
				    
				
				    
				        springmvc
				        *.do
				    
				    
				        cart.html
				    
				
				
				

十二. 工具类:

12. 1 HttpClient工具类:

			package util;
			
			import java.io.IOException;
			import java.security.GeneralSecurityException;
			import java.security.cert.CertificateException;
			import java.security.cert.X509Certificate;
			import java.text.ParseException;
			import java.util.HashMap;
			import java.util.LinkedList;
			import java.util.List;
			import java.util.Map;
			
			import javax.net.ssl.SSLContext;
			import javax.net.ssl.SSLException;
			import javax.net.ssl.SSLSession;
			import javax.net.ssl.SSLSocket;
			import javax.net.ssl.TrustManager;
			import javax.net.ssl.X509TrustManager;
			
			import org.apache.http.Consts;
			import org.apache.http.HttpEntity;
			import org.apache.http.NameValuePair;
			import org.apache.http.client.ClientProtocolException;
			import org.apache.http.client.config.RequestConfig;
			import org.apache.http.client.entity.UrlEncodedFormEntity;
			import org.apache.http.client.methods.CloseableHttpResponse;
			import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
			import org.apache.http.client.methods.HttpGet;
			import org.apache.http.client.methods.HttpPost;
			import org.apache.http.client.methods.HttpPut;
			import org.apache.http.client.methods.HttpUriRequest;
			import org.apache.http.conn.scheme.Scheme;
			import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
			import org.apache.http.conn.ssl.SSLContextBuilder;
			import org.apache.http.conn.ssl.SSLSocketFactory;
			import org.apache.http.conn.ssl.TrustStrategy;
			import org.apache.http.conn.ssl.X509HostnameVerifier;
			import org.apache.http.entity.StringEntity;
			import org.apache.http.impl.client.CloseableHttpClient;
			import org.apache.http.impl.client.DefaultHttpClient;
			import org.apache.http.impl.client.HttpClients;
			import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
			import org.apache.http.message.BasicNameValuePair;
			import org.apache.http.util.EntityUtils;
			
			/**
			 * http请求客户端
			 * 
			 * @author Administrator
			 * 
			 */
			public class HttpClient {
				private String url;
				private Map<String, String> param;
				private int statusCode;
				private String content;
				private String xmlParam;
				private boolean isHttps;
			
				public boolean isHttps() {
					return isHttps;
				}
			
				public void setHttps(boolean isHttps) {
					this.isHttps = isHttps;
				}
			
				public String getXmlParam() {
					return xmlParam;
				}
			
				public void setXmlParam(String xmlParam) {
					this.xmlParam = xmlParam;
				}
			
				public HttpClient(String url, Map<String, String> param) {
					this.url = url;
					this.param = param;
				}
			
				public HttpClient(String url) {
					this.url = url;
				}
			
				public void setParameter(Map<String, String> map) {
					param = map;
				}
			
				public void addParameter(String key, String value) {
					if (param == null)
						param = new HashMap<String, String>();
					param.put(key, value);
				}
			
				public void post() throws ClientProtocolException, IOException {
					HttpPost http = new HttpPost(url);
					setEntity(http);
					execute(http);
				}
			
				public void put() throws ClientProtocolException, IOException {
					HttpPut http = new HttpPut(url);
					setEntity(http);
					execute(http);
				}
			
				public void get() throws ClientProtocolException, IOException {
					if (param != null) {
						StringBuilder url = new StringBuilder(this.url);
						boolean isFirst = true;
						for (String key : param.keySet()) {
							if (isFirst)
								url.append("?");
							else
								url.append("&");
							url.append(key).append("=").append(param.get(key));
						}
						this.url = url.toString();
					}
					HttpGet http = new HttpGet(url);
					execute(http);
				}
			
				/**
				 * set http post,put param
				 */
				private void setEntity(HttpEntityEnclosingRequestBase http) {
					if (param != null) {
						List<NameValuePair> nvps = new LinkedList<NameValuePair>();
						for (String key : param.keySet())
							nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数
						http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数
					}
					if (xmlParam != null) {
						http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));
					}
				}
			
				private void execute(HttpUriRequest http) throws ClientProtocolException,
						IOException {
					CloseableHttpClient httpClient = null;
					try {
						if (isHttps) {
							SSLContext sslContext = new SSLContextBuilder()
									.loadTrustMaterial(null, new TrustStrategy() {
										// 信任所有
										public boolean isTrusted(X509Certificate[] chain,
												String authType)
												throws CertificateException {
											return true;
										}
									}).build();
							SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
									sslContext);
							httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
									.build();
						} else {
							httpClient = HttpClients.createDefault();
						}
						CloseableHttpResponse response = httpClient.execute(http);
						try {
							if (response != null) {
								if (response.getStatusLine() != null)
									statusCode = response.getStatusLine().getStatusCode();
								HttpEntity entity = response.getEntity();
								// 响应内容
								content = EntityUtils.toString(entity, Consts.UTF_8);
							}
						} finally {
							response.close();
						}
					} catch (Exception e) {
						e.printStackTrace();
					} finally {
						httpClient.close();
					}
				}
			
				public int getStatusCode() {
					return statusCode;
				}
			
				public String getContent() throws ParseException, IOException {
					return content;
				}
			
			}

12.2 雪花算法工具类

				package util;
				
				import java.lang.management.ManagementFactory;
				import java.net.InetAddress;
				import java.net.NetworkInterface;
				
				/**
				 * 

名称:IdWorker.java

*

描述:分布式自增长ID

*
				 *     Twitter的 Snowflake JAVA实现方案
				 * 
* 核心代码为其IdWorker这个类实现,其原理结构如下,我分别用一个0表示一位,用—分割开部分的作用: * 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000 * 在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间, * 然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识), * 然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。 * 这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分), * 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。 *

* 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加)) * * @author Polim */ public class IdWorker { // 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动) private final static long twepoch = 1288834974657L; // 机器标识位数 private final static long workerIdBits = 5L; // 数据中心标识位数 private final static long datacenterIdBits = 5L; // 机器ID最大值 private final static long maxWorkerId = -1L ^ (-1L << workerIdBits); // 数据中心ID最大值 private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 毫秒内自增位 private final static long sequenceBits = 12L; // 机器ID偏左移12位 private final static long workerIdShift = sequenceBits; // 数据中心ID左移17位 private final static long datacenterIdShift = sequenceBits + workerIdBits; // 时间毫秒左移22位 private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; private final static long sequenceMask = -1L ^ (-1L << sequenceBits); /* 上次生产id时间戳 */ private static long lastTimestamp = -1L; // 0,并发控制 private long sequence = 0L; private final long workerId; // 数据标识id部分 private final long datacenterId; public IdWorker(){ this.datacenterId = getDatacenterId(maxDatacenterId); this.workerId = getMaxWorkerId(datacenterId, maxWorkerId); } /** * @param workerId * 工作机器ID * @param datacenterId * 序列号 */ public IdWorker(long workerId, long datacenterId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); } if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); } this.workerId = workerId; this.datacenterId = datacenterId; } /** * 获取下一个ID * * @return */ public synchronized long nextId() { long timestamp = timeGen(); if (timestamp < lastTimestamp) { throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } if (lastTimestamp == timestamp) { // 当前毫秒内,则+1 sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { // 当前毫秒内计数满了,则等待下一秒 timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; // ID偏移组合生成最终的ID,并返回ID long nextId = ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; return nextId; } private long tilNextMillis(final long lastTimestamp) { long timestamp = this.timeGen(); while (timestamp <= lastTimestamp) { timestamp = this.timeGen(); } return timestamp; } private long timeGen() { return System.currentTimeMillis(); } /** *

* 获取 maxWorkerId *

*/
protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) { StringBuffer mpid = new StringBuffer(); mpid.append(datacenterId); String name = ManagementFactory.getRuntimeMXBean().getName(); if (!name.isEmpty()) { /* * GET jvmPid */ mpid.append(name.split("@")[0]); } /* * MAC + PID 的 hashcode 获取16个低位 */ return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1); } /** *

* 数据标识id部分 *

*/
protected static long getDatacenterId(long maxDatacenterId) { long id = 0L; try { InetAddress ip = InetAddress.getLocalHost(); NetworkInterface network = NetworkInterface.getByInetAddress(ip); if (network == null) { id = 1L; } else { byte[] mac = network.getHardwareAddress(); id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6; id = id % (maxDatacenterId + 1); } } catch (Exception e) { System.out.println(" getDatacenterId: " + e.getMessage()); } return id; } /* //测试 public static void main(String[] args) { IdWorker idWorker=new IdWorker(0,0); for (int i=0; i < 100; i++) { long nextId=idWorker.nextId(); System.out.println(nextId); } } */ }

你可能感兴趣的:(解决方案)