微信扫码Native扫码支付

  最近在负责公司的微信扫码支付,网上的扫码支付文章写的一言难尽,所以想自己写一篇心得,帮助需要的人。

  开发之前呢,必须要去看看微信的开发api,弄明白了流程剩下的就好办了。废话少说,接下来就来Coding吧 

  1. 第一步是引入依赖:Native支付除了基本的依赖之外还需要的依赖有以下这些

  

        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>javase</artifactId>
            <version>3.3.3</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.jdom</groupId>
            <artifactId>jdom</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.3.5</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.45</version>

  

  2. 创建WeChatConfig类 

  

/**
 * 微信支付配置文件
 * @author lrh
 *
 */
public class WeChatConfig {

    /**
     * 微信服务号APPID
     */
    public static String APPID="xxxxxx";
    /**
     * 微信支付的商户号
     */
    public static String MCHID="xxxxxx";
    /**
     * 微信支付的API密钥
     */
    public static String APIKEY="xxxxxx";
    /**
     * 微信支付成功之后的回调地址【注意:当前回调地址必须是公网能够访问的地址】
     * 这里我是用了natapp来对本地进行穿透,本地调试的时候记得修改
     */
    public static String WECHAT_NOTIFY_URL_PC="http://68jhuv.natappfree.cc/chevip_bms/app/zcp/wechat_notify_url_pc";
    /**
     * 微信统一下单API地址
     */
    public static String UFDODER_URL="https://api.mch.weixin.qq.com/pay/unifiedorder";


}

  3. 创建微信统一下单api下的其他字段的entity-->

import lombok.Data;

/**
 * 微信支付需要的入参
 *  * @author lrh
 */
@Data
public class WeChatParams {

    public String total_fee;//订单金额:以分钱为单位
    public String body;//商品名称
    public String out_trade_no;//商户订单号
    public String attach;//附加参数

}

  4. 接下来就是根据微信的api文档,转换成对应的格式来进行HttpClient请求。工具类我放在最后面提供

import java.awt.image.BufferedImage;

import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;


import com.chevip.bms.modules.Service.wechat.config.WeChatConfig;
import com.chevip.bms.modules.Service.wechat.entity.WeChatDto;
import com.chevip.bms.modules.Service.wechat.utils.HttpClientUtil;
import com.chevip.bms.modules.Service.wechat.utils.PayForUtil;
import com.chevip.bms.modules.Service.wechat.utils.XMLUtil;
import com.chevip.framework.utils.PgsApiUtils;
import lombok.extern.slf4j.Slf4j;

import com.google.zxing.common.BitMatrix;

@Slf4j
public class WeixinPay {


    private static final int BLACK = 0xff000000;
    private static final int WHITE = 0xFFFFFFFF;

    /**
     * 获取微信支付的二维码地址
     *
     * @return
     * @throws Exception
     * @author lrh
     */
    public static String getCodeUrl(WeChatDto ps) throws Exception {
        /**
         * 账号信息
         */
        String appid = WeChatConfig.APPID;//微信服务号的appid
        String mch_id = WeChatConfig.MCHID; //微信支付商户号
        String key = WeChatConfig.APIKEY; // 微信支付的API密钥
        String notify_url = WeChatConfig.WECHAT_NOTIFY_URL_PC;//回调地址【注意,这里必须要使用外网的地址】
        String ufdoder_url = WeChatConfig.UFDODER_URL;//微信下单API地址
        String trade_type = "NATIVE"; //类型【网页扫码支付】

        /**
         * 时间字符串
         */
        String currTime = PayForUtil.getCurrTime();
        String strTime = currTime.substring(8, currTime.length());
        String strRandom = PayForUtil.buildRandom(4) + "";
        String nonce_str = strTime + strRandom;

        /**
         * 参数封装
         */
        SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
        packageParams.put("appid", appid);
        packageParams.put("mch_id", mch_id);
        packageParams.put("nonce_str", nonce_str);//随机字符串
        packageParams.put("body", ps.getBody());//支付的商品名称
        packageParams.put("out_trade_no", ps.getOut_trade_no());//交易订单号->我用的是时间戳
        packageParams.put("total_fee", ps.getTotal_fee());//支付金额
        packageParams.put("spbill_create_ip", PayForUtil.localIp());//客户端主机
        packageParams.put("notify_url", notify_url);
        packageParams.put("trade_type", trade_type);
        packageParams.put("attach", ps.getAttach());

        String sign = PayForUtil.createSign("UTF-8", packageParams, key);  //获取签名
        packageParams.put("sign", sign);

        String requestXML = PayForUtil.getRequestXml(packageParams);//将请求参数转换成String类型
        log.info("微信支付请求参数的报文" + requestXML);
        String resXml = HttpClientUtil.postData(ufdoder_url, requestXML);  //解析请求之后的xml参数并且转换成String类型
        Map map = XMLUtil.doXMLParse(resXml);
        log.info("微信支付响应参数的报文" + resXml);
        String urlCode = (String) map.get("code_url");

        return urlCode;
    }

}

  上面的代码请求返回的是url_code ,其中需要将字段转换成微信所需要的xml格式。

  微信是不支持直接返回二维码图片的 ,所以需要将url_code使用zxing转换为二维码图片

  5.工具类提供:

  

package com.chevip.bms.modules.Service.wechat.utils;

import com.alibaba.fastjson.JSON;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.io.IOException;

/**
 * @author lrh
 */
public class HttpClientUtil {
    public static String postData(String url, String data) {
        //创建HttpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //创建response对象
        CloseableHttpResponse response = null;
        //创建POST请求
        HttpPost httpPost = new HttpPost(url);
        //将data转换为JSON对象
        StringEntity stringEntity = new StringEntity(JSON.toJSONString(data), "UTF-8");
        //将JSON对象保存到Entity中
        httpPost.setEntity(stringEntity);
        //设置请求头
        httpPost.setHeader("Content-Type", "application/json;charset=utf-8");
        try {
            response = httpClient.execute(httpPost);
            if (response != null) {
                return EntityUtils.toString(response.getEntity());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if (httpClient != null) {
                    httpClient.close();
                }
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

  

package com.chevip.bms.modules.Service.wechat.utils;

import com.alibaba.fastjson.JSON;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.io.IOException;

/**
 * @author lrh
 */
public class HttpClientUtil {
    public static String postData(String url, String data) {
        //创建HttpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //创建response对象
        CloseableHttpResponse response = null;
        //创建POST请求
        HttpPost httpPost = new HttpPost(url);
        //将data转换为JSON对象
        StringEntity stringEntity = new StringEntity(JSON.toJSONString(data), "UTF-8");
        //将JSON对象保存到Entity中
        httpPost.setEntity(stringEntity);
        //设置请求头
        httpPost.setHeader("Content-Type", "application/json;charset=utf-8");
        try {
            response = httpClient.execute(httpPost);
            if (response != null) {
                return EntityUtils.toString(response.getEntity());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if (httpClient != null) {
                    httpClient.close();
                }
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

  

package com.chevip.bms.modules.Service.wechat.utils;

import lombok.extern.slf4j.Slf4j;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

/**
 * 支付通用公共类
 *  * @author lrh
 */

@Slf4j
public class PayForUtil {


    /**
     * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
     * @return boolean
     */
    public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
        StringBuffer sb = new StringBuffer();
        Set es = packageParams.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            String v = (String)entry.getValue();
            if(!"sign".equals(k) && null != v && !"".equals(v)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + API_KEY);

        //算出摘要
        String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
        String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();

        return tenpaySign.equals(mysign);
    }

    /**
     * @author chenp
     * @Description:sign签名
     * @param characterEncoding
     *            编码格式
     * @param
     *
     * @return
     */
    public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
        StringBuffer sb = new StringBuffer();
        Set es = packageParams.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + API_KEY);
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }

    /**
     * @author chenp
     * @Description:将请求参数转换为xml格式的string
     * @param parameters
     *            请求参数
     * @return
     */
    public static String getRequestXml(SortedMap<Object, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
            } else {
                sb.append("<" + k + ">" + v + "</" + k + ">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * 取出一个指定长度大小的随机正整数.
     *
     * @param length
     *            int 设定所取出随机数的长度。length小于11
     * @return int 返回生成的随机数。
     */
    public static int buildRandom(int length) {
        int num = 1;
        double random = Math.random();
        if (random < 0.1) {
            random = random + 0.1;
        }
        for (int i = 0; i < length; i++) {
            num = num * 10;
        }
        return (int) ((random * num));
    }

    /**
     * 获取当前时间 yyyyMMddHHmmss
     *  @author chenp
     * @return String
     */
    public static String getCurrTime() {
        Date now = new Date();
        SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        String s = outFormat.format(now);
        return s;
    }
    /**
     * 获取本机IP地址
     * @author chenp
     * @return
     */
    public static 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) {
            log.warn("获取本机Ip失败:异常信息:"+e.getMessage());
        }
        return ip;
    }

}

  

package com.chevip.bms.modules.Service.wechat.utils;

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.apache.commons.lang.StringUtils;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class QRUtil {

    private static final int WIDTH = 222; // 二维码宽
    private static final int HEIGHT = 222; // 二维码高

    private static final int WORDHEIGHT = 235; // 加文字二维码高
    /**
     * 生成二维码
     */
    public static String QREncode(String content, HttpServletResponse response ,String fee,String sLicense,String typeName ) throws WriterException, IOException {
        int width = 200; // 图像宽度
        int height = 200; // 图像高度


        double money = Double.parseDouble(fee) / 100;

        Map<EncodeHintType, Object> hints = new HashMap<>();
        //内容编码格式
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        // 指定纠错等级
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        //设置二维码边的空度,非负数
        hints.put(EncodeHintType.MARGIN, 1);
        BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
      //  MatrixToImageWriter.writeToPath(bitMatrix, format, new File("C:\\pic\\zxing.gif").toPath());// 输出原图片
        MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF);

        BufferedImage bufferedImage = LogoMatrix(MatrixToImageWriter.toBufferedImage(bitMatrix,matrixToImageConfig), new File("C:\\pic\\icon_weipu.png"));
//        BufferedImage bufferedImage = LogoMatrix(toBufferedImage(bitMatrix), new File("D:\\logo.png"));
//        ImageIO.write(bufferedImage, "gif", new File("C:\\pic\\"+out_trade_no+".gif"));//输出带logo图片


        BufferedImage image = insertWords(bufferedImage, sLicense+typeName+"金额为"+money+"元");

        System.out.println("输出成功.");

        ImageIO.write(image, "png", response.getOutputStream());

        //图片转换为base64格式
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(image, "png", os);
        String base64img = safeUrlBase64Encode(os.toByteArray());
//        org.apache.commons.codec.binary.Base64.encodeBase64String(os.toByteArray());

        // Base64 base64 = new Base64();

      //  String base64img = new String(base64.encode(bytes));

        return base64img;

    }
    public static String safeUrlBase64Encode(byte[] data){
        String encodeBase64 = org.apache.commons.codec.binary.Base64.encodeBase64String(data);
        String safeBase64Str = encodeBase64.replace('+', '-');
        safeBase64Str = safeBase64Str.replace('/', '_');
//        safeBase64Str = safeBase64Str.replaceAll("=", "");
        return safeBase64Str;
    }

    /**
     * 识别二维码
     */
    public static void QRReader(File file) throws IOException, NotFoundException {
        MultiFormatReader formatReader = new MultiFormatReader();
        //读取指定的二维码文件
        BufferedImage bufferedImage =ImageIO.read(file);
        BinaryBitmap binaryBitmap= new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(bufferedImage)));
        //定义二维码参数
        Map  hints= new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        Result result = formatReader.decode(binaryBitmap, hints);
        //输出相关的二维码信息
        System.out.println("解析结果:"+result.toString());
        System.out.println("二维码格式类型:"+result.getBarcodeFormat());
        System.out.println("二维码文本内容:"+result.getText());
        bufferedImage.flush();
    }

    /**
     * 二维码添加logo
     * @param matrixImage 源二维码图片
     * @param logoFile logo图片
     * @return 返回带有logo的二维码图片
     */
    public static BufferedImage LogoMatrix(BufferedImage matrixImage, File logoFile) throws IOException{
        /**
         * 读取二维码图片,并构建绘图对象
         */
        Graphics2D g2 = matrixImage.createGraphics();

        int matrixWidth = matrixImage.getWidth();
        int matrixHeigh = matrixImage.getHeight();

        /**
         * 读取Logo图片
         */
        BufferedImage logo = ImageIO.read(logoFile);

        //开始绘制图片
        g2.drawImage(logo,matrixWidth/5*2,matrixHeigh/5*2, matrixWidth/5, matrixHeigh/5, null);//绘制
        BasicStroke stroke = new BasicStroke(5,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
        g2.setStroke(stroke);// 设置笔画对象
        //指定弧度的圆角矩形
        RoundRectangle2D.Float round = new RoundRectangle2D.Float(matrixWidth/5*2, matrixHeigh/5*2, matrixWidth/5, matrixHeigh/5,20,20);
        g2.setColor(Color.white);
        g2.draw(round);// 绘制圆弧矩形

        //设置logo 有一道灰色边框
        BasicStroke stroke2 = new BasicStroke(1,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
        g2.setStroke(stroke2);// 设置笔画对象
        RoundRectangle2D.Float round2 = new RoundRectangle2D.Float(matrixWidth/5*2+2, matrixHeigh/5*2+2, matrixWidth/5-4, matrixHeigh/5-4,20,20);
        g2.setColor(new Color(128,128,128));
        g2.draw(round2);// 绘制圆弧矩形

        g2.dispose();
        matrixImage.flush() ;
        return matrixImage ;
    }

    /**
     * 把带logo的二维码下面加上文字
     * @param image
     * @param words
     * @return
     */
    private static BufferedImage insertWords(BufferedImage image,String words){
        // 新的图片,把带logo的二维码下面加上文字
        if (StringUtils.isNotEmpty(words)) {

            //创建一个带透明色的BufferedImage对象
            BufferedImage outImage = new BufferedImage(WIDTH, WORDHEIGHT, BufferedImage.TYPE_INT_ARGB);
            Graphics2D outg = outImage.createGraphics();
            setGraphics2D(outg);

            // 画二维码到新的面板
            outg.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
            // 画文字到新的面板
            Color color=new Color(183,183,183);
            outg.setColor(color);
            // 字体、字型、字号
            outg.setFont(new Font("微软雅黑", Font.PLAIN, 14));
            //文字长度
            int strWidth = outg.getFontMetrics().stringWidth(words);
            //总长度减去文字长度的一半  (居中显示)
            int wordStartX=(WIDTH - strWidth) / 2;
            //height + (outImage.getHeight() - height) / 2 + 12
            int wordStartY=HEIGHT+10;
            // 画文字
            outg.drawString(words, wordStartX, wordStartY);
            outg.dispose();
            outImage.flush();
            return outImage;

        }
        return null;
    }

    /**
     * 设置 Graphics2D 属性  (抗锯齿)
     * @param graphics2D
     */
    private static void setGraphics2D(Graphics2D graphics2D){
        graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics2D.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT);
        Stroke s = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
        graphics2D.setStroke(s);
    }
}

  

package com.chevip.bms.modules.Service.wechat.utils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;


public class XMLUtil {

    /**
     * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
     * @param strxml
     * @return
     * @throws JDOMException
     * @throws IOException
     */
    public static Map doXMLParse(String strxml) throws JDOMException, IOException {
        strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");

        if(null == strxml || "".equals(strxml)) {
            return null;
        }

        Map m = new HashMap();

        InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        Iterator it = list.iterator();
        while(it.hasNext()) {
            Element e = (Element) it.next();
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if(children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = XMLUtil.getChildrenText(children);
            }

            m.put(k, v);
        }

        //关闭流
        in.close();

        return m;
    }

    /**
     * 获取子结点的xml
     * @param children
     * @return String
     */
    public static String getChildrenText(List children) {
        StringBuffer sb = new StringBuffer();
        if(!children.isEmpty()) {
            Iterator it = children.iterator();
            while(it.hasNext()) {
                Element e = (Element) it.next();
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<" + name + ">");
                if(!list.isEmpty()) {
                    sb.append(XMLUtil.getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }

        return sb.toString();
    }

}

   6. 最后是回调,微信的回调是直接Post我们的接口,所以必须要公网能访问到的接口。

    本地测试的时候可以用内网穿透软件

    

package com.chevip.bms.modules.Service.wechat.service;

import com.chevip.auction_order.service.ServiceOrderService;
import com.chevip.bms.modules.Service.wechat.config.WeChatConfig;
import com.chevip.bms.modules.Service.wechat.dao.WeChatDao;
import com.chevip.bms.modules.Service.wechat.entity.WeChatResultEntity;
import com.chevip.bms.modules.Service.wechat.utils.PayForUtil;
import com.chevip.bms.modules.Service.wechat.utils.XMLUtil;
import lombok.extern.slf4j.Slf4j;
import org.jdom.JDOMException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;

/**
 * 微信回调,通过流进行IO
 *  * @author lrh
 */
@Slf4j
@Service
public class NotifyService {

    @Autowired
    ServiceOrderService serviceOrderService;
    @Autowired
    WeChatDao weChatDao;
    @Transactional
    public void wxnotify(HttpServletRequest request, HttpServletResponse response) throws IOException, JDOMException {

//        InputStream inputStream ;
//        获取前端传来的请求参数
//        inputStream = request.getInputStream();
        BufferedReader in = request.getReader();
//        将字节流转化成字符流
//        BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        StringBuffer sb = new StringBuffer();

        String s;
        while ((s = in.readLine()) != null) {
            sb.append(s);
        }
        in.close();
//        inputStream.close();

        //解析xml成map
        Map<String, String> m = new HashMap<String, String>();
        m = XMLUtil.doXMLParse(sb.toString());

        //过滤空 设置 TreeMap
        SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
        Iterator<String> it = m.keySet().iterator();
        while (it.hasNext()) {
            String parameter = it.next();
            String parameterValue = m.get(parameter);

            String v = "";
            if (null != parameterValue) {
                v = parameterValue.trim();
            }
            packageParams.put(parameter, v);
        }
        // 微信支付的API密钥
        String key = WeChatConfig.APIKEY; // key

        log.info("微信支付返回回来的参数:" + packageParams);
        //判断签名是否正确
        if (PayForUtil.isTenpaySign("UTF-8", packageParams, key)) {

            String resXml = "";
            if ("SUCCESS".equals((String) packageParams.get("result_code"))) {

                String app_id = (String) packageParams.get("appid");
                String mch_id = (String) packageParams.get("mch_id");
                String openid = (String) packageParams.get("openid");
                String is_subscribe = (String) packageParams.get("is_subscribe");//是否关注公众号

                //商户订单号
                String out_trade_no = (String) packageParams.get("out_trade_no");
                //付款金额【以分为单位】
                String total_fee = (String) packageParams.get("total_fee");
                //微信生成的交易订单号
                String transaction_id = (String) packageParams.get("transaction_id");//微信支付订单号
                //支付完成时间
                String time_end = (String) packageParams.get("time_end");
                String attach = (String) packageParams.get("attach");
                String result_code = (String) packageParams.get("result_code");

          // 处理自己的回调业务
          。。。//  保存微信支付订单
          。。。//  微信支付成功后更改支付状态
          。。。

                log.info("支付成功");
                //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";

            } else {
                log.info("支付失败,错误信息:" + packageParams.get("err_code"));
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
            }
            //------------------------------
            //处理业务完毕
            //------------------------------
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
            out.write(resXml.getBytes());
            out.flush();
            out.close();
        } else {
            log.info("通知签名验证失败");
        }


    }
}

  

猜你喜欢

转载自www.cnblogs.com/Leslie-/p/11859271.html