微信支付开发文档:pay.weixin.qq.com/wiki/doc/ap…
微信支付时序图:模式二
- 1.商户后台系统根据用户选购的商品生成订单。
- 2.用户确认支付后调用微信支付【统一下单API】生成预支付交易;
- 3.微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。
- 4.商户后台系统根据返回的code_url生成二维码。
- 5.用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
- 6.微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。
- 7.用户在微信客户端输入密码,确认支付后,微信客户端提交授权。
- 8.微信支付系统根据用户授权完成支付交易。
- 9.微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
- 10.微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
- 11.未收到支付通知的情况,商户后台系统调用【查询订单API】。
- 12.商户确认订单已支付后给用户发货。
相关配置属性介绍
1. appid:微信公众账号或开放平台APP的唯一标识
2. mch_id:商户号 (配置文件中的partner)
3. partnerkey:商户密钥
4. sign:数字签名, 根据微信官方提供的密钥和一套算法生成的一个加密信息, 就是为了保证交易的安全性
1. 引入相关依赖
<!--httpclient支持-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!--微信支付SDK-->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
微信支付SDK相关方法
- 获取随机字符串:
WXPayUtil.generateNonceStr() - MAP转换为XML字符串(自动添加签名):
WXPayUtil.generateSignedXml(param, partnerkey) - XML字符串转换为MAP:
WXPayUtil.xmlToMap(result)
HttpClient工具类
HttpClient工具类代码:
/**
* @Auther: csp1999
* @Date: 2021/01/31/17:04
* @Description: HttpClient g工具类
*/
public class HttpClientUtil {
/**
* 请求url
*/
private String url;
/**
* 请求参数Map集合
*/
private Map<String, String> param;
/**
* 状态码
*/
private int statusCode;
/**
* 请求内容
*/
private String content;
/**
* xml参数
*/
private String xmlParam;
/**
* 是否是https
*/
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() {
// 信任所有
@Override
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;
}
}
测试使用:
/**
* @Auther: csp1999
* @Date: 2021/01/31/17:08
* @Description: HttpClient工具类使用测试
*/
public class HttpClientTest {
public static void main(String[] args) throws IOException {
// 请求地址url
String url = "https://api.mch.weixin.qq.com/pay/orderquery";
// 要发送的xml参数
String xmlParam = "<xml><name>张三</name></xml>";
// 实例化HttpClientUtil
HttpClientUtil client=new HttpClientUtil(url);
client.setHttps(true);// 是否是https协议
client.setXmlParam(xmlParam);// 发送的xml数据
client.post();// 执行post请求
String result = client.getContent();// 获取响应结果
System.out.println(result);
}
}
测试结果:
2. 支付微服务搭建
创建changgou-service-pay 微服务工程
application.yml
创建application.yml,配置文件如下:
server:
port: 8092
spring:
application:
name: changgou-pay
main:
allow-bean-definition-overriding: true
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:7001/eureka
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
# hystrix 配置
hystrix:
command:
default:
execution:
timeout:
# 如果enabled设置为false,则请求超时交给ribbon控制
enabled: true
isolation:
strategy: SEMAPHORE
# 微信支付信息配置
weixin:
appid: wx8397f8696b538317 # 微信公众账号或开放平台APP的唯一标识
partner: 1473426802 # 财付通平台的商户账号
partnerkey: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb # 财付通平台的商户密钥
notifyurl: http://www.itcast.cn # 回调地址
启动类
在changgou-service-pay中创建com.changgou.WeixinPayApplication,代码如下:
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@EnableEurekaClient
public class WeixinPayApplication {
public static void main(String[] args) {
SpringApplication.run(WeixinPayApplication.class,args);
}
}
3. 微信支付二维码生成
需求分析与实现思路
在支付页面上生成支付二维码,并显示订单号和金额,用户拿出手机,打开微信扫描页面上的二维码,然后在微信中完成支付:
实现思路
- 通过HttpClient工具类实现对远程支付接口的调用。接口链接:api.mch.weixin.qq.com/pay/unified…
- 具体参数参见“统一下单”API pay.weixin.qq.com/wiki/doc/ap…, 构建参数发送给统一下单的url ,返回的信息中有支付url,根据url生成二维码,显示的订单号和金额也在返回的信息中
代码实现
(1)业务层
新增com.changgou.service.WeixinPayService接口,代码如下:
/**
* @Auther: csp1999
* @Date: 2021/01/31/18:33
* @Description: 微信支付service接口
*/
public interface WeixinPayService {
/**
* 创建二维码
* <p>
* out_trade_no : 客户端自定义订单编号
* total_fee : 交易金额,单位:分
*
* @return
*/
public Map<String,String> createNative(Map<String, String> paramMap);
}
创建com.changgou.service.impl.WeixinPayServiceImpl类,并发送Post请求获取预支付信息,包含二维码扫码支付地址。代码如下:
/**
* @Auther: csp1999
* @Date: 2021/01/31/18:33
* @Description: 微信支付service接口实现类
*/
@Service
public 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;
/**
* 创建二维码
* <p>
* out_trade_no : 客户端自定义订单编号
* total_fee : 交易金额,单位:分
*
* @return
*/
@Override
public Map<String, String> createNative(Map<String, String> paramMap) {
try {
// 1、封装参数
Map<String, String> param = new HashMap();
param.put("appid", appid); // 应用appID
param.put("mch_id", partner); // 商户ID号
param.put("nonce_str", WXPayUtil.generateNonceStr()); // 随机数
param.put("body", "畅购"); // 订单描述
param.put("out_trade_no", paramMap.get("out_trade_no")); // 商户订单号
param.put("total_fee", paramMap.get("total_fee")); // 交易金额
param.put("spbill_create_ip", "127.0.0.1"); // 终端IP
param.put("notify_url", notifyurl); // 回调地址
param.put("trade_type", "NATIVE"); // 交易类型
// 2、将参数转成xml字符,并携带签名
String paramXml = WXPayUtil.generateSignedXml(param, partnerkey);
// 3、执行请求
HttpClientUtil httpClient = new HttpClientUtil("https://api.mch.weixin.qq.com/pay/unifiedorder");
httpClient.setHttps(true);
httpClient.setXmlParam(paramXml);
httpClient.post();
// 4、获取参数
String content = httpClient.getContent();
Map<String, String> stringMap = WXPayUtil.xmlToMap(content);
System.out.println("stringMap:" + stringMap);
// 5、获取部分页面所需参数
Map<String, String> dataMap = new HashMap<String, String>();
dataMap.put("code_url", stringMap.get("code_url"));
dataMap.put("out_trade_no", paramMap.get("out_trade_no"));
dataMap.put("total_fee", paramMap.get("total_fee"));
return dataMap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
(2) 控制层
创建com.changgou.controller.WeixinPayController,主要调用WeixinPayService的方法获取创建二维码的信息,代码如下:
/**
* @Auther: csp1999
* @Date: 2021/01/31/19:26
* @Description: 微信支付controller
*/
@CrossOrigin
@RestController
@RequestMapping(value = "/weixin/pay")
public class WeixinPayController {
@Autowired
private WeixinPayService weixinPayService;
/**
* 创建下单支付的二维码
*
* @return
*/
@RequestMapping(value = "/create/native")
public Result createNative(@RequestParam Map<String, String> paramMap) {
Map<String, String> resultMap = weixinPayService.createNative(paramMap);
return new Result(true, StatusCode.OK, "创建二维码预付订单成功!", resultMap);
}
}
这里我们订单号通过随机数生成,金额暂时写死,后续开发我们再对接业务系统得到订单号和金额。
(3)测试
访问http://localhost:8092/weixin/pay/create/native?out_trade_no=No055550001&total_fee=1
打开支付页面 pay.html,修改value路径,然后打开,会出现二维码,可以扫码测试:
<html>
<head>
<title>二维码入门小demo</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=MRD74tfzz'//指定二维码图片代表的真正的值
})
</script>
</html>
扫码测试:
4. 检测支付状态
需求分析
当用户支付成功后跳转到成功页面:
当用户支付成功后跳转到成功页面:
实现思路
- 通过HttpClient工具类实现对远程支付接口的调用。接口链接:api.mch.weixin.qq.com/pay/orderqu…
- 具体参数参见“查询订单”API pay.weixin.qq.com/wiki/doc/ap…, 我们在controller方法中轮询调用查询订单(间隔3秒),当返回状态为success时,我们会在controller方法返回结果。前端代码收到结果后跳转到成功页面。
代码实现
(1)业务层
修改com.changgou.service.WeixinPayService,新增方法定义:
/**
* 查询订单状态
*
* @param out_trade_no : 客户端自定义订单编号
* @return
*/
public Map<String,String> queryPayStatus(String out_trade_no);
在com.changgou.pay.service.impl.WeixinPayServiceImpl中增加实现方法:
/**
* 查询订单状态
*
* @param out_trade_no: 客户端自定义订单编号
* @return
*/
@Override
public Map<String,String> queryPayStatus(String out_trade_no) {
try {
// 1.封装参数
Map param = new HashMap();
param.put("appid", appid); // 应用appID
param.put("mch_id", partner); // 商户号
param.put("out_trade_no", out_trade_no); // 商户订单编号
param.put("nonce_str", WXPayUtil.generateNonceStr()); // 随机字符
// 2、将参数转成xml字符,并携带签名
String paramXml = WXPayUtil.generateSignedXml(param, partnerkey);
// 3、发送请求
HttpClientUtil httpClient = new HttpClientUtil("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;
}
(2)控制层
在com.changgou.controller.WeixinPayController新增方法,用于查询支付状态,代码如下:
/**
* 查询支付状态
*
* @param out_trade_no
* @return
*/
@GetMapping(value = "/status/query")
public Result queryStatus(String out_trade_no) {
Map<String, String> resultMap = weixinPayService.queryPayStatus(out_trade_no);
return new Result(true, StatusCode.OK, "查询状态成功!", resultMap);
}
(3)测试
访问:http://localhost:8092/weixin/pay/status/query?out_trade_no=No055550001
5. 支付信息回调
每次实现支付之后,微信支付都会将用户支付结果返回到指定路径,而指定路径是指创建二维码的时候填写的notifyurl参数,响应的数据以及相关文档参考一下地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8
返回参数分析
通知参数如下:
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 返回状态码 | 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 | 请按示例值填写 |
举例如下:
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>
回调接收数据实现
修改changgou-service-pay微服务的com.changgou.pay.controller.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结构
Map<String, String> map = WXPayUtil.xmlToMap(result);
System.out.println(map);
// 响应数据设置
Map<String,String> respMap = new HashMap();
respMap.put("return_code", "SUCCESS");
respMap.put("return_msg", "OK");
return WXPayUtil.mapToXml(respMap);
} catch (Exception e) {
e.printStackTrace();
// 记录错误日志
}
return null;
}
当用户扫码支付时,会触发回调!
application.yml中修改支付回调地址:
6.MQ处理支付回调状态
业务分析
支付系统是独立于其他系统的服务,不做相关业务逻辑操作,只做支付处理,所以回调地址接收微信服务返回的支付状态后,立即将消息发送给RabbitMQ,订单系统再监听支付状态数据,根据状态数据做出修改订单状态或者删除订单操作。
集成RabbitMQ
修改支付微服务,集成RabbitMQ,添加如下依赖:
<!--加入ampq-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
在后台手动创建队列,并绑定队列。如果使用程序创建队列,可以按照如下方式实现。
修改application.yml,配置支付队列和交换机信息,代码如下:
spring:
application:
name: changgou-pay
main:
allow-bean-definition-overriding: true
# rabbitmq相关配置:
rabbitmq:
host: 8.xxx.xx.136
port: 5672
username: guest
password: guest
# 位置支付交换机和队列
mq:
pay:
exchange:
order: exchange.order
queue:
order: queue.order
routing:
key: queue.order
创建队列以及交换机并让队列和交换机绑定,修改com.changgou.WeixinPayApplication,添加如下代码:
/**
* @Auther: csp1999
* @Date: 2021/02/01/12:31
* @Description: RabbitMq配置类
*/
@Configuration
public class RabbitMqConfig {
/**
* 读取配置文件中的信息对象
*/
@Autowired
private Environment environment;
/**
* 创建队列
*
* @return
*/
@Bean
public Queue orderQueue() {
return new Queue(environment.getProperty("mq.pay.queue.order"), true);
}
/**
* 创建交换机
*
* @return
*/
@Bean
public Exchange orderExchange() {
return new DirectExchange(environment.getProperty("mq.pay.exchange.order"), true, false);
}
/**
* 队列兵丁交换机
*
* @param orderQueue
* @param orderExchange
* @return
*/
@Bean
public Binding orderQueueExchange(Queue orderQueue, Exchange orderExchange) {
return BindingBuilder
.bind(orderQueue)
.to(orderExchange)
.with(environment.getProperty("mq.pay.routing.key"))
.noargs();
}
}
发送MQ消息
修改WeixinPayController中的回调方法,在接到支付信息后,立即将支付信息发送给RabbitMQ,如下:
代码如下:
/**
* @Auther: csp1999
* @Date: 2021/01/31/19:26
* @Description: 微信支付controller
*/
@CrossOrigin
@RestController
@RequestMapping(value = "/weixin/pay")
public class WeixinPayController {
@Autowired
private WeixinPayService weixinPayService;
@Autowired
private 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结构
Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
System.out.println(resultMap);
/**
* 发送支付结果给mq
*/
rabbitTemplate.convertAndSend("exchange.order","queue.order",
JSON.toJSONString(resultMap));
// 响应数据设置
Map<String,String> 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消息处理订单
在订单微服务中,我们需要监听MQ支付状态消息,并实现订单数据操作。
在订单微服务中,先集成RabbitMQ,再监听队列消息。
在pom.xml中引入如下依赖:
<!--加入ampq-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
在application.yml中配置rabbitmq配置,代码如下:
spring:
application:
name: changgou-pay
main:
allow-bean-definition-overriding: true
# rabbitmq相关配置:
rabbitmq:
host: 8.xxx.xx.136
port: 5672
username: guest
password: guest
# 位置支付交换机和队列
mq:
pay:
exchange:
order: exchange.order
queue:
order: queue.order
routing:
key: queue.order
OrderMessageListener监听组件:
/**
* @Auther: csp1999
* @Date: 2021/02/01/12:57
* @Description: 订单消息监听
*/
@Component
@RabbitListener(queues = {"${mq.pay.queue.order}"})
public class OrderMessageListener {
@Autowired
private OrderService orderService;
/**
* 支付结果监听处理
*
* @param message
*/
@RabbitHandler
public void getMessage(String message) {
// 支付结果
Map<String, String> resultMap = JSON.parseObject(message, Map.class);
System.out.println("监听到的消息:" + resultMap);
// 通信标识 return_code
String return_code = resultMap.get("return_code");
if (return_code.equals("SUCCESS")) {
// 支付业务结果 result_code
String result_code = resultMap.get("result_code");
// 订单号 out_trade_no
String out_trade_no = resultMap.get("out_trade_no");
// 支付时间 time_end
String time_end = resultMap.get("time_end");
// 支付成功,修改订单状态
if (result_code.equals("SUCCESS")) {
// 微信支付交易流水号 transaction_id
String transaction_id = resultMap.get("transaction_id");
// 修改订单状态
orderService.updateOrderStatus(out_trade_no,time_end,transaction_id);
} else {
// 支付失败,关闭支付,取消订单,回滚库存
// 删除订单
orderService.deleteOrder(out_trade_no);
}
}
}
}
OrderService 中新增如下代码:
/***
* 删除订单,回滚库存
* @param out_trade_no
*/
void deleteOrder(String out_trade_no);
/***
* 修改订单状态
* 1.修改支付时间
* 2.修改支付状态
* @param out_trade_no
* @param pay_time
* @param transaction_id
*/
void updateOrderStatus(String out_trade_no,String pay_time,String transaction_id);
OrderServiceImpl 中新增如下代码:
/***
* 删除订单
* @param out_trade_no
*/
@Override
public void deleteOrder(String out_trade_no) {
Order order = orderMapper.selectByPrimaryKey(out_trade_no);
// 修改订单状态(假删除)
order.setUpdateTime(new Date());
order.setPayStatus("2");// 支付失败
orderMapper.updateByPrimaryKeySelective(order);
// 回滚库存 -> 调用goods微服务
}
/***
* 修改订单状态
* 1.修改支付时间
* 2.修改支付状态
* @param out_trade_no 订单号
* @param pay_time 支付时间 格式:20210109
* @param transaction_id 订单流水号
*/
@Override
public void updateOrderStatus(String out_trade_no, String pay_time, String transaction_id) {
// 时间格式转换
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
Date payTime = null;
try {
// string类型:20210109 -> date类型
payTime = sdf.parse(pay_time);
} catch (ParseException e) {
e.printStackTrace();
}
// 先查询订单信息
Order order = orderMapper.selectByPrimaryKey(out_trade_no);
// 再修改订单信息
order.setPayTime(payTime);// 支付时间
order.setTransactionId(transaction_id);// 修改流水号
order.setPayType("1");// 修改支付状态:1-已支付
orderMapper.updateByPrimaryKeySelective(order);
}
7. 延时队列(扩展)
QueueConfig类
在changgou-service-order微服务的config包下添加QueueConfig类:
/**
* @Auther: csp1999
* @Date: 2021/02/01/15:31
* @Description: 延时队列配置
*/
public class QueueConfig {
/***
* 创建Queue1:延时队列,会过期,过期后将数据发给Queue2
*/
@Bean
public Queue orderDelayQueue() {
return QueueBuilder
.durable("orderDelayQueue")
// 死信:过了一定时间仍未被读取的消息(即,被放弃读取的消息)
.withArgument("x-dead-letter-exchange", "orderListenerExchange")// 死信交换机
.withArgument("x-dead-letter-routing-key", "orderListenerQueue")
.build();
}
/***
* 创建Queue2
*/
@Bean
public Queue orderListenerQueue() {
return new Queue("orderListenerQueue", true);
}
/***
* 创建交换机
*/
@Bean
public Exchange orderListenerExchange() {
return new DirectExchange("orderListenerExchange");
}
/***
* 队列Queue2绑定交换机
*/
@Bean
public Binding orderListenerBinding(Queue orderListenerQueue, Exchange orderListenerExchange) {
return BindingBuilder
.bind(orderListenerQueue)
.to(orderListenerExchange)
.with("orderListenerQueue")
.noargs();
}
}
添加订单方法中增加mq消息发送
OrderServiceImpl类中的add方法添加如下代码:
/**
* 增加Order订单
*
* @param order
*/
@Override
public int add(Order order) {
......
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("创建订单时间:"+sdf.format(new Date()));
// 添加订单消息发送
rabbitTemplate.convertAndSend("orderDelayQueue", (Object) order.getId(), new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
// 设置延时20s读取
message.getMessageProperties().setExpiration("20000");
return message;
}
});
...
}
添加过期消息监听
新建DelayMessageListener配置类
/**
* @Auther: csp1999
* @Date: 2021/02/01/15:58
* @Description: 过期消息监听配置类
*/
@Component
@RabbitListener(queues = "orderListenerQueue")
public class DelayMessageListener {
/**
* 延时队列监听
*
* @param message
*/
@RabbitHandler
public void getDelayMessage(String message) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("监听消息的时间:" + sdf.format(new Date()));
System.out.println(",监听到的消息内容:" + message);
}
}