gjfer 发表于 2015-07-09 10:37

微信支付之扫码支付(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数据格式/**
   * 获取本机Ip
   *
   *通过 获取系统所有的networkInterface网络接口 然后遍历 每个网络下的InterfaceAddress组。
   *获得符合 <code>InetAddress instanceof Inet4Address</code> 条件的一个IpV4地址
   * @return
   */
    @SuppressWarnings("rawtypes")
    private String localIp(){
      String ip = null;
      Enumeration allNetInterfaces;
      try {
            allNetInterfaces = NetworkInterface.getNetworkInterfaces();            
            while (allNetInterfaces.hasMoreElements()) {
                NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
                List<InterfaceAddress> InterfaceAddress = netInterface.getInterfaceAddresses();
                for (InterfaceAddress add : InterfaceAddress) {
                  InetAddress Ip = add.getAddress();
                  if (Ip != null && Ip instanceof Inet4Address) {
                        ip = Ip.getHostAddress();
                  }
                }
            }
      } catch (SocketException e) {
            // TODO Auto-generated catch block      
            logger.warn("获取本机Ip失败:异常信息:"+e.getMessage());
      }
      return ip;
    }成功时返回的XML数据为:<xml><return_code><!]></return_code>
<return_msg><!]></return_msg>
<appid><!]></appid>
<mch_id><!]></mch_id>
<nonce_str><!]></nonce_str>
<sign><!]></sign>
<result_code><!]></result_code>
<prepay_id><!]></prepay_id>
<trade_type><!]></trade_type>
<code_url><!]></code_url>
</xml>解析XML 获取 code_url:String resXml = HtmlUtil.postData("https://api.mch.weixin.qq.com/pay/unifiedorder", xmlData);
Document dd = null;
String code_url=null;
try {
    dd = DocumentHelper.parseText(resXml);
} catch (DocumentException e) {
       return "";
}
if (dd != null) {
    Element root = dd.getRootElement();
    if (root == null) {
    return "";
    }
    Element codeUrl = root.element("code_url");
    if (piEle == null) {
    return "";
    }
    code_url = codeUrl.getText();//解析 xml 获得 code_url
}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);
}/**
   * 生成二维码图片 不存储 直接以流的形式输出到页面
   * @param content
   * @param response
   */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static void encodeQrcode(String content,HttpServletResponse response){
      if(StringUtils.isBlank(content))
            return;
      MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
      Map hints = new HashMap();
      hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); //设置字符集编码类型
      BitMatrix bitMatrix = null;
      try {
            bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, 300, 300,hints);
            BufferedImage image = toBufferedImage(bitMatrix);
            //输出二维码图片流
            try {
                ImageIO.write(image, "png", response.getOutputStream());
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
      } catch (WriterException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
      }         
    }生成二维码图片完整代码:(这里生成的是黑白相间的二维码,没有插入图片。有兴趣的,可以去研究一下)import java.awt.image.BufferedImage;   
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
/**
* 生成二维码
*2015年7月7日
* @author clc
*
*/
public class GenerateQrCodeUtil {
    private static final int WHITE = 0xFFFFFFFF;
    private static final int BLACK = 0xFF000000;
    private static final String UPLOAD ="upload";
    /**
   * 静态生成二维码 存储在磁盘上
   * @param content//二维码信息
   * @param contextPath //上下文相对路径
   * @param realPath    //磁盘真实路径
   * @param subPath   //子路径
   * @return
   */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static String generateQrcode(String content,String contextPath,String realPath,String subPath){
      if(content==null || realPath==null)
            return null;
      String fileName = generateFileName(content.getBytes())+".png";
      String url = "/" + UPLOAD + contextPath + "/" + subPath + "/" + fileName;//图片在项目中存储的相对路径
      String filePath = url;
      //如果是部署在服务器上的情况,则需要到webapps/下面的upload目录
      if (StringUtils.isNotBlank(contextPath) || realPath.endsWith("root")) {   
            filePath = ".." + url;
      }
      MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
      Map hints = new HashMap();
      hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); //设置字符集编码类型
      BitMatrix bitMatrix = null;
      try {
            bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, 300, 300,hints);
            File file1 = new File(realPath,filePath); //创建存储图片的文件
            try {
                GenerateQrCodeUtil.writeToFile(bitMatrix, "png", file1); //存储二维码图片
                return filePath;
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
      } catch (WriterException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
      }         
      return null;
    }
    private static void writeToFile(BitMatrix matrix, String format, File file) throws IOException {
      BufferedImage image = toBufferedImage(matrix);
      if (!ImageIO.write(image, format, file)) {
            throw new IOException("Could not write an image of format " + format + " to " + file);
      }
    }
    private static BufferedImage toBufferedImage(BitMatrix matrix) {
         int width = matrix.getWidth();
         int height = matrix.getHeight();
         BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
         for (int x = 0; x < width; x++) {
         for (int y = 0; y < height; y++) {
             image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
         }
         }
         return image;
    }   
    private static String generateFileName(byte[] content) {
      return CryptUtil.md5(content);//md5加密
    }
   
    /**
   * 生成二维码图片 不存储 直接以流的形式输出到页面
   * @param content
   * @param response
   */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static void encodeQrcode(String content,HttpServletResponse response){
      if(StringUtils.isBlank(content))
            return;
      MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
      Map hints = new HashMap();
      hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); //设置字符集编码类型
      BitMatrix bitMatrix = null;
      try {
            bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, 300, 300,hints);
            BufferedImage image = toBufferedImage(bitMatrix);
            //输出二维码图片流
            try {
                ImageIO.write(image, "png", response.getOutputStream());
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
      } catch (WriterException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
      }         
    }
}然后生成的图片,通过微信扫码就可以发起支付了。

支付成功后,微信会调用,你之前设置的回调函数地址。并且会把你之前传给微信的商户自定义参数带给你,以帮助商户完成余下业务流程。
页: [1]
查看完整版本: 微信支付之扫码支付(java版 native原生支付)