微信掃碼支付

  1. 接入掃碼支付(包含PC網站支付)包含三個階段,問這裏只講使用,也就是第2階段的《啓動設計和開發》。
  2. 點擊查看開發者文檔(掃碼支付)後,這裏感覺微信的文檔沒有支付寶好理解(稍微吐槽下~~~),不過我們忽略一切,直接進入模式二:模式二最簡單直接,不需要在商戶後臺進行配置,推薦大家使用,微信也說流程更爲簡單,我這裏也講的是模式二,模式一大家有興趣可以自行研究下。
  3. 如上圖,總流程有14步,主要流程是生成訂單、調統一下單API、將返回的支付交易鏈接生成二維碼展示;我這邊主要就是將這三步結合springmvc後,成功生兒二維碼之後,用戶就可以掃碼支付了。後面的回調跟跟我的另一篇博文基本類似,大家借鑑下就行了:支付寶:web頁面掃碼支付、網站支付、支付寶即時到賬 + springmvc

四、實現:

  1. 準備:根據統一下單接口API我先定義了三個對象:UnifiedOrderRequest(統一下單請求參數(必填))、UnifiedOrderRequestExt(統一下單請求參數(非必填))、UnifiedOrderRespose(統一下單返回參數);具體如下代碼,get、set方法可自行生產,太佔篇幅。

    UnifiedOrderRequest.class
    [javascript] view plain copy 派生到我的代碼片
    1. /** 
    2.  * 統一下單請求參數(必填) 
    3.  * @author Y 
    4.  * 
    5.  */  
    6. public class UnifiedOrderRequest {  
    7.     private String appid;               //公衆賬號ID  
    8.     private String mch_id;              //商戶號  
    9.     private String nonce_str;           //隨機字符串  
    10.     private String sign;                //簽名  
    11.     private String body;                //商品描述  
    12.     private String out_trade_no;        <span style="white-space:pre">    </span>//商戶訂單號  
    13.     private String total_fee;           //總金額  
    14.     private String spbill_create_ip;    <span style="white-space:pre">    </span>//終端IP  
    15.     private String notify_url;          //通知地址  
    16.     private String trade_type;          //交易類型  
    17. }  
    UnifiedOrderRequestExt.class
    [javascript] view plain copy 派生到我的代碼片
    1. /** 
    2.  * 統一下單請求參數(非必填) 
    3.  * @author Y 
    4.  * 
    5.  */  
    6. public class UnifiedOrderRequestExt extends UnifiedOrderRequest{  
    7.       
    8.     private String device_info;         //設備號  
    9.     private String detail;              //商品詳情  
    10.     private String attach;              //附加數據  
    11.     private String fee_type;            //貨幣類型  
    12.     private String time_start;          //交易起始時間  
    13.     private String time_expire;         //交易結束時間  
    14.     private String goods_tag;           //商品標記  
    15.     private String product_id;          //商品ID  
    16.     private String limit_pay;           //指定支付方式  
    17.     private String openid;              //用戶標識  
    18. }  
    UnifiedOrderRespose.class
    [javascript] view plain copy 派生到我的代碼片
    1. /** 
    2.  * 統一下單返回參數 
    3.  * @author Y 
    4.  * 
    5.  */  
    6. public class UnifiedOrderRespose {  
    7.     private String return_code;             //返回狀態碼  
    8.     private String return_msg;              //返回信息  
    9.     private String appid;                   //公衆賬號ID  
    10.     private String mch_id;                  //商戶號  
    11.     private String device_info;             //設備號  
    12.     private String nonce_str;               //隨機字符串  
    13.     private String sign;                    //簽名  
    14.     private String result_code;             //業務結果  
    15.     private String err_code;                //錯誤代碼  
    16.     private String err_code_des;            <span style="white-space:pre">    </span>//錯誤代碼描述  
    17.     private String trade_type;              //交易類型  
    18.     private String prepay_id;               //預支付交易會話標識  
    19.     private String code_url;                //二維碼鏈接  
    20. }  
  2. Controller主入口:
    [javascript] view plain copy 派生到我的代碼片
    1. /** 
    2.  * 創建二維碼 
    3.  */  
    4. @RequestMapping("createQRCode")  
    5. public void createQRCode(String orderId, HttpServletResponse response) {  
    6.       
    7.     //生成訂單  
    8.     String orderInfo = createOrderInfo(orderId);  
    9.     //調統一下單API  
    10.     String code_url = httpOrder(orderInfo);  
    11.     //將返回預支付交易鏈接(code_url)生成二維碼圖片  
    12.     //這裏使用的是zxing   <span style="color:#ff0000;"><strong>說明1(見文末)</strong></span>  
    13.     try {  
    14.         int width = 200;  
    15.         int height = 200;  
    16.         String format = "png";  
    17.         Hashtable hints = new Hashtable();  
    18.         hints.put(EncodeHintType.CHARACTER_SET, "utf-8");  
    19.         BitMatrix bitMatrix = new MultiFormatWriter().encode(code_url, BarcodeFormat.QR_CODE, width, height, hints);  
    20.         OutputStream out = null;  
    21.         out = response.getOutputStream();  
    22.         MatrixToImageWriter.writeToStream(bitMatrix, format, out);  
    23.         out.flush();  
    24.         out.close();  
    25.     } catch (Exception e) {  
    26.     }  
    27.   
    28. }  

  3. 生成訂單:分兩部分:一部分是業務需求的訂單信息,就是發起支付前的訂單信息,業務系統自行創建存儲;另一部分是滿足統一下單API要求的訂單信息(也是我們這裏要講的)。“xxxxxx”:是你需要自己填寫的對應信息:
    [javascript] view plain copy 派生到我的代碼片
    1. /** 
    2.  * 生成訂單 
    3.  * @param orderId 
    4.  * @return 
    5.  */  
    6. private String createOrderInfo(String orderId) {  
    7.     //生成訂單對象  
    8.     UnifiedOrderRequest unifiedOrderRequest = new UnifiedOrderRequest();  
    9.     unifiedOrderRequest.setAppid("xxxxxxxxxxxxx");//公衆賬號ID  
    10.     unifiedOrderRequest.setMch_id("xxxxxxxxx");//商戶號  
    11.     unifiedOrderRequest.setNonce_str(StringUtil.makeUUID());//隨機字符串       <span style="color:#ff0000;"><strong>說明2(見文末)</strong></span>  
    12.     unifiedOrderRequest.setBody("xxxxxx");//商品描述  
    13.     unifiedOrderRequest.setOut_trade_no(orderId);//商戶訂單號  
    14.     unifiedOrderRequest.setTotal_fee("x");  //金額需要擴大100倍:1代表支付時是0.01  
    15.     unifiedOrderRequest.setSpbill_create_ip("xxxxxxxxxxxxx");//終端IP  
    16.     unifiedOrderRequest.setNotify_url("xxxxxxxxxxxxxx");//通知地址  
    17.     unifiedOrderRequest.setTrade_type("NATIVE");//JSAPI--公衆號支付、NATIVE--原生掃碼支付、APP--app支付  
    18.     unifiedOrderRequest.setSign(createSign(unifiedOrderRequest));//簽名<span style="color:#ff0000;"><strong>說明5(見文末,簽名方法一併給出)</strong></span>  
    19.     //將訂單對象轉爲xml格式  
    20.     XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-""_"))); //<span style="color:#ff0000;"><strong>說明3(見文末)</strong></span>  
    21.     xStream.alias("xml", UnifiedOrderRequest.class);//根元素名需要是xml  
    22.     return xStream.toXML(unifiedOrderRequest);  
    23. }  

  4. 調統一下單API:根據要求將生成訂單中返回的xml向微信給定的統一下單URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder,發送請求,成功並獲得二維碼。
    [javascript] view plain copy 派生到我的代碼片
    1. /** 
    2.  * 調統一下單API 
    3.  * @param orderInfo 
    4.  * @return 
    5.  */  
    6. private String httpOrder(String orderInfo) {  
    7.     String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";  
    8.     try {  
    9.         HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();  
    10.         //加入數據    
    11.            conn.setRequestMethod("POST");    
    12.            conn.setDoOutput(true);    
    13.                
    14.            BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());    
    15.            buffOutStr.write(orderInfo.getBytes());  
    16.            buffOutStr.flush();    
    17.            buffOutStr.close();    
    18.                
    19.            //獲取輸入流    
    20.            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));    
    21.                
    22.            String line = null;    
    23.            StringBuffer sb = new StringBuffer();    
    24.            while((line = reader.readLine())!= null){    
    25.                sb.append(line);    
    26.            }    
    27.              
    28.            XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-""_")));//說明3(見文末)  
    29.            //將請求返回的內容通過xStream轉換爲UnifiedOrderRespose對象  
    30.            xStream.alias("xml", UnifiedOrderRespose.class);  
    31.            UnifiedOrderRespose unifiedOrderRespose = (UnifiedOrderRespose) xStream.fromXML(sb.toString());  
    32.              
    33.            //根據微信文檔return_code 和result_code都爲SUCCESS的時候纔會返回code_url  
    34.            //<span style="color:#ff0000;"><strong>說明4(見文末)</strong></span>  
    35.            if(null!=unifiedOrderRespose   
    36.                 && "SUCCESS".equals(unifiedOrderRespose.getReturn_code())   
    37.                 && "SUCCESS".equals(unifiedOrderRespose.getResult_code())){  
    38.             return unifiedOrderRespose.getCode_url();  
    39.            }else{  
    40.             return null;  
    41.            }  
    42.     } catch (Exception e) {  
    43.         e.printStackTrace();  
    44.     }  
    45.     return null;  
    46. }  

  5. 將返回的支付交易鏈接生成二維碼展示:沒有異常的情況下,在頁面中使用<img>標籤接收就行。實際使用時,結合前端和業務的需求放置二維碼。可以在掃碼支付/案例及規範中找到部分素材和界面規範來設計微信風格的支付頁面。
    [javascript] view plain copy 派生到我的代碼片
    1. <img src="${ctx}/wxPay/createQRCode?orderId=1111" width="174px">  

  6. 用戶可以通過維繫客戶端進行掃碼支付。支付完成後回調我們notify_url設置的url,通過成功的回調來更改業務系統中的訂單狀態或者一些業務需求。這裏回調沒有寫出可以參考支付寶:web頁面掃碼支付、網站支付、支付寶即時到賬 + springmvc中的回調。

五、說明:

  1. 二維碼可以查看zxing實現二維碼生成和解析;微信這邊也提供了二維碼的學習,大家有興趣可以看看:http://www.thonky.com/qr-code-tutorial/ 和http://coolshell.cn/articles/10590.html

  2. 隨機字符串:微信對隨機字符串的要求是不超過32位。我這邊是這樣生成的,用時間戳。
    [javascript] view plain copy 派生到我的代碼片
    1. /** 
    2.  * 創建UUID 
    3.  * @return 
    4.  */  
    5. public static synchronized String makeUUID() {  
    6.     Date date = new Date();  
    7.     StringBuffer s = new StringBuffer(DateUtil.formatYmdhmsm(date));  
    8.     return s.append((new Random().nextInt(900) + 100)).toString();  
    9. }  



  3. 使用Xstream時,由於微信定義的變量名大部分使用了“_”,但是在Xstream中它是關鍵字,所以會自動變爲“__”,引起報錯。詳情請看:XStream異常:對象轉爲XML時,會把"_"轉成"__";報錯:(Lcom/thoughtworks/xstream/io/naming/NameCoder;)V

  4. 獲取二維碼鏈接時,只有在return_code 和result_code都爲SUCCESS的時候有返回;這裏我就簡單的滿足時返回,不滿足返回null,您寫的時候需要結合業務考慮下,是否需要增加判斷,從而滿足不同的業務場景。統一下單API

  5. 簽名在上面一直沒有詳細說明,首先查看微信的安全規範中籤名算法。key值,需要自己填寫
    [javascript] view plain copy 派生到我的代碼片
    1. /** 
    2.  * 生成簽名 
    3.  *  
    4.  * @param appid_value 
    5.  * @param mch_id_value 
    6.  * @param productId 
    7.  * @param nonce_str_value 
    8.  * @param trade_type  
    9.  * @param notify_url  
    10.  * @param spbill_create_ip  
    11.  * @param total_fee  
    12.  * @param out_trade_no  
    13.  * @return 
    14.  */  
    15. private String createSign(UnifiedOrderRequest unifiedOrderRequest) {  
    16.     //根據規則創建可排序的map集合  
    17.     SortedMap<String, String> packageParams = new TreeMap<String, String>();  
    18.     packageParams.put("appid", unifiedOrderRequest.getAppid());  
    19.     packageParams.put("body", unifiedOrderRequest.getBody());  
    20.     packageParams.put("mch_id", unifiedOrderRequest.getMch_id());  
    21.     packageParams.put("nonce_str", unifiedOrderRequest.getNonce_str());  
    22.     packageParams.put("notify_url", unifiedOrderRequest.getNotify_url());  
    23.     packageParams.put("out_trade_no", unifiedOrderRequest.getOut_trade_no());  
    24.     packageParams.put("spbill_create_ip", unifiedOrderRequest.getSpbill_create_ip());  
    25.     packageParams.put("trade_type", unifiedOrderRequest.getTrade_type());  
    26.     packageParams.put("total_fee", unifiedOrderRequest.getTotal_fee());  
    27.   
    28.     StringBuffer sb = new StringBuffer();  
    29.     Set es = packageParams.entrySet();//字典序  
    30.     Iterator it = es.iterator();  
    31.     while (it.hasNext()) {  
    32.         Map.Entry entry = (Map.Entry) it.next();  
    33.         String k = (String) entry.getKey();  
    34.         String v = (String) entry.getValue();  
    35.         //爲空不參與簽名、參數名區分大小寫  
    36.         if (null != v && !"".equals(v) && !"sign".equals(k)  
    37.                 && !"key".equals(k)) {  
    38.             sb.append(k + "=" + v + "&");  
    39.         }  
    40.     }  
    41.     //第二步拼接key,key設置路徑:微信商戶平臺(pay.weixin.qq.com)-->賬戶設置-->API安全-->密鑰設置  
    42.     sb.append("key=" +"xxxxxxxxxxxxxxxxx");  
    43.     String sign = MD5Util.MD5Encode(sb.toString(), "utf-8")  
    44.             .toUpperCase();//MD5加密  
    45.     return sign;  
    46. }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章