03支付回調邏輯處理

3.支付回調邏輯處理

3.1 需求分析

在完成支付後,修改訂單狀態爲已支付,並記錄訂單日誌。

3.2 實現思路

(1)接受微信支付平臺的回調信息(xml)

<xml><appid><![CDATA[wx8397f8696b538317]]></appid>
<bank_type><![CDATA[CFT]]></bank_type>
<cash_fee><![CDATA[1]]></cash_fee>
<fee_type><![CDATA[CNY]]></fee_type>
<is_subscribe><![CDATA[N]]></is_subscribe>
<mch_id><![CDATA[1473426802]]></mch_id>
<nonce_str><![CDATA[c6bea293399a40e0a873df51e667f45a]]></nonce_str>
<openid><![CDATA[oNpSGwbtNBQROpN_dL8WUZG3wRkM]]></openid>
<out_trade_no><![CDATA[1553063775279]]></out_trade_no>
<result_code><![CDATA[SUCCESS]]></result_code>
<return_code><![CDATA[SUCCESS]]></return_code>
<sign><![CDATA[DD4E5DF5AF8D8D8061B0B8BF210127DE]]></sign>
<time_end><![CDATA[20190320143646]]></time_end>
<total_fee>1</total_fee>
<trade_type><![CDATA[NATIVE]]></trade_type>
<transaction_id><![CDATA[4200000248201903206581106357]]></transaction_id>
</xml>

(2)收到通知後,調用查詢接口查詢訂單。
(3)如果支付結果爲成功,則調用修改訂單狀態和記錄訂單日誌的方法。

3.3 代碼實現

3.3.1 內網映射工具EchoSite

在請求統一下單接口時,有個參數notify_url ,這個是回調地址,也就是在支付成功後微信支付會自動訪問這個地址,通知業務系統支付完成。但這個地址必須是互聯網可以訪問的(也就是有域名的)。
那麼如何測試呢?我們可以藉助一個工具 EchoSite 內網映射工具

(1)打開網址: https://www.echosite.cn/ 註冊用戶,登錄到控制檯下載客戶端。

(2)支付3元買一個域名(可以用1個月),點擊域名端口—搶注域名

(3)下載echosite ,添加config.yml (在軟件的配置文件中改)

# 這是你的 EchoSite 購買域名的服務器標誌
server_addr: cross.echosite.cn:4443
trust_host_root_certs: false
echosite_id: #從官網獲得
echosite_token: #從官網獲得
# 以下是你需要開啓的通道,只能開啓屬於你的域名通道
# 以下分別是 http 和 https 以及 tcp 協議的示例
tunnels:
  name1:
    subdomain: "changgou"
      proto:
        http: 127.0.0.1:9010

然後在echosite目錄中輸入以下命令

echosite -config=config.yml start-all

運行效果如下:
在這裏插入圖片描述

這樣你購買的域名就映射到127.0.0.1:9011上了。 ctrl+c 結束程序
麼才能驗證域名是否映射到你的計算機上了呢?
WxPayController新增notifyLogic方法

/**
* 回調
*/
@RequestMapping("/notify")
public void notifyLogic(){
	System.out.println("支付成功回調。。。。");
}

測試:
1)通過本地方式訪問該接口
2)通過域名形式訪問該接口

3.3.2 接收回調信息

(1)修改支付微服務配置文件

wxpay:
  notify_url: http://weizhaohui.cross.echosite.cn/wxpay/notify #回調地址

(2)修改WxPayServiceImpl ,引入

@Value( "${wxpay.notify_url}" )
private String notifyUrl;

(3)修改WxPayServiceImpl 的nativePay方法

map.put("notify_url",notifyUrl);//回調地址

測試: 重新生成訂單並支付。可以發現控制檯在不斷的觸發支付回調通知。這是爲什麼呢?
注意
1、同樣的通知可能會多次發送給商戶系統。商戶系統必須能夠正確處理重複的通知。
2、後臺通知交互時,如果微信收到商戶的應答不符合規範或超時,微信會判定本次通知失敗,重新發送通知,直到成功爲止(在通知一直不成功的情況下,微信總共會發起10次通知,通知頻率爲15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 總計 24h4m),但微信不保證通知最終一定能成功。

3.3.3回調消息接收並轉換

微信支付平臺發送給回調地址的數據是二進制流,我們需要提取二進制流轉換爲字符串,這個字符串就是xml格式。
(1)在changgou_common工程添加工具類ConvertUtils。(資源提供:資源\類文件\工具類)

package com.changgou.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * 轉換工具類
 */
public class ConvertUtils {

    /**
     * 輸入流轉換爲xml字符串
     * @param inputStream
     * @return
     */
    public static String convertToString(InputStream inputStream) throws IOException {
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inputStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        outSteam.close();
        inputStream.close();
        String result  = new String(outSteam.toByteArray(), "utf-8");
        return result;
    }
}

(2)修改notifyLogic方法

@RequestMapping("/notify")
    public void notifyLogic(HttpServletRequest request, HttpServletResponse response){
        System.out.println("支付成功回調");
        try{
            //輸入流轉換爲字符串
            String xml = ConvertUtils.convertToString(request.getInputStream());
            System.out.println(xml);
              //給微信一個結果通知
            response.setContentType("text/xml");
            String data="<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
            response.getWriter().write(data);
        }catch (Exception e){
            e.printStackTrace();
        }

    }

測試後,在控制檯看到輸出的消息

<xml><appid><![CDATA[wx8397f8696b538317]]></appid>
<bank_type><![CDATA[CFT]]></bank_type>
<cash_fee><![CDATA[1]]></cash_fee>
<fee_type><![CDATA[CNY]]></fee_type>
<is_subscribe><![CDATA[N]]></is_subscribe>
<mch_id><![CDATA[1473426802]]></mch_id>
<nonce_str><![CDATA[c6bea293399a40e0a873df51e667f45a]]></nonce_str>
<openid><![CDATA[oNpSGwbtNBQROpN_dL8WUZG3wRkM]]></openid>
<out_trade_no><![CDATA[1553063775279]]></out_trade_no>
<result_code><![CDATA[SUCCESS]]></result_code>
<return_code><![CDATA[SUCCESS]]></return_code>
<sign><![CDATA[DD4E5DF5AF8D8D8061B0B8BF210127DE]]></sign>
<time_end><![CDATA[20190320143646]]></time_end>
<total_fee>1</total_fee>
<trade_type><![CDATA[NATIVE]]></trade_type>
<transaction_id><![CDATA[4200000248201903206581106357]]></transaction_id>
</xml>

我們可以將此xml字符串,轉換爲map,提取其中的out_trade_no(訂單號),根據訂單號修改訂單狀態。

3.3.4 查詢訂單驗證通知

在這裏插入圖片描述

(1)WxPayService新增方法定義

/**
* 查詢訂單
* @param orderId
* @return
*/
Map queryOrder(String orderId);

(2)WxPayServiceImpl實現方法

@Override
public Map queryOrder(String orderId) {
	Map map=new HashMap( );
	map.put( "out_trade_no", orderId );
	try {
		return wxPay.orderQuery( map );
	} catch (Exception e) {
		e.printStackTrace();
		return null;
	}
}

(3)修改notifyLogic方法

@RequestMapping("/notify")
    public void notifyLogic(HttpServletRequest request, HttpServletResponse response){
        System.out.println("支付成功回調");
        try{
            //輸入流轉換爲字符串
            String xml = ConvertUtils.convertToString(request.getInputStream());
            System.out.println(xml);

            //基於微信發送的通知內容,完成後續的業務邏輯處理
            Map<String, String> map = WXPayUtil.xmlToMap(xml);
            if ("SUCCESS".equals(map.get("result_code"))){

                //查詢訂單
                Map result = wxPayService.queryOrder(map.get("out_trade_no"));
                System.out.println("查詢訂單結果:"+result);

                if ("SUCCESS".equals(result.get("result_code"))){

                    //將訂單的消息發送到mq'
                    Map message = new HashMap();
                    message.put("orderId",result.get("out_trade_no"));
                    message.put("transactionId",result.get("transaction_id"));

                    //消息的發送
                    rabbitTemplate.convertAndSend("", RabbitMQConfig.ORDER_PAY, JSON.toJSONString(message));

                    //完成雙向通信
                    rabbitTemplate.convertAndSend("paynotify","",result.get("out_trade_no"));
                }else {
                    //輸出錯誤原因
                    System.out.println(map.get("err_code_des"));
                }

            }else{
                //輸出錯誤原因
                System.out.println(map.get("err_code_des"));
            }



            //給微信一個結果通知
            response.setContentType("text/xml");
            String data="<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
            response.getWriter().write(data);
        }catch (Exception e){
            e.printStackTrace();
        }

    }

(4)rabbitmq中添加order_pay隊列

3.3.5 修改訂單狀態

(1)com.changgou.order.listener包下創建OrderPayListener

package com.changgou.order.listener;

import com.alibaba.fastjson.JSON;
import com.changgou.order.config.RabbitMQConfig;
import com.changgou.order.service.OrderService;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class OrderPayListener {

    @Autowired
    private OrderService orderService;

    @RabbitListener(queues = RabbitMQConfig.ORDER_PAY)
    public void receivePayMessage(String message){
        System.out.println("接收到了訂單支付的消息:"+message);

        Map map = JSON.parseObject(message, Map.class);

        //調用業務層,完成訂單數據庫的修改
        orderService.updatePayStatus((String)map.get("orderId"),(String)map.get("transactionId"));
    }
}

(2)OrderService接口新增方法定義

void updatePayStatus(String orderId, String transactionId);

(3)OrderServiceImpl新增方法實現

  @Override
    @Transactional
    public void updatePayStatus(String orderId, String transactionId) {

        //1.查詢訂單
        Order order = orderMapper.selectByPrimaryKey(orderId);
        if (order != null && "0".equals(order.getPayStatus())){
            //2.修改訂單的支付狀態
            order.setPayStatus("1");
            order.setOrderStatus("1");
            order.setUpdateTime(new Date());
            order.setPayTime(new Date());
            order.setTransactionId(transactionId); //微信返回的交易流水號
            orderMapper.updateByPrimaryKeySelective(order);

            //3.記錄訂單日誌
            OrderLog orderLog = new OrderLog();
            orderLog.setId(idWorker.nextId()+"");
            orderLog.setOperater("system");
            orderLog.setOperateTime(new Date());
            orderLog.setOrderStatus("1");
            orderLog.setPayStatus("1");
            orderLog.setRemarks("交易流水號:"+transactionId);
            orderLog.setOrderId(orderId);
            orderLogMapper.insert(orderLog);
        }


    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章