微信二维码支付
一、创建微服务1、导入依赖<!--微信支付--><dependency><groupId>com.github.wxpay</groupId><artifactId>wxpay-sdk</artifactId><version>0.0.3</version></dependency><
一、创建微服务
1、导入依赖
<!--微信支付-->
<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>
2、需要的工具类
HttpClient:http/https相关操作
public class HttpClient {
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 HttpClient(String url, Map<String, String> param) {
this.url = url;
this.param = param;
}
public HttpClient(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;
}
}
3、配置文件:application.yml
server:
port: 18090
spring:
application:
name: 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:
#应用ID
appid: wx8397f8696b538317
#商户号
partner: 1473426802
#密钥
partnerkey: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
#支付回调地址 通知地址
notifyurl: https://www.cnblogs.com/chawaner/
4、SpringBoot启动类
/**
* @Author TeaBowl
* @Date 2021/2/1 10:47
* @Version 1.0
*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableEurekaClient
public class WeiXinPayApplication {
public static void main(String[] args) {
SpringApplication.run(WeiXinPayApplication.class,args);
}
}
5、控制层
/**
* @Author TeaBowl
* @Date 2021/2/1 12:01
* @Version 1.0
*/
@RestController
@RequestMapping(value = "/weixin/pay")
public class WeiXinPayController {
@Autowired
private WeixinPayService weixinPayService;
/**
* 创建二维码
* @param parameterMap:用户订单信息
* @return
*/
@RequestMapping(value = "/create/native")
public Result createNative(@RequestParam Map<String,String> parameterMap){
Map<String,String> resultMap = weixinPayService.createnative(parameterMap);
return new Result(true, StatusCode.OK,"创建二维码预付订单成功!",resultMap);
}
}
6、应用层
/**
* @Author TeaBowl
* @Date 2021/2/1 11:01
* @Version 1.0
*/
public interface WeixinPayService {
/**
* 创建二维码
* @param parameterMap:用户订单信息
* @return
*/
Map createnative(Map<String,String> parameterMap);
}
/**
* @Author TeaBowl
* @Date 2021/2/1 11:00
* @Version 1.0
*/
@Service
public class WeixinPayServiceImpl implements WeixinPayService {
//应用ID
@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 Map createnative(Map<String, String> parameterMap) {
try {
//远程调用
//创建一个Map集合存放请求参数
Map<String, String> paramMap = new HashMap<>();
//添加数据
//应用ID
paramMap.put("appid", appid);
//商户号
paramMap.put("mch_id", partner);
//随机字符串
paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
//商品描述
paramMap.put("body", "茶碗儿购物是真的好");
//订单号
paramMap.put("out_trade_no", parameterMap.get("outtradeno"));
//交易总金额,单位为:分
paramMap.put("total_fee", parameterMap.get("totalfee"));
//终端IP
paramMap.put("spbill_create_ip", "127.0.0.1");
//通知地址,添URL地址
paramMap.put("notify_url", notifyurl);
//交易类型,NATIVE
paramMap.put("trade_type", "NATIVE");
//签名
//paramMap.put("sign","");
//Map转为XML数据,可以自带签名
//将请求参数集合,转为带有签名的XML数据格式
String xmlStr = WXPayUtil.generateSignedXml(paramMap, partnerkey);
//URL地址,微信支付接口链接
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//提交方式:HTTPS
//创建HttpClient对象,对url请求地址进行操作
HttpClient httpClient = new HttpClient(url);
//设置提交方式为HTTPS
httpClient.setHttps(true);
//提交参数,参数以XML数据格式提交
httpClient.setXmlParam(xmlStr);
//执行请求
//发送XML数据,使用post请求
httpClient.post();
//获取返回的数据,此时返回的数据为XML数据格式
String content = httpClient.getContent();
System.out.println("content:" + content);
//返回数据转成Map
Map<String, String> resultMap = WXPayUtil.xmlToMap(content);
return resultMap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
二、测试
1、浏览器输入请求地址:http://localhost:18090/weixin/pay/create/native?outtradeno=198999&totalfee=101
2、参数解释:
outtradeno:订单号
totalfee:总金额
3、浏览器请求结果
{
"flag": true,
"code": 20000,
"message": "创建二维码预付订单成功!",
"data": {
"nonce_str": "pUPI4MeBq1ikJbOJ",
"code_url": "weixin://wxpay/bizpayurl?pr=qJVBnYYzz",
"appid": "wx8397f8696b538317",
"sign": "7BCED5501AD892D5490A109DADE3383F",
"trade_type": "NATIVE",
"return_msg": "OK",
"result_code": "SUCCESS",
"mch_id": "1473426802",
"return_code": "SUCCESS",
"prepay_id": "wx011403161517455f3f2880f7e02e920000"
}
}
三、生成二维码
1、使用qrious.js写一个页面pay.html,用于生成二维码
<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=qJVBnYYzz'//指定二维码图片代表的真正的值
})
</script>
</html>
2、打开pay.html,显示一个二维码,微信扫码支付
四、查询支付状态
1、控制层添加方法(controller)
/**
* 微信支付状态查询
* @param outtradeno:商户订单号,由微信服务器生成
* @return
*/
@GetMapping(value = "/status/query")
public Result queryStatus(String outtradeno){
//查询微信支付状态
Map map = weixinPayService.queryStatus(outtradeno);
return new Result(true, StatusCode.OK,"微信支付状态查询成功!",map);
}
2、应用层添加方法(service、serviceImpl)
/**
* 查询微信支付状态
* @param outtradeno:商户订单号,由微信服务器生成
* @return
*/
Map queryStatus(String outtradeno);
/**
* 查询微信支付状态
* @param outtradeno:商户订单号,由微信服务器生成
* @return
*/
@Override
public Map queryStatus(String outtradeno) {
try {
//远程调用
//创建一个Map集合存放请求参数
Map<String, String> paramMap = new HashMap<>();
//添加数据
//应用ID
paramMap.put("appid", appid);
//商户号
paramMap.put("mch_id", partner);
//随机字符串
paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
//订单号
paramMap.put("out_trade_no", outtradeno);
//签名
//paramMap.put("sign","");
//Map转为XML数据,可以自带签名
//将请求参数集合,转为带有签名的XML数据格式
String xmlStr = WXPayUtil.generateSignedXml(paramMap, partnerkey);
//URL地址,微信查询订单接口链接
String url = "https://api.mch.weixin.qq.com/pay/orderquery";
//提交方式:HTTPS
//创建HttpClient对象,对url请求地址进行操作
HttpClient httpClient = new HttpClient(url);
//设置提交方式为HTTPS
httpClient.setHttps(true);
//提交参数,参数以XML数据格式提交
httpClient.setXmlParam(xmlStr);
//执行请求
//发送XML数据,使用post请求
httpClient.post();
//获取返回的数据,此时返回的数据为XML数据格式
String content = httpClient.getContent();
//System.out.println("content:" + content);
//返回数据转成Map
Map<String, String> resultMap = WXPayUtil.xmlToMap(content);
return resultMap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
3、浏览器输入请求地址:http://localhost:18090/weixin/pay/status/query?outtradeno=198999
参数解释:outtradeno:订单号
{
"flag": true,
"code": 20000,
"message": "微信支付状态查询成功!",
"data": {
"nonce_str": "9FMEOJb0nhdJzGOQ",
"device_info": "",
"out_trade_no": "198999",
"trade_state": "NOTPAY",
"appid": "wx8397f8696b538317",
"total_fee": "101",
"sign": "1C86209F252D64254542EFC5481FD0D0",
"trade_state_desc": "订单未支付",
"return_msg": "OK",
"result_code": "SUCCESS",
"mch_id": "1473426802",
"return_code": "SUCCESS"
}
}
因为上面测试支付二维码时候,我没支付,所以查询订单支付状态显示“订单未支付”。
五、支付结果回调
1、控制层添加方法
/**
* 支付结果通知回调方法
* @param request
* @return
*/
@RequestMapping(value = "/notify/url")
public String notifyurl(HttpServletRequest request) throws Exception {
//获取网络输入流,也就是网络输入流格式的通知结果
ServletInputStream inputStream = request.getInputStream();
//创建一个OutputStream输出流->输入文件
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//定义缓冲区
byte[] buffer = new byte[1024];
//初始化一个数据长度
int len = 0;
//往缓冲区里读文件,数据长度 不等于-1说明有数据
while ((len = inputStream.read(buffer))!=-1){
//缓冲区中的数据 写入到 输出流对象中
//从0开始读到长度最后一位
byteArrayOutputStream.write(buffer,0,len);
}
//将byteArrayOutputStream字节流转为字节数组
//这就是微信支付结果的字节数组
byte[] byteArray = byteArrayOutputStream.toByteArray();
//字节数组 转为 xml字符串
String xmlResult = new String(byteArray, "Utf-8");
System.out.println("xmlResult:\n"+xmlResult);
//xml字符串 转为 Map
Map<String, String> resultMap = WXPayUtil.xmlToMap(xmlResult);
System.out.println("resultMap:\n"+resultMap);
//返回结果
String result = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
return result;
}
2、配置文件中修改回调地址为动态调用
#微信支付信息配置
weixin:
#应用ID
appid: wx8397f8696b538317
#商户号
partner: 1473426802
#密钥
partnerkey: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
#支付回调地址 通知地址
#外网域名:http://19453k43d4.51vip.biz:32375
notifyurl: http://19453k43d4.51vip.biz:32375/weixin/pay/notify/url
3、创建二维码
浏览器输入请求:http://19453k43d4.51vip.biz:32375/weixin/pay//create/native?outtradeno=1999&totalfee=1
{
"flag": true,
"code": 20000,
"message": "创建二维码预付订单成功!",
"data": {
"nonce_str": "pUPI4MeBq1ikJbOJ",
"code_url": "weixin://wxpay/bizpayurl?pr=qJVBnYYzd",
"appid": "wx8397f8696b538317",
"sign": "7BCED5501AD892D5490A109DADE3383F",
"trade_type": "NATIVE",
"return_msg": "OK",
"result_code": "SUCCESS",
"mch_id": "1473426802",
"return_code": "SUCCESS",
"prepay_id": "wx011403161517455f3f2880f7e02e920000"
}
}
4、修改pay.html中的value为上步的code_url
<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=qJVBnYYzd'//指定二维码图片代表的真正的值
})
</script>
</html>
5、双击打开pay.html,生成一个二维码,扫码支付后;
浏览器输入请求地址:http://19453k43d4.51vip.biz:32375/weixin/pay/status/query?outtradeno=1999
{
"flag": true,
"code": 20000,
"message": "微信支付状态查询成功!",
"data": {
"transaction_id": "4200000517202002187004756359",
"nonce_str": "ZxVmHMCc1mVr8rxR",
"trade_state": "SUCCESS",
"bank_type": "OTHERS",
"openid": "oNpSGwaqHv74waDX0BLPNrFiYIUo",
"sign": "3DC6A5346C914DE745DBBB7796039BC1",
"return_msg": "OK",
"fee_type": "CNY",
"mch_id": "1473426802",
"cash_fee": "1",
"out_trade_no": "1999",
"cash_fee_type": "CNY",
"appid": "wx8397f8696b538317",
"total_fee": "1",
"trade_state_desc": "支付成功",
"trade_type": "NATIVE",
"result_code": "SUCCESS",
"attach": "",
"time_end": "20200218153715",
"is_subscribe": "N",
"return_code": "SUCCESS"
}
}
6、查看控制台输出,有一个支付结果通知
六、MQ消息中间件监听
微信服务返回的支付状态,发送给MQ消息中间件
1、在支付系统中导入依赖
<!--加入ampq-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2、配置文件application.yml中添加信息
rabbitmq:
host: 服务器地址
port: 端口
username: 用户名
password: 密码
#位置支付交换机和队列
mq:
pay:
exchange:
order: exchange.order
queue:
order: queue.order
routing:
key: queue.order
实际开发中登录“服务器地址:15672”手动创建交换机和队列
3、创建队列绑定交换机配置
/**
* @Author TeaBowl
* @Date 2021/2/3 11:26
* @Version 1.0
*/
@Configuration
public class MQConfig {
/**
* 读取配置文件中的信息
*/
@Autowired
private Environment env;
/**
* 创建队列
* @return
*/
@Bean
public Queue OrderQueue (){
//参数:队列名
return new Queue(env.getProperty("mq.pay.queue.order"));
}
/**
* 创建交换机
* @return
*/
@Bean
public Exchange OrderExchange (){
//参数:交换机名、是否持久化、是否自动删除
return new DirectExchange(env.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(env.getProperty("mq.pay.routing.key")).noargs();
}
}
4、controller中注入MQ操作对象
//注入MQ操作对象
@Autowired
private RabbitTemplate rabbitTemplate;
5、修改controller中支付结果通知回调方法
/**
* 支付结果通知回调方法
* @param request
* @return
*/
@RequestMapping(value = "/notify/url")
public String notifyurl(HttpServletRequest request) throws Exception {
//获取网络输入流,也就是网络输入流格式的通知结果
ServletInputStream inputStream = request.getInputStream();
//创建一个OutputStream输出流->输入文件
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//定义缓冲区
byte[] buffer = new byte[1024];
//初始化一个数据长度
int len = 0;
//往缓冲区里读文件,数据长度 不等于-1说明有数据
while ((len = inputStream.read(buffer))!=-1){
//缓冲区中的数据 写入到 输出流对象中
//从0开始读到长度最后一位
byteArrayOutputStream.write(buffer,0,len);
}
//将byteArrayOutputStream字节流转为字节数组
//这就是微信支付结果的字节数组
byte[] byteArray = byteArrayOutputStream.toByteArray();
//字节数组 转为 xml字符串
String xmlResult = new String(byteArray, "Utf-8");
System.out.println("xmlResult:\n"+xmlResult);
//xml字符串 转为 Map
Map<String, String> resultMap = WXPayUtil.xmlToMap(xmlResult);
System.out.println("resultMap:\n"+resultMap);
//发送支付结果给MQ
rabbitTemplate.convertAndSend("exchange.order","queue.order", JSON.toJSONString(resultMap));
//返回结果
String result = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
return result;
}
订单系统监听MQ消息
1、在订单系统中导入依赖
<!--加入ampq-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2、配置文件application.yml中添加信息
rabbitmq:
host: 服务器地址
port: 端口
username: 用户名
password: 密码
#位置支付交换机和队列
mq:
pay:
#交换机
exchange:
order: exchange.order
#队列
queue:
order: queue.order
routing:
key: queue.order
3、创建监听MQ信息配置
/**
* @Author TeaBowl
* @Date 2021/2/3 12:02
* @Version 1.0
* 监听MQ信息
*/
@Component
@RabbitListener(queues = "${mq.pay.queue.order}") //监听队列
public class OrderMessageListener {
/**
* 支付结果监听
* @param message:支付结果
*/
@RabbitHandler
public void getMeaaage(String message){
//支付结果回调通知,Json格式转为Map
Map<String, String> resultMap = JSON.parseObject(message, Map.class);
System.out.println("监听到的支付结果信息:\n"+resultMap);
//从MQ消息中间件中获取 支付操作的通信状态
//通信标识:return_code 状态:SUCCESS/FAIL
String return_code = resultMap.get("return_code");
//如果支付操作的通信成功
if (return_code.equals("SUCCESS")){
//从MQ消息中间件中获取 支付操作的业务结果
//业务结果:result_code 状态:SUCCESS/FAIL
String result_code = resultMap.get("result_code");
//商户订单号:out_trade_no
String out_trade_no = resultMap.get("out_trade_no");
//如果支付操作的业务结果为成功 修改订单状态
if (result_code.equals("SUCCESS")){
//从MQ消息中间件中获取信息
//微信支付订单号:transaction_id
String transaction_id = resultMap.get("transaction_id");
}else {
//如果支付失败,关闭支付,取消订单,回滚库存
}
}
}
}
4、创建二维码实
a. 浏览器请求地址:http://19453k43d4.51vip.biz:32375/weixin/pay/create/native?outtradeno=1769&totalfee=1
{
"flag": true,
"code": 20000,
"message": "创建二维码预付订单成功!",
"data": {
"nonce_str": "QlLrzt4vdsKNck1d",
"code_url": "weixin://wxpay/bizpayurl?pr=MjNPE2Dzz",
"appid": "wx8397f8696b538317",
"sign": "CD80E7E68014CF70C36535E231E0FF14",
"trade_type": "NATIVE",
"return_msg": "OK",
"result_code": "SUCCESS",
"mch_id": "1473426802",
"return_code": "SUCCESS",
"prepay_id": "wx03125509016851a5fca270399354320000"
}
}
b. 修改pay.html中的支付地址为code_url地址
<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=MjNPE2Dzz'//指定二维码图片代表的真正的值
})
</script>
</html>
c. 二维码图片
5、扫码支付后。查看浏览器RabbitMQ后台:“服务器地址:15672/#/queues”
查看消息:
七、修改订单状态
1、在订单工程中进行修改
应用层接口(OrderService)中,添加接口
/**
* 修改订单状态
* @param outtradeno:用户订单号
* @param paytime:支付时间
* @param transactionid:交易流水号
*/
void updataStatus(String outtradeno,String paytime,String transactionid) throws Exception;
应用层接口实现类(OrderServiceImpl)中,添加方法实现
/**
* 修改订单状态
* @param outtradeno:用户订单号
* @param paytime:支付时间
* @param transactionid:交易流水号
*/
@Override
public void updataStatus(String outtradeno, String paytime, String transactionid) throws Exception {
//使用时间转换工具,转换时间格式
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
Date payTimeInfo = simpleDateFormat.parse(paytime);
//根据用户订单号,查询订单 order订单信息封装类
Order order = orderMapper.selectByPrimaryKey(outtradeno);
//修改订单信息
//设置交易时间
order.setPayTime(payTimeInfo);
//设置支付状态:0未支付,1已支付,2支付失败
order.setPayStatus("1");
//设置交易流水号
order.setTransactionId(transactionid);
//信息更新到数据库订单表中
orderMapper.updateByPrimaryKeySelective(order);
}
2、删除订单,回滚库存
在订单支付后,如果支付失败,订单数据表中的订单信息就没必要保留了,商品库存也需要回滚到原来的数量;
为了方便二次查询,订单并不是真的删除了,而是修改了状态,这叫逻辑删除。
应用层接口(OrderService)中,添加接口
/**
* 支付失败,删除[修改状态]订单,回滚库存
* @param outtradeno:用户订单号
*/
void deleteOrder(String outtradeno);
应用层接口实现类(OrderServiceImpl)中,添加方法实现
/**
* 支付失败,删除[修改状态]订单,回滚库存
* @param outtradeno:用户订单号
*/
@Override
public void deleteOrder(String outtradeno) {
//根据用户订单号,查询订单 order订单信息封装类
Order order = orderMapper.selectByPrimaryKey(outtradeno);
//修改状态
//设置更新时间
order.setUpdateTime(new Date());
//设置支付状态:0未支付,1已支付,2支付失败
order.setPayStatus("2");
//信息更新到数据库订单表中
orderMapper.updateByPrimaryKeySelective(order);
//回滚库存->调用商品微服务 暂略
}
3、对接监听
修改MQ信息监听配置OrderMessageListener
注入订单应用的操作对象
@Autowired
private OrderService orderService;
修改支付结果监听方法
/**
* 支付结果监听
* @param message:支付结果
*/
@RabbitHandler
public void getMeaaage(String message) throws Exception {
//支付结果回调通知,Json格式转为Map
Map<String, String> resultMap = JSON.parseObject(message, Map.class);
System.out.println("监听到的支付结果信息:\n"+resultMap);
//从MQ消息中间件中获取 支付操作的通信状态
//通信标识:return_code 状态:SUCCESS/FAIL
String return_code = resultMap.get("return_code");
//如果支付操作的通信成功
if (return_code.equals("SUCCESS")){
//从MQ消息中间件中获取 支付操作的业务结果
//业务结果:result_code 状态:SUCCESS/FAIL
String result_code = resultMap.get("result_code");
//商户订单号:out_trade_no
String out_trade_no = resultMap.get("out_trade_no");
//如果支付操作的业务结果为成功 修改订单状态
if (result_code.equals("SUCCESS")){
//修改订单状态
//参数:用户订单号、支付完成时间、交易流水号
orderService.updataStatus(out_trade_no,resultMap.get("time_end"),resultMap.get("transaction_id"));
}else {
//如果支付失败,关闭支付,取消订单,回滚库存
//关闭支付 暂略
//支付失败,删除[修改状态]订单,回滚库存
orderService.deleteOrder(out_trade_no);
}
}
}
4、测试
创建二维码信息,浏览器输入请求:http://localhost:18090/weixin/pay/create/native?outtradeno=1355321180126445568&totalfee=1
{
"flag": true,
"code": 20000,
"message": "创建二维码预付订单成功!",
"data": {
"nonce_str": "Dc7zjYlcdvPxienu",
"code_url": "weixin://wxpay/bizpayurl?pr=s2V4HM3zz",
"appid": "wx8397f8696b538317",
"sign": "7D75E71E44AD669496C14684A66D8501",
"trade_type": "NATIVE",
"return_msg": "OK",
"result_code": "SUCCESS",
"mch_id": "1473426802",
"return_code": "SUCCESS",
"prepay_id": "wx04014823728571d0a1350cf856a41e0000"
}
}
修改pay.html的支付地址
<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=s2V4HM3zz'//指定二维码图片代表的真正的值
})
</script>
</html>
双击pay.html生成二维码
扫码支付成功后,已经监听到了信息
数据库中的订单表,也根据监听到的消息进行了改变
八、延时队列配置
创建订单后,延长支付时间
1、订单系统中,创建延时队列配置类
/**
* @Author TeaBowl
* @Date 2021/2/4 11:11
* @Version 1.0
* 延时队列配置
* Queue1->Queue2
*/
@Configuration
public class QueueConfig {
/**
* 创建Queue1
* 延时队列会过期,过期后将数据发送给Queue2
* 死信:就是超过有效时间仍没有读取的数据,就是放弃读取的数据
*/
@Bean
public Queue orderDelayQueue() {
return QueueBuilder
//设置队列名
.durable("orderDelayQueue")
/**
* 死信交换机->新交换机
* Queue1消息超时,进入死信队列,绑定死信交换机x-dead-letter-exchange
* 参数:死信交换机、被绑定的新交换机
*/
.withArgument("x-dead-letter-exchange","orderListenerExchange")
/**
* 死信路由Key->新路由Key
* Queue1绑定死信交换机后,路由key就是x-dead-letter-routing-key
* orderListenerQueue是新的路由key,名称就是队列Queue2的名称
*/
.withArgument("x-dead-letter-routing-key","orderListenerQueue")
.build();
}
/**
* 创建Queue2
* Queue1过期后,进入死信交换机x-dead-letter-exchange
* Queue1数据绑定新的交换机orderListenerExchange
* 路由到队列Queue2
* @return
*/
@Bean
public Queue orderListenerQueue() {
//参数:队列名、是否持久化
return new Queue("orderListenerQueue",true);
}
/**
* 创建交换机
* @return
*/
@Bean
public Exchange orderListenerExchange(){
return new DirectExchange("orderListenerExchange");
}
/**
* 队列Queue2绑定新交换机
* @param orderListenerQueue:队列Queue2
* @param orderListenerExchange:新的交换机
* @return
*/
@Bean
public Binding orderListenerBinding(Queue orderListenerQueue,Exchange orderListenerExchange){
return BindingBuilder
//新队列
.bind(orderListenerQueue)
//新交换机
.to(orderListenerExchange)
//新路由key
.with("orderListenerQueue")
.noargs();
}
}
2、修改订单应用层实现类OrderServiceImpl
注入MQ操作对象
@Autowired
private RabbitTemplate rabbitTemplate;
修改添加订单方法
/**
* 增加Order 添加订单实现 就是把购物车里的商品信息生成一个订单
* 订单表:tb_order 统计信息(订单里的商品)
* 明细表:tb_order_item 商品详细信息
* 订单添加1次,明细添加多次
*
* @param order:前端接收的订单信息
*/
@Override
public void add(Order order) {
/**
* 未实现
* 1.价格校验
* 2.当前购物车和订单捆绑了,没有拆开
*/
//生成订单id
order.setId(String.valueOf(idWorker.nextId()));
/**
* 获取订单明细——>购物车集合
* 获取登录名:order.getUsername()
* orderItems:明细对象集合
*/
List<OrderItem> orderItems = new ArrayList<>();
/**
* 获取勾选的商品ID,需要下单的商品,将要下单的商品ID从购物车中移除
* 勾选了的id集合:order.getSkuIds()
*/
for (Long skuId : order.getSkuIds()) {
//根据商品id,查询要下单的商品
orderItems.add((OrderItem) redisTemplate.boundHashOps("Cart_" + order.getUsername()).get(skuId));
//根据商品id,从购物车中删除要下单的商品
redisTemplate.boundHashOps("Cart_" + order.getUsername()).delete(skuId);
}
//初始化总数量
int totalNum = 0;
//初始化总金额
int totalMoney = 0;
//封装Map<String,Integer> 封装库存递减数据
Map<String, Integer> decrmap = new HashMap<>();
//循环明细 orderItems:购物车里的所有商品
for (OrderItem orderItem : orderItems) {
//获取总数量 orderItem:购物车里,其中一种商品
//totalNum:购物车里,商品的总数量
totalNum += orderItem.getNum();
//totalMoney:购物车里,商品的总金额
totalMoney += orderItem.getMoney();
//设置订单明细id
orderItem.setId(String.valueOf(idWorker.nextId()));
//设置订单明细所属的订单 order.getId():订单id
orderItem.setOrderId(order.getId());
//是否退货 0:未退货,1:已退货
orderItem.setIsReturn("0");
//封装递减数据 参数:商品ID、订单中的商品数量
decrmap.put(orderItem.getSkuId().toString(),orderItem.getNum());
}
//1、添加订单
//创建订单时间:当前时间
order.setCreateTime(new Date());
//修改订单时间:当前时间
order.setUpdateTime(order.getCreateTime());
//订单来源
// 1:web,2:app,3:微信公众号,4:微信小程序 5 H5手机页面
order.setSourceType("1");
//订单状态
//0:未完成,1:已完成,2:已退货
order.setOrderStatus("0");
//支付状态
//0:未支付,1:已支付,2:支付失败
order.setPayStatus("0");
//订单是否删除 0:未删除,1:已删除
order.setIsDelete("0");
/**
* 订单里,购买商品的总数量=每个商品的总数量之和
* 1.获取订单明细——>购物车集合
* 2.循环订单明细,获取每个商品的购买数量
* 3.所有商品的数量求和
*/
order.setTotalNum(totalNum);
//订单里,商品的总金额=每个商品的总金额之和
order.setTotalMoney(totalMoney);
//实付金额,实付金额金额=每个商品的总金额之和
//存在优惠时,金额会变动,此处无优惠,商品总金额=实付金额
order.setPayMoney(totalMoney);
//2、添加明细
//添加订单信息
orderMapper.insertSelective(order);
//循环添加订单明细信息
for (OrderItem orderItem : orderItems) {
//根据订单,循环添加订单明细信息
orderItemMapper.insertSelective(orderItem);
}
/**
* 库存递减
* decrmap:要减掉的数据
*/
skuFeign.decrCount(decrmap);
//为用户添加积分活跃度,+1
userFeign.addPoints(1);
//设置延时消息发送时间
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("创建订单时间:"+simpleDateFormat.format(new Date()));
//添加订单
rabbitTemplate.convertAndSend("orderDelayQueue", (Object) order.getId(),
new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
//设置延时读取 10s
message.getMessageProperties().setExpiration("10000");
return message;
}
});
}
3、创建过期消息监听
/**
* @Author TeaBowl
* @Date 2021/2/4 12:35
* @Version 1.0
* 过期消息监听,监听队列Queue2
*/
@Component
@RabbitListener(queues = "orderListenerQueue")
public class DelayMessageListener {
/**
* 延时队列监听
* @param message
*/
@RabbitHandler
public void getDelayMessage(String message){
//设置延时消息发送时间
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.print("监听到消息的时间:\n"+simpleDateFormat.format(new Date()));
System.out.println("监听到的消息:\n"+message);
}
}
结果:
更多推荐
所有评论(0)