微信支付之掃碼支付(java版 native原生支付)


本文直接從代碼調用微信掃碼支付講起。賬號配置,參數生成等請參考官方文檔:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1

友情提示:沒讀過微信官方文檔的就不用往下看了。

微信掃碼支付。簡單來說,就是你把微信支付需要的信息,生成到二維碼圖片中。通過微信掃一掃,發起支付。我們需要做的就是二件事:

  一是:按照微信掃碼支付規則生成二維碼信息.

  二是:微信沒有提供生成二維碼圖片的接口。需要我們自己把二維碼信息生成到二維碼圖片中。

1.模式選擇:

微信掃碼支付,有兩種模式,文檔中有介紹。第二種模式,微信接口會返回二維碼信息給我們。而第一種模式則需要我們自己去生成二維碼信息。會有些麻煩。尤其是參數大小寫,還有簽名的問題,容易出錯。總的來說第二種模式比第一種模式簡單。因此我采用的是第二種模式,比較通用。京東與攜程亦用的是第二種模式。

2.調用統一下單接口獲取帶有二維碼信息的url:(模式二)

模式二的微信掃碼支付,需要先調用微信的統一下單接口,生成預交易單。(參數傳遞與接收都是XML 數據格式。)

正確調用后,會返回含有交易標示ID,和二維碼鏈接的URL。

HashMap<String, String> paramMap = Maps.newHashMap(); 
paramMap.put("trade_type", "NATIVE"); //交易類型
paramMap.put("spbill_create_ip",localIp()); //本機的Ip
paramMap.put("product_id", payOrderIdsStr); // 商戶根據自己業務傳遞的參數 必填
paramMap.put("body", orderSubject);         //描述
paramMap.put("out_trade_no", payOrderIdsStr); //商戶 后台的貿易單號
paramMap.put("total_fee", "" + totalCount); //金額必須為整數  單位為分
paramMap.put("notify_url", "http://" + getAccessDomain() + "/wx_pay_notify"); //支付成功后,回調地址     
paramMap.put("appid", siteConfig.getWxPayAppId()); //appid
paramMap.put("mch_id", siteConfig.getWxPayMchId()); //商戶號      
paramMap.put("nonce_str", CommonUtilPub.createNoncestr(32));  //隨機數  
paramMap.put("sign",CommonUtilPub.getSign(paramMap,siteConfig.getWxPayPartnerKey()));//根據微信簽名規則,生成簽名 
String xmlData = CommonUtilPub.mapToXml(paramMap);//把參數轉換成XML數據格式
 1 /**
 2      * 獲取本機Ip 
 3      *  
 4      *  通過 獲取系統所有的networkInterface網絡接口 然后遍歷 每個網絡下的InterfaceAddress組。
 5      *  獲得符合 <code>InetAddress instanceof Inet4Address</code> 條件的一個IpV4地址
 6      * @return
 7      */
 8     @SuppressWarnings("rawtypes")
 9     private String localIp(){
10         String ip = null;
11         Enumeration allNetInterfaces;
12         try {
13             allNetInterfaces = NetworkInterface.getNetworkInterfaces();            
14             while (allNetInterfaces.hasMoreElements()) {
15                 NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
16                 List<InterfaceAddress> InterfaceAddress = netInterface.getInterfaceAddresses();
17                 for (InterfaceAddress add : InterfaceAddress) {
18                     InetAddress Ip = add.getAddress();
19                     if (Ip != null && Ip instanceof Inet4Address) {
20                         ip = Ip.getHostAddress();
21                     }
22                 }
23             }
24         } catch (SocketException e) {
25             // TODO Auto-generated catch block        
26             logger.warn("獲取本機Ip失敗:異常信息:"+e.getMessage());
27         }
28         return ip;
29     }

 

 成功時返回的XML數據為:

 1 <xml><return_code><![CDATA[SUCCESS]]></return_code>
 2 <return_msg><![CDATA[OK]]></return_msg>
 3 <appid><![CDATA[wx49342bda0ef105dd]]></appid>
 4 <mch_id><![CDATA[10019460]]></mch_id>
 5 <nonce_str><![CDATA[UneMQd4qWQd0hJ4L]]></nonce_str>
 6 <sign><![CDATA[C621A9C586C1F0397D4C6B8003E0CBCE]]></sign>
 7 <result_code><![CDATA[SUCCESS]]></result_code>
 8 <prepay_id><![CDATA[wx2015070818251790742fea5e0865034508]]></prepay_id>
 9 <trade_type><![CDATA[NATIVE]]></trade_type>
10 <code_url><![CDATA[weixin://wxpay/bizpayurl?pr=AOFEsxf]]></code_url>
11 </xml>

解析XML 獲取 code_url:

 1 String resXml = HtmlUtil.postData("https://api.mch.weixin.qq.com/pay/unifiedorder", xmlData);
 2 Document dd = null;
 3 String code_url=null;
 4 try {
 5     dd = DocumentHelper.parseText(resXml);
 6   } catch (DocumentException e) {
 7        return ""; 
 8 }
 9 if (dd != null) {
10     Element root = dd.getRootElement();
11     if (root == null) {
12     return "";
13     }
14     Element codeUrl = root.element("code_url");
15     if (codeUrl == null) {
16     return "";
17     }  
18     code_url = codeUrl.getText();  //解析 xml 獲得 code_url
19 }

 postData方法:

 1     private static Logger logger_ = LoggerFactory.getLogger(HtmlUtil.class);
 2 
 3     private final static int CONNECT_TIMEOUT = 5000; // in milliseconds
 4     private final static String DEFAULT_ENCODING = "UTF-8";
 5     
 6     public static String postData(String urlStr, String data){
 7         return postData(urlStr, data, null);
 8     }
 9     
10     public static String postData(String urlStr, String data, String contentType) {
11         BufferedReader reader = null;
12         try {
13             URL url = new URL(urlStr);
14             URLConnection conn = url.openConnection();
15             conn.setDoOutput(true);
16             conn.setConnectTimeout(CONNECT_TIMEOUT);
17             conn.setReadTimeout(CONNECT_TIMEOUT);
18             if(StringUtils.isNotBlank(contentType))
19                 conn.setRequestProperty("content-type", contentType);
20             OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);
21             if(data == null)
22                 data = "";
23             writer.write(data); 
24             writer.flush();
25             writer.close();  
26 
27             reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));
28             StringBuilder sb = new StringBuilder();
29             String line = null;
30             while ((line = reader.readLine()) != null) {
31                 sb.append(line);
32                 sb.append("\r\n");
33             }
34             return sb.toString();
35         } catch (IOException e) {
36             logger_.error("Error connecting to " + urlStr + ": " + e.getMessage());
37         } finally {
38             try {
39                 if (reader != null)
40                     reader.close();
41             } catch (IOException e) {
42             }
43         }
44         return null;
45     }

 

3.動態生成二維碼圖片

使用的是google ZXing庫。 提供一個 jar 地址 直接引入到自己項目即可。http://download.csdn.net/detail/gonwy/7658135  

 頁面代碼:

<img src="qr_code.img?code_url= <#if code_url??>${code_url}</#if>" style="width:300px;height:300px;"/>

java 代碼:

/**
   * 生成二維碼圖片並直接以流的形式輸出到頁面
   * @param code_url
   * @param response
   */
  @RequestMapping("qr_code.img")
  @ResponseBody
  public void getQRCode(String code_url,HttpServletResponse response){
	GenerateQrCodeUtil.encodeQrcode(code_url, response);
  }
 1 /**
 2      * 生成二維碼圖片 不存儲 直接以流的形式輸出到頁面
 3      * @param content
 4      * @param response
 5      */
 6     @SuppressWarnings({ "unchecked", "rawtypes" })
 7     public static void encodeQrcode(String content,HttpServletResponse response){
 8         if(StringUtils.isBlank(content))
 9             return;
10         MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
11         Map hints = new HashMap();
12         hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); //設置字符集編碼類型
13         BitMatrix bitMatrix = null;
14         try {
15             bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, 300, 300,hints);
16             BufferedImage image = toBufferedImage(bitMatrix);
17             //輸出二維碼圖片流
18             try {
19                 ImageIO.write(image, "png", response.getOutputStream());
20             } catch (IOException e) {
21                 // TODO Auto-generated catch block
22                 e.printStackTrace();
23             }
24         } catch (WriterException e1) {
25             // TODO Auto-generated catch block
26             e1.printStackTrace();
27         }         
28     }

生成二維碼圖片完整代碼:(這里生成的是黑白相間的二維碼,沒有插入圖片。有興趣的,可以去研究一下)

  1 import java.awt.image.BufferedImage;   
  2 import java.io.File;
  3 import java.io.IOException;
  4 import java.util.HashMap;
  5 import java.util.Map;
  6 
  7 import javax.imageio.ImageIO;
  8 import javax.servlet.http.HttpServletResponse;
  9 
 10 import org.apache.commons.lang3.StringUtils;
 11 
 12 import com.google.zxing.BarcodeFormat;
 13 import com.google.zxing.EncodeHintType;
 14 import com.google.zxing.MultiFormatWriter;
 15 import com.google.zxing.WriterException;
 16 import com.google.zxing.common.BitMatrix;
 17 /**
 18  * 生成二維碼
 19  *2015年7月7日
 20  * @author clc
 21  *
 22  */
 23 public class GenerateQrCodeUtil {
 24     private static final int WHITE = 0xFFFFFFFF;
 25     private static final int BLACK = 0xFF000000;
 26     private static final String UPLOAD ="upload";
 27     /**
 28      * 靜態生成二維碼 存儲在磁盤上
 29      * @param content  //二維碼信息
 30      * @param contextPath //上下文相對路徑
 31      * @param realPath    //磁盤真實路徑
 32      * @param subPath     //子路徑
 33      * @return
 34      */
 35     @SuppressWarnings({ "rawtypes", "unchecked" })
 36     public static String generateQrcode(String content,String contextPath,String realPath,String subPath){
 37         if(content==null || realPath==null)
 38             return null;
 39         String fileName = generateFileName(content.getBytes())+".png";
 40         String url = "/" + UPLOAD + contextPath + "/" + subPath + "/" + fileName;//圖片在項目中存儲的相對路徑
 41         String filePath = url;
 42         //如果是部署在服務器上的情況,則需要到webapps/下面的upload目錄
 43         if (StringUtils.isNotBlank(contextPath) || realPath.endsWith("root")) {    
 44             filePath = ".." + url;
 45         }
 46         MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
 47         Map hints = new HashMap();
 48         hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); //設置字符集編碼類型
 49         BitMatrix bitMatrix = null;
 50         try {
 51             bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, 300, 300,hints);
 52             File file1 = new File(realPath,filePath); //創建存儲圖片的文件
 53             try {
 54                 GenerateQrCodeUtil.writeToFile(bitMatrix, "png", file1); //存儲二維碼圖片
 55                 return filePath;
 56             } catch (IOException e) {
 57                 // TODO Auto-generated catch block
 58                 e.printStackTrace();
 59             }
 60         } catch (WriterException e1) {
 61             // TODO Auto-generated catch block
 62             e1.printStackTrace();
 63         }         
 64         return null;
 65     }
 66     private static void writeToFile(BitMatrix matrix, String format, File file) throws IOException {
 67         BufferedImage image = toBufferedImage(matrix);
 68         if (!ImageIO.write(image, format, file)) {
 69             throw new IOException("Could not write an image of format " + format + " to " + file);
 70         }
 71     }
 72     private static BufferedImage toBufferedImage(BitMatrix matrix) {
 73          int width = matrix.getWidth();
 74          int height = matrix.getHeight();
 75          BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
 76          for (int x = 0; x < width; x++) {
 77            for (int y = 0; y < height; y++) {
 78              image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
 79            }
 80          }
 81          return image;
 82     }    
 83     private static String generateFileName(byte[] content) {
 84         return CryptUtil.md5(content);  //md5加密
 85     }
 86     
 87     /**
 88      * 生成二維碼圖片 不存儲 直接以流的形式輸出到頁面
 89      * @param content
 90      * @param response
 91      */
 92     @SuppressWarnings({ "unchecked", "rawtypes" })
 93     public static void encodeQrcode(String content,HttpServletResponse response){
 94         if(StringUtils.isBlank(content))
 95             return;
 96         MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
 97         Map hints = new HashMap();
 98         hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); //設置字符集編碼類型
 99         BitMatrix bitMatrix = null;
100         try {
101             bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, 300, 300,hints);
102             BufferedImage image = toBufferedImage(bitMatrix);
103             //輸出二維碼圖片流
104             try {
105                 ImageIO.write(image, "png", response.getOutputStream());
106             } catch (IOException e) {
107                 // TODO Auto-generated catch block
108                 e.printStackTrace();
109             }
110         } catch (WriterException e1) {
111             // TODO Auto-generated catch block
112             e1.printStackTrace();
113         }         
114     }
115 }

 

然后生成的圖片,通過微信掃碼就可以發起支付了。

支付成功后,微信會調用,你之前設置的回調函數地址。並且會把你之前傳給微信的商戶自定義參數帶給你,以幫助商戶完成余下業務流程。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM