本文共 23145 字,大约阅读时间需要 77 分钟。
整个流程使用到的组件代码:
链接: 提取码:ys87利用qrious制作二维码插件。
qrious是一款基于HTML5 Canvas的纯JS二维码生成插件。通过qrious.js可以快速生成各种二维码,你可以控制二维码的尺寸颜色,还可以将生成的二维码进行Base64编码。
qrious.js二维码插件的可用配置参数如下:
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
background | String | “white” | 二维码的背景颜色。 |
foreground | String | “black” | 二维码的前景颜色。 |
level | String | “L” | 二维码的误差校正级别(L, M, Q, H)。 |
mime | String | “image/png” | 二维码输出为图片时的MIME类型。 |
size | Number | 100 | 二维码的尺寸,单位像素。 |
value | String | “” | 需要编码为二维码的值,一般是跳转url |
下面的代码即可生成一张二维码
二维码入门小demo
运行效果:
微信扫码支付是商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站支付、实体店单品或订单支付、媒体广告支付等场景。
申请步骤:
第一步:注册公众号(类型须为:服务号)
请根据营业执照类型选择以下主体注册:| | | | 。
需要企业,才可微信支付。
第二步:认证公众号
公众号认证后才可申请微信支付,认证费:300元/次。
第三步:提交资料申请微信支付
登录公众平台,点击左侧菜单【微信支付】,开始填写资料等待审核,审核时间为1-5个工作日内。
第四步:开户成功,登录商户平台进行验证
资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填写财付通备付金打的小额资金数额,完成账户验证。
第五步:在线签署协议
本协议为线上电子协议,签署后方可进行交易及资金结算,签署完立即生效。
有“传智播客”的微信支付账号,学员无需申请。
微信支付接口调用的整体思路:
按API要求组装参数,以XML方式发送(POST)给微信支付接口(URL),微信支付接口也是以XML方式给予响应。程序根据返回的结果(其中包括支付URL)生成二维码或判断订单状态。
在线微信支付开发文档:
Native二维码扫码支付。
”统一下单”和”查询订单”两组API
1. appid:微信公众账号或开放平台APP的唯一标识2. mch_id:商户号 (配置文件中的partner)3. partnerkey:商户密钥4. sign:数字签名, 根据微信官方提供的密钥和一套算法生成的一个加密信息, 就是为了保证交易的安全性
商家二维码不过时。
业务流程说明:
1.商户后台系统根据微信支付规定格式生成二维码(规则见下文),展示给用户扫码。2.用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。3.微信支付系统收到客户端请求,发起对商户后台系统支付回调URL的调用。调用请求将带productid和用户的openid等参数,并要求商户系统返回交数据包,详细请见"本节3.1回调数据输入参数"4.商户后台系统收到微信支付系统的回调请求,根据productid生成商户系统的订单。5.商户系统调用微信支付【统一下单API】请求下单,获取交易会话标识(prepay_id)6.微信支付系统根据商户系统的请求生成预支付交易,并返回交易会话标识(prepay_id)。7.商户后台系统得到交易会话标识prepay_id(2小时内有效)。8.商户后台系统将prepay_id返回给微信支付系统。返回数据见"本节3.2回调数据输出参数"9.微信支付系统根据交易会话标识,发起用户端授权支付流程。10.用户在微信客户端输入密码,确认支付后,微信客户端提交支付授权。11.微信支付系统验证后扣款,完成支付交易。12.微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。13.微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。14.未收到支付通知的情况,商户后台系统调用【查询订单API】。15.商户确认订单已支付后给用户发货。
商家二维码会过时。
业务流程说明:
1.商户后台系统根据用户选购的商品生成订单。2.用户确认支付后调用微信支付【统一下单API】生成预支付交易;3.微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。4.商户后台系统根据返回的code_url生成二维码。5.用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。6.微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。7.用户在微信客户端输入密码,确认支付后,微信客户端提交授权。8.微信支付系统根据用户授权完成支付交易。9.微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。10.微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。11.未收到支付通知的情况,商户后台系统调用【查询订单API】。12.商户确认订单已支付后给用户发货。
微信支付提供了SDK, 下载后打开源码,install到本地仓库。
安装SDK,jar包。
使用微信支付SDK(开发工具包), 在maven工程中引入依赖
0.0.3 com.github.wxpay wxpay-sdk ${wxpay.version}
我们主要会用到微信支付SDK的以下功能:
@Test public void test() throws Exception { //生成随机字符串 String s = WXPayUtil.generateNonceStr(); System.out.println("随机字符串:"+s); //将map转成xml字符串 Mapparam = new HashMap<>(); param.put("id","1"); param.put("title","吴泽强"); String mapToXml = WXPayUtil.mapToXml(param); System.out.println("map转成xml字符串:\n"+mapToXml); //将map转成字符串,并且生成签名 String partnerKey = "wzq"; //私钥 String generateSignedXml = WXPayUtil.generateSignedXml(param, partnerKey); System.out.println("xml字符串带有签名:\n"+generateSignedXml); //将XML字符串转成map Map mapResult = WXPayUtil.xmlToMap(mapToXml); System.out.println("xml字符串转成map"+mapResult); }
支付可单独是一个微服务。
依赖:
0.0.3 com.github.wxpay wxpay-sdk ${wxpay.version}
商户到腾讯官网申请才有的配置application.yml:
#微信支付信息配置,这里是黑马提供的weixin: #应用id appid: wx8397f8696b538317 #商户id partner: 1473426802 #私钥 partnerkey: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb #支付回调地址,自己本机模拟,花生壳 notifyurl: http://c3009841m5.zicp.vip:动态端口/weixin/pay/notify/url
appid: 微信公众账号或开放平台APP的唯一标识
partner:财付通平台的商户账号
partnerkey:财付通平台的商户密钥
notifyurl: 回调地址
默认二维码2小时失效,具体官网文档有说。
在支付页面上生成支付二维码,并显示订单号和金额
用户拿出手机,打开微信扫描页面上的二维码,然后在微信中完成支付
官网文档:
二维码只能支付成功一次,成功后则失效,默认2小时未支付,二维码无效,具体修改无效时长看文档。结合HttpClient工具类实现。我们传给微信服务器和微信服务器传过来都是xml格式的,有工具类可以转。
dto:
参数dto和返回dto,看情况使用。
/** * Title: * Description:创建二维码需要的参数 * @author WZQ * @version 1.0.0 * @date 2020/3/13 */public class NativeParamDto implements Serializable { private static final long serialVersionUID = -2376427360982551378L; //客户端自定义订单编号 private String outTradeNo; //交易金额(单位:分) private String totalFee; //set.get..}/** * Title: * Description:创建二维码成功的结果 * @author WZQ * @version 1.0.0 * @date 2020/3/13 */public class NativeDto implements Serializable { private static final long serialVersionUID = -2376427360982551378L; //客户端自定义订单编号 private String outTradeNo; //交易金额(单位:分) private String totalFee; //二维码地址 private String codeUrl //set.get..}
servie:
/** * 创建二维码 * @param paramMap 客户端自定义订单编号;交易金额,单位:分 * @return */ MapcreateNative(Map paramMap);
import com.changgou.commons.utils.HttpClient;import com.changgou.service.pay.service.WeixinPayService;import com.github.wxpay.sdk.WXPayUtil;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service;import java.util.HashMap;import java.util.Map;@Servicepublic class WeixinPayServiceImpl implements WeixinPayService { @Value("${weixin.appid}") private String appid; @Value("${weixin.partner}") private String partner; @Value("${weixin.partnerkey}") private String partnerkey; @Value("${weixin.notifyurl}") private String notifyurl; /** * 创建二维码 * @param parameterMap 客户端自定义订单编号;交易金额(单位:分) * @return */ @Override public MapcreateNative(Map parameterMap){ //客户端自定义订单编号;交易金额(单位:分) String outTradeNo = parameterMap.get("no"); String totalFee = parameterMap.get("money"); try { //1、封装参数 Map paramMap = new HashMap<>(); paramMap.put("appid", appid); //应用ID paramMap.put("mch_id", partner); //商户ID号 paramMap.put("nonce_str", WXPayUtil.generateNonceStr()); //随机数 paramMap.put("body", "畅购"); //订单描述 paramMap.put("out_trade_no", outTradeNo); //商户订单号 paramMap.put("total_fee", totalFee); //交易金额,单位分 paramMap.put("spbill_create_ip", "127.0.0.1"); //终端IP,请求的地址 paramMap.put("notify_url", notifyurl); //回调地址 paramMap.put("trade_type", "NATIVE"); //交易类型,扫码 //2、将参数转成xml字符,并携带签名 String paramXml = WXPayUtil.generateSignedXml(paramMap, partnerkey); ///3、执行请求,微信统一下单api HttpClient httpClient = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder"); httpClient.setHttps(true); httpClient.setXmlParam(paramXml); httpClient.post(); //执行 //4、获取参数 String content = httpClient.getContent(); Map stringMap = WXPayUtil.xmlToMap(content); System.out.println("stringMap:"+stringMap); //5、获取部分页面所需参数 Map dataMap = new HashMap (); dataMap.put("code_url", stringMap.get("code_url")); dataMap.put("out_trade_no", outTradeNo); dataMap.put("total_fee", totalFee); return dataMap; } catch (Exception e) { e.printStackTrace(); } return null; }}
controller:
@RestController@RequestMapping(value = "/weixin/pay")@CrossOriginpublic class WeixinPayController { @Autowired private WeixinPayService weixinPayService; /*** * 创建二维码 * 参数是订单号和金额,分为单位 * @return */ @RequestMapping(value = "/create/native") public ResponseResult
测试 http://localhost:18092/weixin/pay/create/native?no=1714080902133&money=1:
这里是表单传参的,json的话也可以,自定义好dto即可。
把code_url的地址显示到qrious组件的前端页面,存放到value对应的值即是一个二维码
每次实现支付之后,微信支付都会将用户支付结果返回到指定路径,而指定路径是指创建二维码的时候填写的notifyurl
参数,响应的数据以及相关文档参考一下地址:
通知参数如下:
字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
---|---|---|---|---|---|
返回状态码 | return_code | 是 | String(16) | SUCCESS | SUCCESS |
返回信息 | return_msg | 是 | String(128) | OK | OK |
以下字段在return_code为SUCCESS的时候有返回
字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
---|---|---|---|---|---|
公众账号ID | appid | 是 | String(32) | wx8888888888888888 | 微信分配的公众账号ID(企业号corpid即为此appId) |
业务结果 | result_code | 是 | String(16) | SUCCESS | SUCCESS/FAIL |
商户订单号 | out_trade_no | 是 | String(32) | 1212321211201407033568112322 | 商户系统内部订单号 |
微信支付订单号 | transaction_id | 是 | String(32) | 1217752501201407033233368018 | 微信支付订单号 |
回调地址接收到数据后,需要响应信息给微信服务器,告知已经收到数据,不然微信服务器会再次发送4次请求推送支付信息。
字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
---|---|---|---|---|---|
返回状态码 | return_code | 是 | String(16) | SUCCESS | 请按示例值填写 |
返回信息 | return_msg | 是 | String(128) | OK | 请按示例值填写 |
举例如下:
WeixinPayController, 添加回调方法,代码如下:
/*** * 支付回调,数据流数据 * @param request * @return */@RequestMapping(value = "/notify/url")public String notifyUrl(HttpServletRequest request){ InputStream inStream; try { //读取支付回调数据 inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; //缓存区 int len = 0; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } outSteam.close(); inStream.close(); // 将支付回调数据转换成xml字符串 String result = new String(outSteam.toByteArray(), "utf-8"); //将xml字符串转换成Map结构 Mapmap = WXPayUtil.xmlToMap(result); //响应数据设置 Map respMap = new HashMap<>(); respMap.put("return_code","SUCCESS"); respMap.put("return_msg","OK"); return WXPayUtil.mapToXml(respMap); } catch (Exception e) { e.printStackTrace(); //记录错误日志 } return null;}
本地机可以使用花生壳来模拟外网访问我们的本地机服务。花生壳服务器通过该软件和账号把一个域名和本地机的ip绑定在一起,实现内网穿透。通过随机域名可外网访问本地机。
外网访问:http://c3009841m5.zicp.vip:动态端口/weixin/pay/notify/url
官网文档:
通过HttpClient工具类实现对远程支付接口的调用。具体参数参见“查询订单”API, 我们在controller方法中轮询调用查询订单(间隔3秒),当返回状态为SUCCESS时,我们会在controller方法返回结果。前端代码收到结果后跳转到成功页面。默认统一下单api是有回调地址notifyurl,我们自己设定的地址,一般微信服务器支付成功后,会回调该地址过来,默认5次,该回调带有订单状态。如果出事故,则需要我们自己用查询订单api来查询订单的状态。
service:
/*** * 查询订单状态 * @param outTradeNo : 客户端自定义订单编号 * @return */ MapqueryPayStatus(String outTradeNo); /*** * 查询订单状态 * @param outTradeNo : 客户端自定义订单编号 * @return */ @Override public Map queryPayStatus(String outTradeNo) { try { //1.封装参数 Map param = new HashMap<>(); param.put("appid",appid); //应用ID param.put("mch_id",partner); //商户号 param.put("out_trade_no",outTradeNo); //商户订单编号 param.put("nonce_str",WXPayUtil.generateNonceStr()); //随机字符 //2、将参数转成xml字符,并携带签名 String paramXml = WXPayUtil.generateSignedXml(param,partnerkey); //3、发送请求 HttpClient httpClient = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery"); httpClient.setHttps(true); httpClient.setXmlParam(paramXml); httpClient.post(); //4、获取返回值,并将返回值转成Map String content = httpClient.getContent(); return WXPayUtil.xmlToMap(content); } catch (Exception e) { e.printStackTrace(); } return null; }
controller:
/*** * 查询支付状态 * @param outTradeNo 自定义订单id,不是微信的id * @return */ @GetMapping(value = "/status/query") public ResponseResult> queryStatus(String outTradeNo){ Map resultMap = weixinPayService.queryPayStatus(outTradeNo); return new ResponseResult >(true,StatusCode.OK,"查询状态成功!",resultMap); }
测试:
官网文档:
用户如果一直未支付,或者取消订单,我们除了要对自定义数据库取消支付状态回滚库存外,还需要向微信服务器请求关闭订单api,让二维码失效(默认2小时失效)。
关闭支付订单,但在关闭之前,需要先关闭微信支付,防止中途用户支付。
修改支付微服务的WeixinPayService,添加关闭支付方法,代码如下:
/*** * 关闭支付 * @param orderId * @return */MapclosePay(Long orderId) throws Exception;
修改WeixinPayServiceImpl,实现关闭微信支付方法,代码如下:
/*** * 关闭微信支付 * @param orderId * @return * @throws Exception */@Overridepublic MapclosePay(Long orderId) throws Exception { //参数设置 Map paramMap = new HashMap (); paramMap.put("appid",appid); //应用ID paramMap.put("mch_id",partner); //商户编号 paramMap.put("nonce_str",WXPayUtil.generateNonceStr());//随机字符 paramMap.put("out_trade_no",String.valueOf(orderId)); //商家的唯一编号 //将Map数据转成XML字符 String xmlParam = WXPayUtil.generateSignedXml(paramMap,partnerkey); //确定url String url = "https://api.mch.weixin.qq.com/pay/closeorder"; //发送请求 HttpClient httpClient = new HttpClient(url); //https httpClient.setHttps(true); //提交参数 httpClient.setXmlParam(xmlParam); //提交 httpClient.post(); //获取返回数据 String content = httpClient.getContent(); //将返回数据解析成Map return WXPayUtil.xmlToMap(content);}
controller:
/*** * 关闭订单状态 * @param outTradeNo 自定义订单id,不是微信订单的id * @return */ @GetMapping(value = "/status/close") public ResponseResult> closePay(String outTradeNo){ Map resultMap = weixinPayService.closePay(outTradeNo); return new ResponseResult >(true,StatusCode.OK,"关闭订单成功!",resultMap); }
支付系统是独立于其他系统的服务,不做相关业务逻辑操作,只做支付处理,所以回调地址接收微信服务返回的支付状态后,立即将消息发送给RabbitMQ,订单系统再监听支付状态数据,根据状态数据做出修改订单状态或者删除订单操作。
支付微服务回调后发送消息
支付微服务集成RabbitMQ,添加依赖:
org.springframework.boot spring-boot-starter-amqp
在后台手动创建队列,并绑定队列(进入rabbitmq主页,手动创建)。如果使用程序创建队列,可以按照如下方式实现。
修改application.yml,配置支付队列和交换机信息,代码如下:
spring: rabbitmq: host: 192.168.169.140 #ip地址 port: 5672 #端口 username: guest #账号 password: guest #密码 #设置order交换机和order队列名称 mq: pay: exchange: order: exchange.order queue: order: queue.order routing: orderkey: queue.order
创建队列以及交换机并让队列和交换机绑定,添加到MQConfig,添加如下代码:
import org.springframework.amqp.core.Binding;import org.springframework.amqp.core.BindingBuilder;import org.springframework.amqp.core.DirectExchange;import org.springframework.amqp.core.Queue;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.env.Environment;import javax.annotation.Resource;import java.util.Objects;/** * Title:rabbitmq配置 * Description:创建队列和交换机 * @author WZQ * @version 1.0.0 * @date 2020/3/13 */@Configurationpublic class MQConfig { //读取配置文件的内容 @Resource private Environment env; /*** * 创建DirectExchange交换机 * @return */ @Bean public DirectExchange basicExchange(){ //true:是否实体化,false:是否自动删除 return new DirectExchange(env.getProperty("mq.pay.exchange.order"), true,false); } /*** * 创建队列 * @return */ @Bean(name = "queueOrder") public Queue queueOrder(){ return new Queue(Objects.requireNonNull(env.getProperty("mq.pay.queue.order")), true); } /**** * 队列绑定到交换机上 * @return */ @Bean public Binding basicBinding(){ return BindingBuilder.bind(queueOrder()).to(basicExchange()).with(env.getProperty("mq.pay.routing.orderkey")); }}
使用:
@Value("${mq.pay.exchange.order}")private String exchange;@Value("${mq.pay.queue.order}")private String queue;@Value("${mq.pay.routing.key}")private String routing;@Autowiredprivate RabbitTemplate rabbitTemplate;/*** * 支付回调 * @param request * @return */@RequestMapping(value = "/notify/url")public String notifyUrl(HttpServletRequest request){ InputStream inStream; try { //读取支付回调数据 inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } outSteam.close(); inStream.close(); // 将支付回调数据转换成xml字符串 String result = new String(outSteam.toByteArray(), "utf-8"); //将xml字符串转换成Map结构 Mapmap = WXPayUtil.xmlToMap(result); //将消息发送给RabbitMQ rabbitTemplate.convertAndSend(exchange,routing, JSON.toJSONString(map)); //响应数据设置 Map respMap = new HashMap(); respMap.put("return_code","SUCCESS"); respMap.put("return_msg","OK"); return WXPayUtil.mapToXml(respMap); } catch (Exception e) { e.printStackTrace(); //记录错误日志 } return null;}
处理订单状态。在订单微服务中,我们需要监听MQ支付状态消息,并实现订单数据操作。
订单微服务集成RabbitMQ,再监听队列消息。
在pom.xml中引入如下依赖:
org.springframework.boot spring-boot-starter-amqp
在application.yml中加入配置,代码如下:
spring: rabbitmq: host: 192.168.169.140 #ip地址 port: 5672 #端口 username: guest #账号 password: guest #密码 #设置队列名称,跟发送消息的队列名字一致mq: pay: queue: order: queue.order
监听消息修改订单
在订单微服务于中创建consumer.OrderPayMessageListener,在该类中consumeMessage方法,用于监听消息,并根据支付状态处理订单,代码如下:
@Component@RabbitListener(queues = { "${mq.pay.queue.order}"})public class OrderPayMessageListener { @Resource private OrderService orderService; /*** * 接收消息 */ @RabbitHandler public void consumeMessage(String msg){ //将数据转成Map Mapresult = JSON.parseObject(msg,Map.class); //return_code=SUCCESS String return_code = result.get("return_code"); //业务结果 String result_code = result.get("result_code"); //状态码 return_code=SUCCESS/FAIL,修改订单状态 if(return_code.equalsIgnoreCase("success") ){ //获取订单号 String outTradeNo = result.get("out_trade_no"); //业务结果,支付成功的 if(result_code.equalsIgnoreCase("success")){ if(outTradeNo!=null){ //修改订单状态根据out_trade_no,具体逻辑业务,看情况 //transaction_id微信支付的订单号,time_end是结算时间 orderService.updateStatus(outTradeNo, result.get("time_end"),result.get("transaction_id")); } }else{ //需要调用微信服务器关闭订单 //订单删除,具体逻辑业务,看情况 orderService.deleteOrder(outTradeNo); } } }}
其它微信api接口,详看:
延时队列即放置在该队列里面的消息是不需要立即消费的,而是等待一段时间之后取出消费。
那么,为什么需要延迟消费呢?我们来看以下的场景:网上商城下订单后30分钟后没有完成支付,取消订单(如:淘宝、去哪儿网);
系统创建了预约之后,需要在预约时间到达前一小时提醒被预约的双方参会; 系统中的业务失败之后,需要重试; 这些场景都非常常见,我们可以思考,比如第二个需求,系统创建了预约之后,需要在预约时间到达前一小时提醒被预约的双方参会。那么一天之中肯定是会有很多个预约的,时间也是不一定的,假设现在有1点 2点 3点 三个预约,如何让系统知道在当前时间等于0点 1点 2点给用户发送信息呢,是不是需要一个轮询,一直去查看所有的预约,比对当前的系统时间和预约提前一小时的时间是否相等呢?这样做非常浪费资源而且轮询的时间间隔不好控制。如果我们使用延时消息队列呢,我们在创建时把需要通知的预约放入消息中间件中,并且设置该消息的过期时间,等过期时间到达时再取出消费即可。Rabbitmq实现延时队列一般而言有两种形式:
第一种方式:利用两个特性: Time To Live(TTL)、Dead Letter Exchanges(DLX)[A队列过期->转发给B队列]第二种方式:利用rabbitmq中的插件x-delay-message
TTL
RabbitMQ可以针对队列设置x-expires(则队列中所有的消息都有相同的过期时间)或者针对Message设置x-message-ttl(对消息进行单独设置,每条消息TTL可以不同),来控制消息的生存时间,如果超时(两者同时设置以最先到期的时间为准),则消息变为dead letter(死信)Dead Letter Exchanges(DLX)
RabbitMQ的Queue可以配置x-dead-letter-exchange和x-dead-letter-routing-key(可选)两个参数,如果队列内出现了dead letter,则按照这两个参数重新路由转发到指定的队列。 x-dead-letter-exchange:出现dead letter之后将dead letter重新发送到指定exchangex-dead-letter-routing-key:出现dead letter之后将dead letter重新按照指定的routing-key发送
还是在订单微服务中,当用户生成订单支付状态后,如果半小时未支付,则取消支付,向微信服务器请求关闭订单api。
#设置交换机和队列名称mq: pay: exchange: order: exchange.order cancelOrder: dlx.exchange queue: order: queue.order cancelOrder: queue.listener cancelOrderDelay: queue.delay routing: orderkey: queue.order cancelOrderkey: queue.listener
创建2个队列,用于接收消息的叫延时队列DelayQueue,用于转发消息的队列叫ListenerQueue,同时创建一个交换机,代码如下:
/** * Title:延时队列配置 * Description:创建2个队列,利用rabbit队列超时机制 * @author WZQ * @version 1.0.0 * @date 2020/3/14 */@Configurationpublic class DelayQueueConfig { //读取配置文件的内容 @Resource private Environment env; /** * 创建queue1,没有被监听,延时队列 * 消息过期了发送给死信队列,死信队列绑定queue2 */ @Bean public Queue orderDelayQueue(){ return QueueBuilder .durable(Objects.requireNonNull(env.getProperty("mq.pay.queue.cancelOrderDelay")) // 延时队列过期的消息会进入给死信队列(没有被读的消息),死信队列的消息就发送到queue2中 // 这里就需要死信队列的交换机和队列,规定是死信路由key的就路由到queue2 .withArgument("x-dead-letter-exchange", env.getProperty("mq.pay.exchange.cancelOrder")) //死信交换机 .withArgument("x-dead-letter-routing-key", Objects.requireNonNull(env.getProperty("mq.pay.queue.cancelOrder")) //交换机绑定路由消息到queue2 .build(); } /** * 创建queue2,普通有监听的队列 */ @Bean public Queue orderListenerQueue(){ return new Queue(Objects.requireNonNull(env.getProperty("mq.pay.queue.cancelOrder"),true); } /** * 创建交换机 */ @Bean public Exchange orderListenerExchange(){ return new DirectExchange(env.getProperty("mq.pay.exchange.cancelOrder")); } /** * 队列queue2绑定交换机 */ @Bean public Binding orderListenerBinding(){ return BindingBuilder.bind(orderListenerQueue()).to(orderListenerExchange()).with(Objects.requireNonNull(env.getProperty("mq.pay.queue.cancelOrderkey")).noargs(); }}
@EnableRabbit
在生成订单,获取支付状态的service中,加入发送消息代码
@Resourceprivate RabbitTemplate rabbitTemplate;//读取配置文件的内容@Resourceprivate Environment env;//传订单id过去rabbitTemplate.convertAndSend(env.getProperty("mq.pay.queue.cancelOrderkey"), (Object) order.getId(), new MessagePostProcessor() { @Override public Message postProcessMessage(Message message) throws AmqpException { //设置延时 message.getMessageProperties().setExpiration("1800000");//30*60*1000,半小时 return message; } });
订单微服务中监听订单支付状态
@Component@RabbitListener(queues = DelayQueueConfig.LISTENER_QUEUE)public class OrderDelayMessageListener { @Autowired private OrderService orderService; @Autowired private WeixinPayFeign weixinPayFeign; /*** * 读取消息,消息是id * 关闭支付,再关闭订单 * @param message */ @RabbitHandler public void consumeMessage(String message){ //@Payload Object message消息是实体类的json数据 //读取消息,消息封装类,自定义,订单号 //MessageStatus messageStatus = JSON.parseObject(message,MessageStatus.class); //查询订单状态,确定未支付 selectById(message); //如果订单未支付 //关闭支付,feign调用支付微服务的关闭订单接口 Result closeResult = weixinPayFeign.closePay(message); MapcloseMap = (Map ) closeResult.getData(); if(closeMap!=null && closeMap.get("return_code").equalsIgnoreCase("success") && closeMap.get("result_code").equalsIgnoreCase("success") ){ //关闭订单 OrderService.closeOrder(username); //库存回滚 //... } }}
转载地址:http://wtiwi.baihongyu.com/