微信統一下單接口

 

 項目目前使用了微信公衆平臺的支付接口,遇到不少坑。其實仍是以爲微信接口的API太散亂,作完了一套以後再看下,發現基本都有說明。沒有java版本的demo。下面是本身的一些步驟思路;前端

  1. 步驟大致講解:項目作的時候,是用戶點擊付款,獲取預支付ID,在返回前臺頁面進行調用JSAPI,付款完成。因此第7-9步我先作了。若是按照圖中步驟作也許能成功。

        

  • 付款詳情頁面 (有金額、付款對象、商品等信息)
  • 點擊付款按鈕,進入到後臺
  • 將付款的參數、key、appid、mchid 等進行簽名,以後組裝爲xml格式的字符串
  • 調用支付API並傳遞字符串。獲得返回的預支付xml字符串,驗證code
  • 將預支付ID以及必要的參數 返回到前臺調用JSAPI進行付款。
  • 用戶輸入密碼,提示付款完成
  • 微信調用回調通知地址
  • 通知地址返回成功信息
  • 結束

2.準備工做:java

  • appid:在微信公衆平臺獲取
  • key:微信商戶key(第一次的時候會出現簽名無效,重置下key)
  • mch_id:商戶ID

3.步驟詳解git

  • 付款詳情頁面:
    $("#wxsubmit").on("click",function(){
    	
    	if (typeof WeixinJSBridge == "undefined"){
    		if( document.addEventListener ){
    			document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
    		}else if (document.attachEvent){
    			document.attachEvent('WeixinJSBridgeReady', onBridgeReady); 
    		    document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
    		}
    	}else{
    		onBridgeReady();
    	}
    				
    	function onBridgeReady(){
    		$$.ajax({
    			type: "get",
    			async:"false",
    			url:  "pay?total_fee=" + pay_total_fee, //請求後臺支付方法
    			dataType: "json",
    			success:function(result){
    			     var data = result.data;
    			     if(null == data.appId || data.code == "-100"){
    			          alert(data.msg);
    			     }
                     //獲得預支付ID、其餘必須值傳遞給微信
    			     WeixinJSBridge.invoke('getBrandWCPayRequest',{
    					"appId" : data.appId.trim(),
    					"timeStamp" : data.timeStamp.trim(),
    					"nonceStr" : data.nonceStr.trim(),
    					"package" : data.package.trim(),
    					"signType" :"MD5".trim(),
    					"paySign" : data.sign.trim()
    				},function(res){
    					 // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功後返回    ok,但並不保證它絕對可靠。
    					if(res.err_msg == "get_brand_wcpay_request:ok" ) {
    					    view.router.back();
    					 }
    				})
    			}
    		});
    	}
    });

    支付方法:ajax

  • /**
         * 微信支付接口
         *
         * @param request
         * @param total_fee 支付金額
         * @return
         * @throws Exception
         */
        @ResponseBody
        @RequestMapping(value = "pay")
        public Map<String, Object> pay(HttpServletRequest request,
                                       @RequestParam(required = true) String total_fee) throws Exception {
            
            String appid =  "********"; 
            String key =  "********"; //商戶號key
            String mch_id =  "********"; //商戶號
            String openid = "********";// 用戶在商戶appid下的惟一標識
            String uuid = UUIDUtil.create32UUID(); //做爲隨機值串、商戶訂單號使用
            String product_name = "付款";
            String totalFee = 1;//獲取提交的商品價格(分)
            Map<String, Object> sign; //用於兩次MD5簽名結果
    
            //參與簽名的map
            Map<String, Object> signParams = new HashMap<>();
            signParams.put("attach", compId); //微信附帶參數,給什麼返回什麼
            signParams.put("appid", appid);
            signParams.put("mch_id", mch_id);
            signParams.put("nonce_str", uuid);
            signParams.put("body", product_name); //商品描述
            signParams.put("out_trade_no", uuid); //商戶訂單號
            signParams.put("total_fee", totalFee); //商品總金額,以分爲單位
            signParams.put("spbill_create_ip", request.getRemoteAddr()); //訂單生成的機器IP,指用戶瀏覽器端IP
            signParams.put("notify_url", prop.get("wx.NOTIFY_URL")); //通知地址
            signParams.put("trade_type", prop.get("wx.TRADE_TYPE")); //交易類型
            signParams.put("openid", openid);
    
            //MD5簽名
            sign = createSign(signParams, key);
    
            //組裝xml字符串
            StringBuffer xmlStr = new StringBuffer();
            xmlStr.append("<xml>");
            for (String str : new ArrayList<>(sign.keySet())) {
                xmlStr.append("<" + str + "><![CDATA[" + sign.get(str) + "]]></" + str + ">");
            }
            xmlStr.append("</xml>");
    
            try {
                //與微信支付創建鏈接,獲取預支付ID(prepay_id),以便支付使用
                HttpURLConnection conn = (HttpURLConnection) new URL((String) prop.get("wx.URL")).openConnection();
                conn.setRequestMethod("POST");
                conn.setDoOutput(true); //是否進行輸入輸出 默認爲false
                BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());
    
                //向輸出流中添加數據
                buffOutStr.write(xmlStr.toString().getBytes("UTF8"));
                buffOutStr.flush();
                buffOutStr.close();
    
                //獲取微信返回xml字符串
                String sa = getStrFormInStream(conn.getInputStream());
    
                //將xml轉換爲Map
                Map<String, Object> map = XmlToMap.getMap(sa);
                //預支付ID,返回的code正確(根據微信文檔return_code和result_code都爲SUCCESS的時候纔會返回code_url、prepay_id)
                if (!CollectionUtils.isEmpty(map)
                        && "SUCCESS".equals(map.get("return_code")) && "SUCCESS".equals(map.get("result_code"))) {
                    //設置支付參數
                    Map<String, Object> params = new TreeMap<>();
                    params.put("appId", appid);
                    params.put("nonceStr", map.get("nonce_str").toString().trim());
                    params.put("package", "prepay_id=" + map.get("prepay_id").toString().trim());
                    params.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000).trim());
                    params.put("signType", "MD5");
    
                    //第二次簽名
                    sign.clear();
                    sign = createSign(params, key);
    
                    //返回結果到前臺,並調用微信支付頁面
                    return Response.success(sign);
                } else {
                    return Response.failure("獲取預支付ID失敗");
                }
            } catch (Exception e) {
               
            }
        }

    公用方法(支付方法中用到的通用方法):spring

    /**
         * 微信支付結果通知
         * @param request
         * @param response
         * @throws Exception
         */
        @SystemLog(module = "微信支付結果通知", methods = "/wx/notifyUrl.json", description = "", level = "1", type = "1")
        @RequestMapping(value = "/wx/notifyUrl.json", method = {RequestMethod.GET, RequestMethod.POST})
        public void notifyUrl(HttpServletRequest request, HttpServletResponse response) throws Exception {
            //獲取微信返回結果xml字符串,並轉換爲map
            String result = getStrFormInStream(request.getInputStream());
            Map<String, Object> map = XmlToMap.getMap(result);
            saleService.pay(payRecordService, (String) map.get("attach"), (String) map.get("out_trade_no"));
            //TODO 測試是否有8次 通知 --  像一張表中加入一條數據 8條數據
            response.getWriter().print("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");
        }
    
        /**
         * 將輸入流中的字符轉換爲utf8編碼的字符串
         *
         * @param inStream 輸入流
         * @return uft8編碼字符串
         * @throws IOException
         */
        private String getStrFormInStream(InputStream inStream) throws IOException {
            BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
            String line;
            StringBuffer sb = new StringBuffer();
    
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            inStream.close();
            return new String(sb.toString().getBytes("gbk"), "utf-8");
        }
    
        /**
         * 建立md5摘要,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。
         *
         * @param params
         * @param key
         * @return
         */
        private Map<String, Object> createSign(Map<String, Object> params, String key) {
            List<String> list = new ArrayList<String>(params.keySet());
            Collections.sort(list);
            StringBuffer rs = new StringBuffer();
            for (String str : list) {
                String value = (String) params.get(str);
                if (null != value && !"".equals(value) && !"sign".equals(value)
                        && !"key".equals(value)) {
                    rs.append("&" + str + "=" + value);
                    logger.info("  " + str + " : " + value);
                }
            }
            params.put("sign", MD5Util.MD5Encode((rs.append("&key=") + key).toString().replaceFirst("&", ""), "UTF8").toUpperCase());
           
            return params;
        }

    基礎類(公共方法中用的的類):json

  • MD5加密類:瀏覽器

    package ******;
    
    import java.security.MessageDigest;
    
    /**
     * @author JGS
     * @description MD5加密
     * @created 2016-09-08 15:49
     */
    public class MD5Util {
    
        private static String byteArrayToHexString(byte b[]) {
            StringBuffer resultSb = new StringBuffer();
            for (int i = 0; i < b.length; i++)
                resultSb.append(byteToHexString(b[i]));
    
            return resultSb.toString();
        }
    
        private static String byteToHexString(byte b) {
            int n = b;
            if (n < 0)
                n += 256;
            int d1 = n / 16;
            int d2 = n % 16;
            return hexDigits[d1] + hexDigits[d2];
        }
    
        public static String MD5Encode(String origin, String charsetname) {
            String resultString = null;
            try {
                resultString = new String(origin);
                MessageDigest md = MessageDigest.getInstance("MD5");
                if (charsetname == null || "".equals(charsetname))
                    resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
                else {
                    resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
                }
            } catch (Exception exception) {
            }
            return resultString;
        }
    
        private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
                "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
    
    }

    解析xml微信

  • package *****;
    
    import com.suncd.dev.util.BeanUtil;
    import org.springframework.util.StringUtils;
    import org.xml.sax.InputSource;
    import org.xml.sax.SAXException;
    import org.xml.sax.helpers.DefaultHandler;
    
    import javax.xml.parsers.ParserConfigurationException;
    import javax.xml.parsers.SAXParserFactory;
    import java.io.IOException;
    import java.io.StringReader;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @author JGS
     * @description 解析xml(key=xml的不解析)
     * @created 2016-09-12 11:12
     */
    
    public class XmlToMap extends DefaultHandler {
    
        private StringBuffer buf;
        private String str;
        private static Map<String, Object> map = new HashMap<>();
    
        public XmlToMap() {
            super();
        }
    
        public void startDocument() throws SAXException {
            buf = new StringBuffer();
            System.out.println("*******開始解析XML*******");
        }
    
        public void endDocument() throws SAXException {
            map.remove("xml");
            System.out.println("*******XML解析結束*******");
        }
    
        public void endElement(String namespaceURI, String localName, String fullName) throws SAXException {
            str = buf.toString();
            String value = str;
            if (!StringUtils.isEmpty(buf) && !fullName.equals("xml")) {
                map.put(fullName, value);
            }
            buf.delete(0, buf.length());
        }
    
        public void characters(char[] chars, int start, int length) throws SAXException {
            //將元素內容累加到StringBuffer中
            buf.append(chars, start, length);
            System.out.println(buf.toString());
        }
    
        /**
         * 將xml字符串轉換爲對象
         *
         * @param str       轉換的XML字符串
         * @param beanClass 對象Class
         * @return
         * @throws javax.xml.parsers.ParserConfigurationException
         * @throws org.xml.sax.SAXException
         * @throws java.io.IOException
         */
        public static <T> T getObject(String str, Class<T> beanClass) throws IOException, SAXException, ParserConfigurationException {
            return (T) BeanUtil.map2Bean(getMap(str), beanClass);
        }
    
        public static Map getMap(String str) throws ParserConfigurationException, SAXException, IOException {
            XmlToMap xmlMap = new XmlToMap();
            SAXParserFactory.newInstance().newSAXParser().parse(new InputSource(new StringReader(str)), xmlMap);
            return map;
        }
    }
相關文章
相關標籤/搜索