微信二維碼支付


一、創建微服務

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

結果:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM