1. 程式人生 > >微信支付:回調處理

微信支付:回調處理

led user date fin dev org 信息 reader content

1.封裝工具類:

  包括的方法實現:

    xml轉Map,Map轉xml,生成微信支付簽名sign,簽名的校驗,把Map轉為SortMap(有序)

package net.xdclass.xdvideo.controller;

import net.xdclass.xdvideo.config.WeChatConfig;
import net.xdclass.xdvideo.domain.JsonData;
import net.xdclass.xdvideo.domain.User;
import net.xdclass.xdvideo.domain.VideoOrder;
import net.xdclass.xdvideo.service.UserService; import net.xdclass.xdvideo.service.VideoOrderService; import net.xdclass.xdvideo.utils.CommonUtils; import net.xdclass.xdvideo.utils.JwtUtils; import net.xdclass.xdvideo.utils.WXPayUtil; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import
org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.*; import java.net.URLEncoder; import java.util.*; @Controller @RequestMapping("/api/v1/wechat") public class WechatController { @Autowired private WeChatConfig weChatConfig; @Autowired private UserService userService; @Autowired private VideoOrderService videoOrderService; /** * 拼裝微信掃一掃登錄url * @return */ @GetMapping("login_url") @ResponseBody public JsonData loginUrl(@RequestParam(value = "access_page",required = true)String accessPage) throws UnsupportedEncodingException { String redirectUrl = weChatConfig.getOpenRedirectUrl(); //獲取開放平臺重定向地址 String callbackUrl = URLEncoder.encode(redirectUrl,"GBK"); //進行編碼 String qrcodeUrl = String.format(weChatConfig.getOpenQrcodeUrl(),weChatConfig.getOpenAppid(),callbackUrl,accessPage); return JsonData.buildSuccess(qrcodeUrl); } /** * 微信掃碼登錄,回調地址 * @param code * @param state * @param response * @throws IOException */ @GetMapping("/user/callback") public void wechatUserCallback(@RequestParam(value = "code",required = true) String code, String state, HttpServletResponse response) throws IOException { User user = userService.saveWeChatUser(code); if(user != null){ //生成jwt String token = JwtUtils.geneJsonWebToken(user); // state 當前用戶的頁面地址,需要拼接 http:// 這樣才不會站內跳轉 response.sendRedirect(state+"?token="+token+"&head_img="+user.getHeadImg()+"&name="+URLEncoder.encode(user.getName(),"UTF-8")); } } package net.xdclass.xdvideo.utils; import org.w3c.dom.Entity; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.StringWriter; import java.util.*; /** * 微信支付工具類,xml轉map,map轉xml,生成簽名 */ public class WXPayUtil { /** * XML格式字符串轉換為Map * * @param strXML XML字符串 * @return XML數據轉換後的Map * @throws Exception */ public static Map<String, String> xmlToMap(String strXML) throws Exception { try { Map<String, String> data = new HashMap<String, String>(); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8")); org.w3c.dom.Document doc = documentBuilder.parse(stream); doc.getDocumentElement().normalize(); NodeList nodeList = doc.getDocumentElement().getChildNodes(); for (int idx = 0; idx < nodeList.getLength(); ++idx) { Node node = nodeList.item(idx); if (node.getNodeType() == Node.ELEMENT_NODE) { org.w3c.dom.Element element = (org.w3c.dom.Element) node; data.put(element.getNodeName(), element.getTextContent()); } } try { stream.close(); } catch (Exception ex) { // do nothing } return data; } catch (Exception ex) { throw ex; } } /** * 將Map轉換為XML格式的字符串 * * @param data Map類型數據 * @return XML格式的字符串 * @throws Exception */ public static String mapToXml(Map<String, String> data) throws Exception { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder(); org.w3c.dom.Document document = documentBuilder.newDocument(); org.w3c.dom.Element root = document.createElement("xml"); document.appendChild(root); for (String key: data.keySet()) { String value = data.get(key); if (value == null) { value = ""; } value = value.trim(); org.w3c.dom.Element filed = document.createElement(key); filed.appendChild(document.createTextNode(value)); root.appendChild(filed); } TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); DOMSource source = new DOMSource(document); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); transformer.transform(source, result); String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", ""); try { writer.close(); } catch (Exception ex) { } return output; } /** * 生成微信支付sign * @return */ public static String createSign(SortedMap<String, String> params, String key){ StringBuilder sb = new StringBuilder(); Set<Map.Entry<String, String>> es = params.entrySet(); Iterator<Map.Entry<String,String>> it = es.iterator(); //生成 stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA"; while (it.hasNext()){ Map.Entry<String,String> entry = (Map.Entry<String,String>)it.next(); String k = (String)entry.getKey(); String v = (String)entry.getValue(); if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)){ sb.append(k+"="+v+"&"); } } sb.append("key=").append(key); String sign = CommonUtils.MD5(sb.toString()).toUpperCase(); return sign; } /** * 校驗簽名 * @param params * @param key * @return */ public static boolean isCorrectSign(SortedMap<String, String> params, String key){ String sign = createSign(params,key); String weixinPaySign = params.get("sign").toUpperCase(); return weixinPaySign.equals(sign); } /** * 獲取有序map * @param map * @return */ public static SortedMap<String,String> getSortedMap(Map<String,String> map){ SortedMap<String, String> sortedMap = new TreeMap<>(); Iterator<String> it = map.keySet().iterator(); while (it.hasNext()){ String key = (String)it.next(); String value = map.get(key); String temp = ""; if( null != value){ temp = value.trim(); } sortedMap.put(key,temp); } return sortedMap; } } }

2.根據配置的回調url開發controller:

  掃碼後返回給回調url的結果是個xml字符串:

package net.xdclass.xdvideo.controller;

import net.xdclass.xdvideo.config.WeChatConfig;
import net.xdclass.xdvideo.domain.JsonData;
import net.xdclass.xdvideo.domain.User;
import net.xdclass.xdvideo.domain.VideoOrder;
import net.xdclass.xdvideo.service.UserService;
import net.xdclass.xdvideo.service.VideoOrderService;
import net.xdclass.xdvideo.utils.JwtUtils;
import net.xdclass.xdvideo.utils.WXPayUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Date;
import java.util.Map;
import java.util.SortedMap;

@Controller
@RequestMapping("/api/v1/wechat")
public class WechatController {

    @Autowired
    private WeChatConfig weChatConfig;

    @Autowired
    private UserService userService;


    @Autowired
    private VideoOrderService videoOrderService;

    /**
     * 拼裝微信掃一掃登錄url
     * @return
     */
    @GetMapping("login_url")
    @ResponseBody
    public JsonData loginUrl(@RequestParam(value = "access_page",required = true)String accessPage) throws UnsupportedEncodingException {

        String redirectUrl = weChatConfig.getOpenRedirectUrl(); //獲取開放平臺重定向地址

        String callbackUrl = URLEncoder.encode(redirectUrl,"GBK"); //進行編碼

        String qrcodeUrl = String.format(weChatConfig.getOpenQrcodeUrl(),weChatConfig.getOpenAppid(),callbackUrl,accessPage);

        return JsonData.buildSuccess(qrcodeUrl);
    }


    /**
     * 微信掃碼登錄,回調地址
     * @param code
     * @param state
     * @param response
     * @throws IOException
     */
    @GetMapping("/user/callback")
    public void wechatUserCallback(@RequestParam(value = "code",required = true) String code,
                                   String state, HttpServletResponse response) throws IOException {


        User user = userService.saveWeChatUser(code);
        if(user != null){
            //生成jwt
            String token = JwtUtils.geneJsonWebToken(user);
            // state 當前用戶的頁面地址,需要拼接 http://  這樣才不會站內跳轉

            response.sendRedirect(state+"?token="+token+"&head_img="+user.getHeadImg()+"&name="+URLEncoder.encode(user.getName(),"UTF-8"));
        }


    }


    /**
     * 微信支付回調
     */
    @RequestMapping("/order/callback")
    public void orderCallback(HttpServletRequest request,HttpServletResponse response) throws Exception {

        InputStream inputStream =  request.getInputStream();

        //BufferedReader是包裝設計模式,性能更搞
        BufferedReader in =  new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
        StringBuffer sb = new StringBuffer();
        String line ;
        while ((line = in.readLine()) != null){
            sb.append(line);
        }
        in.close();
        inputStream.close();
        Map<String,String> callbackMap = WXPayUtil.xmlToMap(sb.toString());
        System.out.println(callbackMap.toString());

        SortedMap<String,String> sortedMap = WXPayUtil.getSortedMap(callbackMap);

        //判斷簽名是否正確
        if(WXPayUtil.isCorrectSign(sortedMap,weChatConfig.getKey())){

            if("SUCCESS".equals(sortedMap.get("result_code"))){
                //獲取訂單流水號
                String outTradeNo = sortedMap.get("out_trade_no");
                //根據訂單流水號獲取訂單信息
                VideoOrder dbVideoOrder = videoOrderService.findByOutTradeNo(outTradeNo);
                //判斷邏輯看業務場景,狀態是否為未支付0,是的話更新訂單信息
                if(dbVideoOrder != null && dbVideoOrder.getState()==0){
                    VideoOrder videoOrder = new VideoOrder();
                    videoOrder.setOpenid(sortedMap.get("openid"));
                    videoOrder.setOutTradeNo(outTradeNo);
                    videoOrder.setNotifyTime(new Date());
                    videoOrder.setState(1);
                    int rows = videoOrderService.updateVideoOderByOutTradeNo(videoOrder);
                    if(rows == 1){ //通知微信訂單處理成功
                        response.setContentType("text/xml");
                        response.getWriter().println("success");
                        return;
                    }
                }
            }
        }
        //都處理失敗
        response.setContentType("text/xml");
        response.getWriter().println("fail");

    }
}

文檔參考:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_7&index=3

微信支付:回調處理