1. 微信支付快速入门
一.微信支付申请(了解)
- 第一步:注册公众号(类型须为:服务号)
请根据营业执照类型选择以下主体注册:个体工商户| 企业/公司| 政府| 媒体| 其他类型。 - 第二步:认证公众号
公众号认证后才可申请微信支付,认证费:300元/次。 - 第三步:提交资料申请微信支付
登录公众平台,点击左侧菜单【微信支付】,开始填写资料等待审核,审核时间为1-5个工作日内。 - 第四步:开户成功,登录商户平台进行验证
资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填写财付通备付金打的小额资金数额,完成账户验证。 - 第五步:在线签署协议
本协议为线上电子协议,签署后方可进行交易及资金结算,签署完立即生效。完成上述步骤,你可以得到调用API用到的账号和密钥
appid:微信公众账号或开放平台APP的唯一标识
mch_id:商户号
key:商户密钥
二.微信支付开发文档与SDK
- 在线微信支付开发文档:
https://pay.weixin.qq.com/wiki/doc/api/index.html
微信支付接口调用的整体思路:
按API要求组装参数,以XML方式发送(POST)给微信支付接口(URL),微信支付接口也是以XML方式给予响应。程序根据返回的结果(其中包括支付URL)生成二维码或判断订单状态。
我们解压从官网下载的sdk ,安装到本地仓库
三.统一下单API
- 新建工程,引入微信支付Api
- 创建Config类 ,继承自抽象类WXPayConfig
- 创建测试类,编写代码
在调用API的代码中,我们经常用到WXPayUtil提供的以下功能:
WXPayRequest为API的支付请求类,封装了httpClient。 - 执行后返回结果
其中的code_url就是我们的支付URl ,我们可以根据这个URl 生成支付二维码
四.二维码JS插件- QRcode.js
- QRCode.js 是一个用于生成二维码的 JavaScript 库。主要是通过获取 DOM 的标签,再通过 HTML5 Canvas 绘制而成,不依赖任何库。支持该库的浏览器有:IE6~10, Chrome,Firefox, Safari, Opera, Mobile Safari, Android, Windows Mobile, 等
- 我们新建页面,编写一下代码
五.内网映射工具EchoSite
- 在请求统一下单接口时,有个参数notify_url ,这个是回调地址,也就是在支付成功后微信支付会自动访问这个地址,通知业务系统支付完成。但这个地址必须是互联网可以访问的(也就是有域名的)。
- 那么如何测试呢?我们可以借助一个工具 EchoSite 内网映射工具
(1)打开网址: https://www.echosite.cn/ 注册用户,登录到控制台下载客户端。
(2)支付2元买一个域名(可以用1个月),点击域名端口---抢注域名
(3)使用课程提供的软件echosite ,添加config.yml
然后在echosite目录中输入以下命令
这样你购买的域名就映射到127.0.0.1:9102上了。 ctrl+c 结束程序
2. 青橙-微信支付二维码
一.需求分析
- 用户在提交订单后,如果是选择支付方式为微信支付,那应该跳转到微信支付二维码页面,用户扫描二维码可以进行支付,金额与订单金额相同。
二.实现思路
- 前端页面向后端传递订单号,后端根据订单号查询订单,检查是否为当前用户的未支付订单,如果是则根据订单号和金额生成支付url返给前端,前端得到支付url生成支付二维码。
三.后端代码
- qingcheng_service_order服务 pom.xml添加依赖
- 创建config类
在spring配置文件中进行配置 - qingcheng_interface创建包com.qingcheng.service.pay ,包下创建接口
- qingcheng_service_pay新增服务类
- qingcheng_web_portal新增PayController
四.前端代码
- qingcheng_web_portal新增weixinpay.html 、paysuccess.html、payfail.html
- 将二维码插件 qrcode.min.js 拷贝到qingcheng_web_portal下的js目录
- 修改weixinpay.html
添加js代码
显示订单号和金额
修改页面二维码部分 - 修改pay.html,完成到微信支付页面的跳转
调用
五.修改配置文件并测试
- 修改qingcheng_web_portal的cas.properties
- 修改spring-security-portal.xml,添加配置
3. 青橙-支付回调逻辑处理
一.需求分析
- 在完成支付后,修改订单状态为已支付,并记录订单日志。
二.实现思路
- 接受微信支付平台的回调信息(xml)
- 通过签名验证内容的合法性
- 如果支付结果为成功,则调用修改订单状态和记录订单日志的方法。
三.接收回调信息
- 微信支付平台发送给回调地址的是二进制流,我们需要提取二进制流转换为字符串,这个字符串就是xml格式。
修改notifyLogic方法 - 测试后,在控制台看到输出的消息
我们可以将此xml字符串,转换为map,提取其中的out_trade_no(订单号),根据订单号修改订单状态。
四.内容解析与签名验证
- WxPayService新增方法定义
- WxPayServiceImpl实现此方法
五.修改订单状态
- OrderService接口新增方法定义
- OrderServiceImpl新增方法实现
- 在WxPayServiceImpl的notifyLogic方法调用updatePayStatus方法
4. 青橙-推送支付通知
一.需求分析
- 当用户完成扫码支付后,跳转到支付成功页面
二.服务端推送方案
- 我们需要将支付的结果通知前端页面,其实就是我们通过所说的服务器端推送,主要有三种实现方案
- Ajax 短轮询
Ajax 轮询主要通过页面端的 JS 定时异步刷新任务来实现数据的加载如果我们使用ajax短轮询方式,需要后端提供方法,通过调用微信支付接口实现根据订单号查询支付状态的方法(参见查询订单API) 。 前端每间隔三秒查询一次,如果后端返回支付成功则执行页面跳转。
缺点:这种方式实时效果较差,而且对服务端的压力也较大。 - 长轮询
长轮询主要也是通过 Ajax 机制,但区别于传统的 Ajax 应用,长轮询的服务器端会在没有数据时阻塞请求直到有新的数据产生或者请求超时才返回,之后客户端再重新建立连接获取数据。如果使用长轮询,也同样需要后端提供方法,通过调用微信支付接口实现根据订单号查询支付状态的方法,只不过循环是写在后端的。
缺点:长轮询服务端会长时间地占用资源,如果消息频繁发送的话会给服务端带来较大的压力。 - WebSocket 双向通信
WebSocket 是 HTML5 中一种新的通信协议,能够实现浏览器与服务器之间全双工通信。如果浏览器和服务端都支持 WebSocket 协议的话,该方式实现的消息推送无疑是最高效、简洁的。并且最新版本的 IE、Firefox、Chrome 等浏览器都已经支持 WebSocket协议,Apache Tomcat 7.0.27 以后的版本也开始支持 WebSocket。
三.RabbitMQ Web STOMP 插件
- 借助于 RabbitMQ 的 Web STOMP 插件,实现浏览器与服务端的全双工通信。从本质上说,RabbitMQ 的 Web STOMP 插件也是利用 WebSocket 对 STOMP 协议进行了一次桥接,从而实现浏览器与服务端的双向通信。
- STOMP协议
STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议。前身是TTMP协议(一个简单的基于文本的协议),专为消息中间件设计。它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。STOMP协议由于设计简单,易于开发客户端,因此在多种语言和多种平台上得到广泛地应用。 - 插件安装
我们进入rabbitmq的sbin目录,执行下面的命令开启stomp插件
安装后重新启动rabbitmq - 消息推送测试
我们在浏览器访问http://localhost:15670 可以看到stomp的例子代码 。
将stomp.min.js拷贝到web_portal工程
我们根据stomp的例子代码创建一个页面,内容如下:
我们在rabbitmq中创建一个叫paynotify的交换机(fanout类型)测试,我们在rabbitmq管理界面中向paynotify交换机发送消息,页面就会接收这个消息。
为了安全,我们在页面上不能用我们的rabbitmq的超级管理员用户guest,所以我们需要在rabbitmq中新建一个普通用户webguest(普通用户无法登录管理后台)
四.后端代码
- 实现思路:后端在收到回调通知后发送订单号给mq(paynotify交换器),前端通过stomp连接到mq订阅paynotify交换器的消息,判断接收的订单号是不是当前页面的订单号,如果是则进行页面的跳转。
- 修改WxPayServiceImpl,引入rabbitmq
- 修改notifyLogic方法,在 "SUCCESS".equals(map.get("result_code")) 后添加
五.前端代码
- 修改weixinpay.html ,引入stomp.min.js
添加js代码
在createNative方法的回调处添加代码 - 修改paysuccess.html,添加代码
5. 青橙-超时未支付订单处理
一.需求分析
- 超过60分钟未支付的订单,我们需要进行超时订单的处理:先调用微信支付api,查询该订单的支付状态。如果未支付调用关闭订单的api,并修改订单状态为已关闭,并回滚库存数。如果该订单已经支付,做补偿操作(修改订单状态和记录)。
二.实现思路
- 如何获取超过60分钟的订单?我们目前有两种实现方案
(1)定时任务轮询方案
编写定时任务,查询所有60分钟前创建的订单列表。
循环此订单列表,查询每个订单的支付状态。如果已支付进行状态补偿,如果未支付则关闭订单。
这种实现方案缺点是时间精度不高,对系统压力比较大。
(2)使用延迟消息队列
所谓延迟消息队列,就是消息的生产者发送的消息并不会立刻被消费,而是在设定的时间之后才可以消费。
我们可以在订单创建时发送一个延迟消息,消息为订单号,青橙系统会在60分钟后取出这个消息,然后查询订单的支付状态,根据结果做出相应的处理。
我们在青橙中采用延迟消息队列的实现方案。
另外我们还会用到微信支付api中的查询订单和关闭订单。
三.rabbitmq延迟消息
- 使用RabbitMQ来实现延迟消息必须先了解RabbitMQ的两个概念:消息的TTL和死信Exchange,通过这两者的组合来实现上述需求。
- 消息的TTL(Time To Live)
消息的TTL就是消息的存活时间。RabbitMQ可以对队列和消息分别设置TTL。对队列设置就是队列没有消费者连着的保留时间,也可以对每一个单独的消息做单独的设置。超过了这个时间,我们认为这个消息就死了,称之为死信。
我们创建一个队列queue.temp,在Arguments 中添加x-message-ttl 为5000 (单位是毫秒),那所在压在这个队列的消息在5秒后会消失。 - 死信交换器 Dead Letter Exchanges
(1) 一个消息被Consumer拒收了,并且reject方法的参数里requeue是false。也就是说不会被再次放在队列里,被其他消费者使用。
(2)上面的消息的TTL到了,消息过期了。
(3)队列的长度限制满了。排在前面的消息会被丢弃或者扔到死信路由上。Dead Letter Exchange其实就是一种普通的exchange,和创建其他exchange没有两样。只是在某一个设置Dead Letter Exchange的队列中有消息过期了,会自动触发消息的转发,发送到Dead Letter Exchange中去。 - 我们现在可以测试一下延迟队列。
(1)创建死信交换器 exchange.ordertimeout (fanout)
(2)创建队列queue.ordertimeout
(3)建立死信交换器 exchange.ordertimeout 与队列exchange.ordertimeout 之间的绑定 routingkey为
exchange.ordertimeout.key
(4)创建队列queue.order,Arguments添加
x-message-ttl=5000
x-dead-letter-exchange: exchange.ordertimeout
x-dead-letter-routing-key: exchange.ordertimeout.key
四.代码实现(作业)
- 创建队列queue.ordercreate,Arguments添加
x-message-ttl=3600000
x-dead-letter-exchange: exchange.ordertimeout
x-dead-letter-routing-key: exchange.ordertimeout.key - 在创建订单的方法中添加代码,发送消息到queue.ordercreate ,消息内容为订单号
- 在WxPayServiceImpl中编写业务逻辑方法,调用微信支付api中的查询订单api,实现根据订单号查询支付结果。
- 在WxPayServiceImpl中编写业务逻辑方法,调用微信支付api中的关闭订单api,实现根据订单号关闭微信订单。
- 在OrderServiceImpl中编写关闭订单的业务逻辑方法,逻辑为调用微信支付关闭订单的方法、修改订单表的订单状态、记录订单日志、恢复商品表库存。
- 在订单服务中提取queue.ordertimeout 队列 中的消息(订单号),先查询业务系统的订单状态,如果订单状态为未支付,调用微信支付查询订单的方法查询。如果返回结果是未支付,调用关闭订单的业务逻辑方法。 如果返回结果是已支付,实现补偿操作。