1. 程式人生 > 實用技巧 >微信掃碼支付4-支付回撥

微信掃碼支付4-支付回撥

一、準備

1、配置ngrok

將ngrok對映到本地8170埠,並啟動

2、新增工具類

在common_util中新增工具類StreamUtils.java

public class StreamUtils {

    private static int _buffer_size = 1024;

    /**
     * InputStream流轉換成String字串
     * @param inStream InputStream流
     * @param encoding 編碼格式
     * @return String字串
     */
    public static String inputStream2String(InputStream inStream, String encoding){
        String result = null;
        ByteArrayOutputStream outStream = null;
        try {
            if(inStream != null){
                outStream = new ByteArrayOutputStream();
                byte[] tempBytes = new byte[_buffer_size];
                int count = -1;
                while((count = inStream.read(tempBytes, 0, _buffer_size)) != -1){
                    outStream.write(tempBytes, 0, count);
                }
                tempBytes = null;
                outStream.flush();
                result = new String(outStream.toByteArray(), encoding);

                outStream.close();
            }
        } catch (Exception e) {
            result = null;
        } finally {
            try {
                if(inStream != null) {
                    inStream.close();
                    inStream = null;
                }
                if(outStream != null) {
                    outStream.close();
                    outStream = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}

二、支付回撥

1、回撥方法

該連結是通過【統一下單API】中提交的引數notify_url設定,如果連結無法訪問,商戶將無法接收到微信通知。
參考文件:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8
ApiWeixinPayController

@Autowired
private WeixinPayProperties weixinPayProperties;

@Autowired
private OrderService orderService;

/**
     * 支付回撥:注意這裡是【post】方式
     */
@PostMapping("callback/notify")
public String wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {

    System.out.println("callback/notify 被呼叫");
    
    // 獲得通知結果
    ServletInputStream inputStream = request.getInputStream();
    String notifyXml = StreamUtils.inputStream2String(inputStream, "utf-8");
    System.out.println("xmlString = " + notifyXml);
    
    // 定義響應物件
    HashMap<String, String> returnMap = new HashMap<>();
    
    // 簽名驗證:防止偽造回撥
    if (WXPayUtil.isSignatureValid(notifyXml, weixinPayProperties.getPartnerKey())) {
    
        // 解析返回結果
        Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyXml);
    
        //判斷支付是否成功
        if("SUCCESS".equals(notifyMap.get("result_code"))){
    
            // 校驗訂單金額是否一致
            String totalFee = notifyMap.get("total_fee");
            String outTradeNo = notifyMap.get("out_trade_no");
            Order order = orderService.getOrderByOrderNo(outTradeNo);
            if(order != null && order.getTotalFee().intValue() == Integer.parseInt(totalFee)){
    
                // 判斷訂單狀態:保證介面呼叫的冪等性,如果訂單狀態已更新直接返回成功響應
                // 冪等性:無論呼叫多少次結果都是一樣的
                if(order.getStatus() == 1){
                    returnMap.put("return_code", "SUCCESS");
                    returnMap.put("return_msg", "OK");
                    String returnXml = WXPayUtil.mapToXml(returnMap);
                    response.setContentType("text/xml");
                    log.warn("通知已處理");
                    return returnXml;
                }else{
                    // 更新訂單支付狀態,並返回成功響應
                    orderService.updateOrderStatus(notifyMap);
                    returnMap.put("return_code", "SUCCESS");
                    returnMap.put("return_msg", "OK");
                    String returnXml = WXPayUtil.mapToXml(returnMap);
                    response.setContentType("text/xml");
                    log.info("支付成功,通知已處理");
                    return returnXml;
                }
            }
        }
    }
    
    // 校驗失敗,返回失敗應答
    returnMap.put("return_code", "FAIL");
    returnMap.put("return_msg", "");
    String returnXml = WXPayUtil.mapToXml(returnMap);
    response.setContentType("text/xml");
    log.warn("校驗失敗");
    return returnXml;
}

2、更新訂單狀態

更新訂單支付狀態並記錄支付日誌,將微信返回的支付結果全部記錄進資料庫的json欄位中

介面:OrderService

void updateOrderStatus(Map<String, String> map);

實現:OrderServiceImpl

@Autowired
private PayLogMapper payLogMapper;

@Transactional(rollbackFor = Exception.class)
@Override
public void updateOrderStatus(Map<String, String> map) {

    //更新訂單狀態
    String orderNo = map.get("out_trade_no");
    Order order = this.getOrderByOrderNo(orderNo);
    order.setStatus(1);//支付成功
    baseMapper.updateById(order);
    
    //記錄支付日誌
    PayLog payLog = new PayLog();
    payLog.setOrderNo(orderNo);
    payLog.setPayTime(new Date());
    payLog.setPayType(1);//支付型別
    payLog.setTotalFee(Long.parseLong(map.get("total_fee")));//總金額(分)
    payLog.setTradeState(map.get("result_code"));//支付狀態
    payLog.setTransactionId(map.get("transaction_id"));
    payLog.setAttr(new Gson().toJson(map));
    payLogMapper.insert(payLog);
}