微信二维码支付
1|0一、创建微服务
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; } }
2|0二、测试
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" } }
3|0三、生成二维码
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>
4|0四、查询支付状态
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" } }
因为上面测试支付二维码时候,我没支付,所以查询订单支付状态显示“订单未支付”。
5|0五、支付结果回调
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|0六、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”
查看消息:
7|0七、修改订单状态
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>
8|0八、延时队列配置
创建订单后,延长支付时间
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); } }
__EOF__

本文链接:https://www.cnblogs.com/chawaner/p/14360089.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix