一、创建微服务

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);
    }
}

结果:

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐