微信支付
微信支付的两种模式
1:由微信生成二维码,客户扫描二维码后,确认支付,微信回调给当前系统.
2:由系统调用统一下单API,取得预支付交易信息后,根据信息生成二维码,然后后台循环查询订单API接口查询付款状态(统一下单API,查询订单API)
区别:模式一是跳到微信让微信给我们生成二维码
模式二是可控制高,比较自主.
一:二维码
1.1:二维码优势
♦ 信息量大,可以容纳1850个大写字母或者2710个数字或500多个汉字
♦ 应用范围广,支持文字,声音(存放地址,地址里播放声音其实还是地址),图片,指纹等等
♦ 容错能力强,即是图片出现部分的破损也能使用
♦ 成本低,容易制作
二:微信支付
2.1:一共12个API
实现思路:
微信接口只接收xml字符串需要sas解析或者dom4j解析.将map转成xml通过httpClient远程提交参数接收返回结果
微信接口提供了一些工具类把map转换成xml
统一下单接口最终就是通过它生成一串支付地址,然后把支付地址展示给前端页面,让用户支付.最终订单有没有完成,需要调用查询订单接口,查看完成状态.
微信支付发送的是post请求,请求是xml格式的
代码实现
一:需要创建支付服务模块(pyg_pay_interface和pyg_pay_service)
**A😗*pyg_pay_service
1:pom.xml文件导入坐标
//打包方式为war包为web项目 <packaging>war</packaging> <dependencies> //导入工具类依赖 <dependency> <groupId>com.pinyougou</groupId> <artifactId>pyg_common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> //依赖dao层操作数据库 <dependency> <groupId>com.pinyougou</groupId> <artifactId>pyg_dao</artifactId> <version>1.0-SNAPSHOT</version> </dependency> //依赖于pyg_pay_interface接口 <dependency> <groupId>com.pinyougou</groupId> <artifactId>pyg_pay_interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> //微信的坐标,可以使用它的工具类生成uuid <dependency> <groupId>com.github.wxpay</groupId> <artifactId>wxpay-sdk</artifactId> <version>0.0.3</version> </dependency> //指定一台服务器 <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> </dependencies>
2: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>
3.1: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要占用的当前主机端口,默认端口不写是20880 --> <dubbo:protocol name="dubbo" port="20880"></dubbo:protocol> <!-- 配置dubbo服务的名称 --> <dubbo:application name="pinyougou-pay-service"/> <!-- 配置dubbo注册地址--> <dubbo:registry address="zookeeper://192.168.25.134:2181"/> <!--配置dubbo服务注解扫描的包 --> <dubbo:annotation package="com.pinyougou.pay.service.impl" /> </beans>
3.2:applicationContext-tx.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:tx="http://www.springframework.org/schema/tx" 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://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 开启事务控制的注解支持 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
4.在pyg_common导入配置文件
支付相关的信息
appid=wx8397f8696b538317 //商户ID partner=1473426802 //商户号 partnerkey=T6m9iK73b0kn9g5v426MKfHQH7X8rKwb //商户的key值 notifyurl=http://a31ef7db.ngrok.io/WeChatPay/WeChatPayNotify //回调地址,我们的二维码使我们自己生成的所以不需要回调
导入HttpClientUtils.java工具类
package util; /** * http请求客户端 * * @author Administrator * */ public class HttpClientUtil { public static HttpClientContext context = null; static { System.out.println("====================begin"); context = HttpClientContext.create(); } 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 HttpClientUtil(String url, Map<String, String> param) { this.url = url; this.param = param; } public HttpClientUtil(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,context); 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; } }
导入生成支付二维码的页面
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=9; IE=8; IE=7; IE=EDGE"> <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /> <title>微信支付页</title> <link rel="icon" href="/assets/img/favicon.ico"> <script type="text/javascript" src="plugins/angularjs/angular.min.js"> </script> <script type="text/javascript" src="js/base.js"> </script> <script type="text/javascript" src="js/service/payService.js"> </script> <script type="text/javascript" src="js/controller/payController.js"> </script> <script type="text/javascript" src="plugins/qrious.min.js"></script> <link rel="stylesheet" type="text/css" href="css/webbase.css" /> <link rel="stylesheet" type="text/css" href="css/pages-weixinpay.css" /> </head> <body ng-app="pinyougou" ng-controller="payController" ng-init="createNative()"> <!--head--> <div class="top"> <div class="py-container"> <div class="shortcut"> <ul class="fl"> <li class="f-item">品优购欢迎您!</li> <li class="f-item">请登录 <span><a href="#">免费注册</a></span></li> </ul> <ul class="fr"> <li class="f-item">我的订单</li> <li class="f-item space"></li> <li class="f-item">我的品优购</li> <li class="f-item space"></li> <li class="f-item">品优购会员</li> <li class="f-item space"></li> <li class="f-item">企业采购</li> <li class="f-item space"></li> <li class="f-item">关注品优购</li> <li class="f-item space"></li> <li class="f-item">客户服务</li> <li class="f-item space"></li> <li class="f-item">网站导航</li> </ul> </div> </div> </div> <div class="cart py-container"> <!--logoArea--> <div class="logoArea"> <div class="fl logo"><span class="title">收银台</span></div> </div> <!--主内容--> <div class="checkout py-container pay"> <div class="checkout-tit"> <h4 class="fl tit-txt"><span class="success-icon"></span><span class="success-info">订单提交成功,请您及时付款!订单号:{{payNo}}</span></h4> <span class="fr"><em class="sui-lead">应付金额:</em><em class="orange money">¥{{payMoney}}</em>元</span> <div class="clearfix"></div> </div> <div class="checkout-steps"> <div class="fl weixin">微信支付</div> <div class="fl sao"> <p class="red">二维码已过期,刷新页面重新获取二维码。</p> <div class="fl code"> <img id="qrious" alt=""> <div class="saosao"> <p>请使用微信扫一扫</p> <p>扫描二维码支付</p> </div> </div> <div class="fl phone"> </div> </div> <div class="clearfix"></div> <p><a href="pay.html" target="_blank">> 其他支付方式</a></p> </div> </div> </div> <!-- 底部栏位 --> <!--页面底部--> <div class="clearfix footer"> <div class="py-container"> <div class="footlink"> <div class="Mod-service"> <ul class="Mod-Service-list"> <li class="grid-service-item intro intro1"> <i class="serivce-item fl"></i> <div class="service-text"> <h4>正品保障</h4> <p>正品保障,提供发票</p> </div> </li> <li class="grid-service-item intro intro2"> <i class="serivce-item fl"></i> <div class="service-text"> <h4>正品保障</h4> <p>正品保障,提供发票</p> </div> </li> <li class="grid-service-item intro intro3"> <i class="serivce-item fl"></i> <div class="service-text"> <h4>正品保障</h4> <p>正品保障,提供发票</p> </div> </li> <li class="grid-service-item intro intro4"> <i class="serivce-item fl"></i> <div class="service-text"> <h4>正品保障</h4> <p>正品保障,提供发票</p> </div> </li> <li class="grid-service-item intro intro5"> <i class="serivce-item fl"></i> <div class="service-text"> <h4>正品保障</h4> <p>正品保障,提供发票</p> </div> </li> </ul> </div> <div class="clearfix Mod-list"> <div class="yui3-g"> <div class="yui3-u-1-6"> <h4>购物指南</h4> <ul class="unstyled"> <li>购物流程</li> <li>会员介绍</li> <li>生活旅行/团购</li> <li>常见问题</li> <li>购物指南</li> </ul> </div> <div class="yui3-u-1-6"> <h4>配送方式</h4> <ul class="unstyled"> <li>上门自提</li> <li>211限时达</li> <li>配送服务查询</li> <li>配送费收取标准</li> <li>海外配送</li> </ul> </div> <div class="yui3-u-1-6"> <h4>支付方式</h4> <ul class="unstyled"> <li>货到付款</li> <li>在线支付</li> <li>分期付款</li> <li>邮局汇款</li> <li>公司转账</li> </ul> </div> <div class="yui3-u-1-6"> <h4>售后服务</h4> <ul class="unstyled"> <li>售后政策</li> <li>价格保护</li> <li>退款说明</li> <li>返修/退换货</li> <li>取消订单</li> </ul> </div> <div class="yui3-u-1-6"> <h4>特色服务</h4> <ul class="unstyled"> <li>夺宝岛</li> <li>DIY装机</li> <li>延保服务</li> <li>品优购E卡</li> <li>品优购通信</li> </ul> </div> <div class="yui3-u-1-6"> <h4>帮助中心</h4> <img src="img/wx_cz.jpg"> </div> </div> </div> <div class="Mod-copyright"> <ul class="helpLink"> <li>关于我们<span class="space"></span></li> <li>联系我们<span class="space"></span></li> <li>关于我们<span class="space"></span></li> <li>商家入驻<span class="space"></span></li> <li>营销中心<span class="space"></span></li> <li>友情链接<span class="space"></span></li> <li>关于我们<span class="space"></span></li> <li>营销中心<span class="space"></span></li> <li>友情链接<span class="space"></span></li> <li>关于我们</li> </ul> <p>地址:北京市昌平区建材城西路金燕龙办公楼一层 邮编:100096 电话:400-618-4000 传真:010-82935100</p> <p>京ICP备08001421号京公网安备110108007702</p> </div> </div> </div> </div> <!--页面底部END--> <script type="text/javascript" src="js/plugins/jquery/jquery.min.js"></script> <script type="text/javascript" src="js/plugins/jquery.easing/jquery.easing.min.js"></script> <script type="text/javascript" src="js/plugins/sui/sui.min.js"></script> <script type="text/javascript" src="js/widget/nav.js"></script> <script type="text/javascript"> $(function(){ $("ul.payType li").click(function(){ $(this).css("border","2px solid #E4393C").siblings().css("border-color","#ddd"); }) }) </script> </body> </html>
5.根据Dubbo服务扫描的包创建包(com.pinyougou.pay.service.impl)
♦ 5.1:pyg_pay_interface模块下创建PayService.java接口
public interface PayService{ //请求完微信生成支付地址的方法(二维码) //参数1:商户订单号 参数二:总金额 public Map createNative(String out_trade_no,String total_fee); //请求微信查询支付状态 public Map queryPayStatus(String out_trade_no); //根据用户id获取支付单对象 public TbPayLog searchPayLogFromRedis(String userId); //根据支付单号,更新付款信息 public void updateOrderStatus(String out_trade_no,String transaction_id); }
♦ 5.2:pyg_pay_service模块com.pinyougou.pay.service.impl路径下创建PayServiceImpl.java实现类
@Service public class PayServiceImpl implement PayService{ //拿到配置文件的属性 @Value("${appid}") private String appid;//商户ID @Value("${partner}") private String partner;//商家号 @Value("${partnerkey}") private String partnerkey;//秘钥 @Value("${notifyurl}") private String notifyurl;//回调地址(必填,内容随便) @Autowride private RedisTemplate redisTemplate; @Autowride private TbPayLogMapper payLogMapper; @Autowride private TborderMapper orderMapper; @Override public Map createNative(String out_trade_no,String total_fee){ try{//请求微信的生成订单接口地址,获取支付的连接地址 HttpClientUtils util=new HttpClientUtils("https://api.mch.weixin.qq.com/pay/unifiedorder"); //请求内容数据的封装 Map map= new HashMap<>(); map.put("appid",appid); map.put("mch_id",partner); map.put("nonce_str",WXPayUtil.generateUUID());//随机32位字符串 map.put("body","品优购商品"); //描述信息 map.put("out_trade_no",out_trade_no);//商户订单号 double totalMoney=new Double(total_fee)*100; map.put("total_fee",(long)totalMoney+"") //商品总金额单位为分 map.put("spbill_create_ip","127.0.0.1"); //终端地址 map.put("notify_url",notifyurl); //通知地址 map.put("trade_type","NATIVE"); //交易类型 //通过微信的api将map转成xml格式的字符串 //生成带签名的xml String signedXml= WXPayUtil.generateSignedXml(map,partnerkey); Sytem.out.print("发送给微信"+signedXml) //设置请求参数setXmlParam();方法发送的是xml格式 util.setXmlParam(signedXml); //发送post请求,发送的是xml格式的 util.post(); String content=util.getContent(); Sytem.out.print("微信返回的"+content) //通过工具类将返回的String的xml内容转换成map Map responseMap=WXPayUtil.xmlToMap(content); Map resultMap=new HashMap<>(); resultMap.put("out_trade_no",out_trade_no); //支付单号 resultMap.put("total_fee",total_fee); //支付金额 resultMap.put("code_url",responseMap.get("code_url"));//支付地址 return resultMap; }catch(IOException e){ e.printStackTrace(); } return null; } } @Override public Map queryPayStatus(String out_trade_no){ try{ //1.请求微信的订单状态查询接口 HttpClient util new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery"); //2.封装数据 Map requestMap= new HashMap<>(); requestMap.put("appid",appid); requestMap.put("mch_id",mch_id); requestMap.put("out_trade_no",out_trade_no); requestMap.put("nonce_str",WXPayUtil.generateUUID()); //3.转换带签名的数据 String signedXml=WXPayUtil.generateSignedXml(requestMap,partnerkey); //4.设置到xml格式到post请求中 util.setXmlParam(signedXml); //5.发送请求(xml) util.post(); //6.获取返回值 String content= util.getContent(); Map resultMap= WXPayUtil.xmlToMap(content); return resultMap; }catch(Exception e){ e.printStackTrace(); return null; } } @Override public TbPayLog searchPayLogFromRedis(String userId){ TbPayLog payLog=(TbPayLog) redisTemplate.boundHashOps("payLog").get(userId); return payLog; } @Override //根据支付单号,更新付款信息 public void updateOrderStatus(String out_trade_no,String transaction_id){ //获取支付单对象 TbPayLog payLog= payLogMapper.selectByPrimaryKey(out_trade_no); //获取支付状态,微信的业务代码 payLog.setTransactionId(transaction_id); //支付时间 payLog.setPayTime(new Date()); //支付状态 payLog.setTradeState("1");//0未支付 1已支付 //更新数据库中的payLog数据 payLogMapper.updateByPrimaryKey(payLog); //根据支付单的订单列表,更新订单支付的相关信息 String[] orderIds=payLog.getOrdrList().split(","); for(String orderId :orderIds){ TbOrder order= orderMapper.selectByPrimaryKey(Long.parseLong(orderId); //状态:1 未付款 2 已付款 order.setStatus("2"); order.setPaymentTime(new Date()); order.setUpdateTime(new Date()); orderMapper.updateByPrimaryKey(order); } //将redis中的当前用户的付款信息清除 redisTemplate.boundHash("payLog").delete(payLog.getUserId()); }
6.创建PayController.java类
@RestController @RequestMapping("/pay") public class PayController{ @Reference private PayService payService; @RequestMapping("/createNative") public Map createNative(String out_trade_no,String total_fee){ /* String outTradeNo=UUID.randomUUID().toString().replaceAll("_","");*/ //获取用户的支付单 String userId=SecurityContextHolder.getContext().getAuthentication().getName(); //从Redis中获取用户的支付单 TbPayLog payLog= payService.searchPayLogFromRedis(userId); //设置订单号 付款金额 return payService.createNative(payLog.getOutTradeNo(),payLog.getTotalFee()); } @RequestMapping("/queryPayStatus") public Result queryPayStatus(String out_trade_no){ int timeout=1; //无限循环查询订单支付状态 while(true){ Map resultMap=payService.queryPayStatus(out_trade_no); //失败的逻辑 if(resultMap==null){ return new Result(false,"付款失败!!!"); } //支付成功的逻辑 if("SUCCESS".equals(resultMap.get("trade_state"))){ //修改order的状态 修改paylog的状态 payService.updateOrderStatus(out_trade_no,resultMap.get("transaction_id").toString()); return new Result(true,"付款成功!!!"); } //每隔三秒查询订单的支付状态 try{ Thread.sleep(3000); }catch(Exception e){ } timeout++; //退出逻辑 ,超过该时间,停止支付查询 if(timeout>10){ return new Result(false,"timeout"); } } } }