使用消息队列完成微信支付(含内网穿透)
序言
记录一下这个Demo,结合RabbitMQ实现微信支付(二维码版),使用内网穿透的条件下完成消息异步回调。要实现微信支付的话必须要有企业认证的微信公众号->个人是无法使用的。
实现步骤
微信开发目前官网有很完善的步骤,可访问官网:https://pay.weixin.qq.com/wiki/doc/api/index.html
要实现的是结合自己的项目,利用消息队列的形式解耦。
以下是官网开发的步骤:
其实步骤已经说的很详细了。
(1)商户后台系统根据用户选购的商品生成订单。
(2)用户确认支付后调用微信支付【统一下单API】生成预支付交易;
(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。
(4)商户后台系统根据返回的code_url生成二维码。
(5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。
(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。
(8)微信支付系统根据用户授权完成支付交易。
(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
(11)未收到支付通知的情况,商户后台系统调用【查询订单API】(查单实现可参考:支付回调和查单实现指引)。
(12)商户确认订单已支付后给用户发货。
使用消息队列解耦
使用消息队列的最大好处就是能够解耦不同服务之间的耦合->当我方服务器生成订单时,不需要等待用户完成支付就可以实现对订单状态的监控。
由于涉及到第三方服务器(微信支付服务器),使用异步消息完全不用担心第三方服务发送的各种事故造成我方服务器的瘫痪。
从用户的角度来说:
- 用户在我方服务器点击下单,此时支付微服务就会生成相关的参数(如订单号,企业公众号相关参数),通过HTTP请求,调用统一下单API实现下单。
- 微信支付服务器收到请求后就会在内部创建订单,将订单参数以及支付地址发送到我方服务器
- 我方服务器此时就可以拿到这个地址,通过第三方插件来生成二维码->用户此时就可以扫码支付
- 用户进入扫码支付阶段,完全是跟微信服务器进行对接的,此时跟我方服务无关。
在用户成功支付时,此时微信服务器会发送支付结果通知到我方服务器,此时我们只需要把结果发送到消息队列中,让对应服务监听消费即可。
实现微信支付
记录一下实现该功能的伪代码
首先引入微信支付的相关SDK、发起HTTP请求的HTTPClient
<!--微信支付,主要用于处理XML和MAP的转换,以及生成随机字符串--><dependency><groupId>com.github.wxpay</groupId><artifactId>wxpay-sdk</artifactId><version>0.0.3</version></dependency><!--httpclient支持--><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId></dependency>
分为三个角色:订单微服务,支付微服务、微信支付服务
**
在支付微服务中,调用微信统一支付API,从而得到回传的二维码。
创建一个service,实现调用。使用HTTPClient发送HTTP请求
根据微信文档的要求,请求需要发送的都是XML格式,且都是POST请求
/*** 生成二维码,在service中完成,控制层去调用该方法即可* 使用微信SDK生成随机字符串,封装参数后发送HTTP请求,拿到回调的信息* 腾讯的回调信息都是XML格式,因此需要转为MAP结构* @param paramterMap :传入参数包含:订单号和金额(单位为分,人民币)* @return*/@Overridepublic Map createNative(Map<String, String> paramterMap) {try {//1、封装参数Map<String,String> param = new HashMap();param.put("appid", "改成自己的appid"); //应用IDparam.put("mch_id", "改成自己的mch_id"); //商户ID号param.put("nonce_str", WXPayUtil.generateNonceStr()); //使用微信SDK生成随机字符串param.put("body", "此处填写相关订单信息"); //订单描述param.put("out_trade_no",paramterMap.get("outTradeNo")); //商户订单号param.put("total_fee", paramterMap.get("totalFee")); //交易金额param.put("spbill_create_ip", "127.0.0.1"); //终端IPparam.put("notify_url", "接收回调信息的地址"); //回调地址param.put("trade_type", "NATIVE"); //交易类型NATIVE表示二维码支付//将map转为XMLString paramXml = WXPayUtil.generateSignedXml(param, "此处填写公众号的私钥");//发送HTTPS请求String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//调用自定义的方法wxPayResultMap(),得到请求结果。Map<String, String> stringMap = wxPayResultMap(paramXml, url);return stringMap;} catch (Exception e) {e.printStackTrace();return null;}}/ *** 封装成一个通用的获取参数* @param xml 需要添加的xml地址* @param url 需要请求的url* @return 发起HTTP请求返回的结果集*/private Map<String,String> wxPayResultMap(String xml,String url){try {//创建httpClient对象HttpClient httpClient = new HttpClient(url);//设置请求信息httpClient.setHttps(true);httpClient.setXmlParam(xml);//发起请求httpClient.post();//获取参数String content = httpClient.getContent();//转为MapMap<String, String> map = WXPayUtil.xmlToMap(content);return map;} catch (Exception e) {e.printStackTrace();return null;}}
此时使用在控制层调用方法即可,需要传入的参数为->订单号和金额(单位为分,人民币)
严格的说是在订单微服务中,调用feign来实现。
通过POSTMan得到的结果如下:
其中code_url是用来生成二维码的地址
生成访问二维码
可以使用前端的js插件来完成 : ** qrious.js**
源码:
链接:https://pan.baidu.com/s/1YY6noRkjYpdtcCsJ7WNP8w
提取码:6sbu
使用步骤很简单,建立一个HTML页面,引入JS文件,编写demo代码即可
记得更改value的值-> code_url
<html>
<head>
<title>二维码支付生成</title>
<!--1.引入js
2. 创建一个img标签 用来存储显示二维码的图片
3.创建js对象 4.设置js对象的配置项--><script src="qrious.js"> </script></head>
<body><img id="myqrious" ></body><script>var qrious = new QRious({element:document.getElementById("myqrious"),// 指定的是图片所在的DOM对象size:250,//指定图片的像素大小level:'H',//指定二维码的容错级别(H:可以恢复30%的数据)value:'weixin://wxpay/bizpayurl?pr=MjkreGMzz'//指定二维码图片代表的真正的值})</script></html>
查询订单信息
那么如何查询订单是否支付了呢?
微信已经提供好了相关的API,根据文档提示操作即可
![image.png](https://img-blog.csdnimg.cn/img_convert/8b1cd26e7636b30b4300f6e0e947b78e.png#align=left&display=inline&height=300&margin=[object Object]&name=image.png&originHeight=600&originWidth=1247&size=77690&status=done&style=none&width=623.5)
代码实现
/*** 查询订单支付状态* WXPayUtil ->微信提供的SDK工具类* @param outTradeNo //商户订单号* @return*/@Overridepublic Map<String, String> queryStatus(String outTradeNo) {try {Map<String,String> param = new HashMap();param.put("appid", "改成自己的appid"); //应用IDparam.put("mch_id", "改成自己的mch_id"); //商户ID号param.put("nonce_str", WXPayUtil.generateNonceStr()); //随机数param.put("out_trade_no",outTradeNo); //商户订单号//将map转为XMLString paramXml = WXPayUtil.generateSignedXml(param, "改成自己的秘钥");String url = "https://api.mch.weixin.qq.com/pay/orderquery";Map<String, String> map = wxPayResultMap(paramXml, url);return map;} catch (Exception e) {e.printStackTrace();return null;}}/ *** 封装成一个通用的获取参数* @param xml 需要添加的xml地址* @param url 需要请求的url* @return 发起HTTP请求返回的结果集*/private Map<String,String> wxPayResultMap(String xml,String url){try {//创建httpClient对象HttpClient httpClient = new HttpClient(url);//设置请求信息httpClient.setHttps(true);httpClient.setXmlParam(xml);//发起请求httpClient.post();//获取参数String content = httpClient.getContent();//转为MapMap<String, String> map = WXPayUtil.xmlToMap(content);return map;} catch (Exception e) {e.printStackTrace();return null;}}
依旧在控制层调用,传入订单编号即可
异步消息通知
以上是主动的去查询订单信息,微信会自动将消息传给我吗?
答案是肯定的。在 调用统一下单API时需要传入一个**notify_url **, 表示的就是需要接收微信信息的回调地址
param.put("notify_url", "接收回调信息的地址"); //回调地址
只需要根据支付结果通知API文档的参数来处理即可
此时需要将获取到的信息,丢到消息队列中,让订单微服务去监听消费->如根据订单的结果做订单的删除、修改,商品库存的回退等等操作
此操作在Controller中完成
//注入 RabbitTemplate@Autowiredprivate RabbitTemplate rabbitTemplate;/**** 支付回调* @param request* @return*/@RequestMapping(value = "/notify/url")public String notifyUrl(HttpServletRequest request) throws Exception {//输入流ServletInputStream inputStream = request.getInputStream();ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();//将输入流转为输出流,并写为String字符串,转为Mapbyte[] bytes = new byte[1024];int len = 0;while ((len=inputStream.read(bytes))!=-1){byteArrayOutputStream.write(bytes,0,len);}byte[] byteArray = byteArrayOutputStream.toByteArray();//装为字符串String xml = new String(byteArray);//将消息转为MapMap<String, String> map = WXPayUtil.xmlToMap(xml);System.out.println("map:"+map);//将map的信息发送到mqrabbitTemplate.convertAndSend("exchange.order","queue.order", JSON.toJSONString(map));//微信规定需要返回该xml给它String result = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";return result;}
随后订单微服务监听并消费即可。
但是问题来了,在开发环境时的回调地址,是本地的地址
如我的地址是:
localhost:18092/wx/pay/notifly
此时微信服务器是无法访问到这个地址的
因此可以使用内网穿透的形式:
百度百科:
内网穿透,也即 NAT 穿透,进行 NAT 穿透是为了使具有某一个特定源 IP 地址和源端口号的数据包不被 NAT 设备屏蔽而正确路由到内网主机。下面就相互通信的主机在网络中与 NAT 设备的相对位置介绍内网穿透方法
使用内网穿透需要一个第三方的供应商来给我们提供一个域名,这个域名一般是第三方的域名,因此我们使用的时候就不需要进行备案即可。
使用内网穿透,能够让公网的用户访问到你的电脑。
目前提供的服务商有很多如:
断续
花生壳
…
此处演示->断续 的过程
访问地址:https://cloud.zhexi.tech/auth/signin
扫码登录,进行一系列的认证即可,需要按提示安装客户端,都弄完了就点击建立隧道:
然后按照这个顺序点击新建
此时会生成一个地址,通过这个地址就能访问到我本地127.0.0.1:8080端口
到了这一步之后其实已经完成了内网穿透,此时通过这个域名就能让微信服务器访问到你的本机。
使用消息队列完成微信支付(含内网穿透)相关推荐
- 毕业设计学习锋迷商城的的笔记(自己设计并手写后台商品管理,分类管理,用户,地址管理系统,订单管理,微信支付(内网穿透))
文章目录 自己添加的后端管理页面 视频演示效果 论文地址 后台代码视频实现讲解思路 1. 商品管理 2.商品分类管理 3.商品地址管理 4.权限管理系统 权限管理系统文章地址 5.订单管理 5.1 6 ...
- 微信支付之 内网穿透
一.免费工具的下载 ngrok.com 打开网址 注册账号密码登录,下载工具 解压文件后进入文件夹在此处用cmd指令 打开命令行后为计算机授权 WIN系统 ngrok authtoken 6a6Yc6 ...
- 本地调试支付回调内网穿透工具
一,为什么使用内网穿透 我想装个B让其他同学在外网访问我的程序,应该怎么办? 接了个小外包,给客户演示Demo没有站点怎么办? 做微信.支付宝支付等其他第三方平台的功能,没有外网回调地址,应该怎么办? ...
- 项目对接支付宝支付,内网穿透实现监听支付宝的支付成功异步回调通知
调试的话使用支付宝的沙箱环境来进行调试 调试的时候因为是本地调试,没有上线的域名,无法被支付宝的异步回调请求所打到,这种情况下可以使用内网穿透来进行解决 内网穿透步骤: 内网穿透的几个常用软件 1.n ...
- Springboot整合支付宝支付加内网穿透工具实现本地回调
项目中我们集成了支付宝,用户可以使用支付宝来进行支付 项目测试时,我们当然也需要一套测试环境对支付流程进行测试 如果我们使用线上的支付宝配置进行测试,那么我们必须使用真实货币进行支付 并且支付的金额会 ...
- SpringBoot 支付宝沙箱支付 natapp内网穿透
沙箱应用支付宝开放平台 (alipay.com) 1.生成商户私钥.公钥(记得保存) 2.利用商户公钥生成支付宝公钥 Natapp内网穿透 NATAPP-内网穿透 基于ngrok的国内高速内网映射工具 ...
- 微信开发-ngrok内网穿透部署
由于要接入微信公众号,查看了官网上的接入文档,必需是80端口而且微信服务器需要验证token,那我本地开发不可能每次都把源码上传到服务器上吧,而且也不方便,这就需要能内网穿透的工具,能让外网通过外网域 ...
- 本地调试微信接口(内网穿透到外网)
原文链接:http://blog.csdn.net/xyang81/article/details/52141881 在做微信开发的时候,调用微信接口成功之后,微信会回调我们事先配置好的一个接口.由于 ...
- 消息队列处理微信支付超时订单
1.配置交换机.队列 RabbitMqConfig /*** * 延时队列交换机* * 注意这里的交换机类型:CustomExchange* ** * @return* */ @Bean publ ...
最新文章
- Add Digits
- 【原】Github系列之三:开源iOS下 渐变颜色的进度条WGradientProgress
- 为什么用户体验设计师需要像建筑师一样思考?
- linux系统开发环境配置
- 实现tinyc语言的扫描程序_适合编程小白的C语言设计习题,实现自动发牌程序!源码分享!...
- 中科院博士返乡卖汉服:3个月卖了300万,预计全年能破1500万
- 想要写好的程序应该远离计算机
- POLL原理分析与java实战
- mysql链接压测_MySQL压测工具mysqlslap的介绍与使用
- 英特尔 开源降噪库 api_Google的新操作系统,英特尔的开源VR耳机以及更多新闻
- 颠覆大数据分析之结论
- web前端常用开发工具排行:8款html开发工具推荐下载
- flash遮罩弹性跟随效果
- 直播六脉神剑,练好这几招才能行走江湖
- 基于百度地图实现Android定位功能实现(详解+教程)
- 【SQLite3+Qt开发】SQLite3简要介绍+在Qt5中的使用步骤
- 最大团问题(使用递归和非递归两种方法)
- 线程同步,为什么要引入线程同步?
- win7下通过easyBCD引导安装Ubuntu14.04(补充完善版)
- wifi卡慢延迟高_家里WIFI越用越卡?教你3个小方法,彻底解决网速慢、卡顿等问题...
热门文章
- 如何查找项目的源代码
- js`${}` 艺术字体语法
- 东软载波M0,HR8P506开发应用初阶-搭建环境及点亮LED.
- vue拖拽组件插件 vue-draggable-resizable-gorkys
- 让AI 作画更快一点
- 【自制分享】低成本做一个B站小电视!(゜-゜)つロ 干杯~-bilibili
- php循环控制打印表格隔行变色的两种方法
- 论文阅读:Data Platform for Machine Learning
- ESP8266-Arduino编程实例-BMP180气压温度传感器驱动
- 一、DMSP/OLS、NPP/VIIRS夜间灯光数据之城市建成区提取之理论介绍