主頁 > 後端開發 > 微信二維碼支付

微信二維碼支付

2021-02-04 13:00:35 後端開發

一、創建微服務

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生成二維碼

掃碼支付成功后,已經監聽到了資訊

資料庫中的訂單表,也根據監聽到的訊息進行了改變

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/256364.html

標籤:java

上一篇:資料結構-快速排序

下一篇:Java多執行緒游戲仿真實體分享

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more