• 微信支付之扫码支付(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("
    ");
    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="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 }

    然后生成的图片,通过微信扫码就可以发起支付了。

    支付成功后,微信会调用,你之前设置的回调函数地址。并且会把你之前传给微信的商户自定义参数带给你,以帮助商户完成余下业务流程。

  • 相关阅读:
    Protected和Default的区别
    将数组中负数放在正数前面
    java.io包和杯子测楼
    hadoop基础
    极限编程和JUnit
    接口和抽象类
    C# 中窗口AutoScaleMode属性
    计算机的自启动管理
    labview中的移位寄存器、循环隧道,自动索引隧道的区别
    发现C#winform编程中不常用的控件(一)<FlowLayoutPanel控件><拆分器控件Splitcontainer >
  • 原文地址:https://www.cnblogs.com/clcliangcheng/p/4631098.html
Copyright © 2020-2023  润新知