关于准备工作,就“微信扫码支付模式二”官方文档地址在这https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
对于“微信扫码支付模式二”(支付与回调)实际只会用到APP_ID、MCH_ID和API_KEY,其他的都不用。

自己用springBoot搭建一个简单的web项目,把下面代码放在项目里跑就行了,不需要额外的jar包。由于是自己测试用的,代码是不是很简洁。1.WechatPay类是自定义对象,用于传参数的,可以去掉。2.微信配置参数更换一下,把“x x x x x x”换掉就可以了。

java 微信对账单返回值接收 java微信支付回调接口_微信


返回二维码

java 微信对账单返回值接收 java微信支付回调接口_微信支付模式二_02

**一,**不用sdk和jar包,直接是调微信的api。
前端调第一个接口,返回二维码,要是前端自己生成二维码,返回urlCode就可以了。用户扫码支付成功,微信会调第二个接口。前端循环调第三个接口,判断用户是否支付。

(1)主要的业务代码

package com.controller;

import com.domain.WechatPay;
import com.sun.org.apache.regexp.internal.RE;
import com.util.*;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;

/**
 * @author zhubin
 * @version 0.1
 * @Pacakage com.controller
 * @Description 微信支付
 * @create 2019/12/04 11:17
 **/
@RestController
@RequestMapping("/wechat")
public class WechatPayController {

    @PostMapping("/getPayQRcode")
    public void getWechatPayQRcode(@RequestBody WechatPay wechatPay, HttpServletRequest request, HttpServletResponse response){

        //实际开发,这三个参数可以由前端传,或从数据库查询
        // 价格   注意:价格的单位是分
        String order_price = "1";
        // 商品描述
        String body = "为科技充值";
        // 商户订单号
        String out_trade_no = "113389054";


        String currTime = PayCommonUtil.getCurrTime();
        String strTime = currTime.substring(8, currTime.length());
        String strRandom = PayCommonUtil.buildRandom(4) + "";
        //随机字符串
        String nonce_str = strTime + strRandom;
        // 获取发起电脑 ip(终端ip)
        String spbill_create_ip = PayCommonUtil.getRemoteAddr(request);

        SortedMap<Object,Object> packageParams = new TreeMap<>();
        // 公众号id
        packageParams.put("appid", PayConfigUtil.APP_ID);
        //商户号
        packageParams.put("mch_id", PayConfigUtil.MCH_ID);
        //随机字符串
        packageParams.put("nonce_str", nonce_str);
        //商品描述
        packageParams.put("body", body);
        //商品订单号
        packageParams.put("out_trade_no", out_trade_no);
        //订单金额
        packageParams.put("total_fee", order_price);
        //发起者ip
        packageParams.put("spbill_create_ip", spbill_create_ip);
        // 回调接口
        packageParams.put("notify_url", PayConfigUtil.NOTIFY_URL);
        // 交易类型
        packageParams.put("trade_type", PayConfigUtil.TRADE_TYPE);
        // 签名
        String sign = PayCommonUtil.createSign("UTF-8", packageParams,PayConfigUtil.API_KEY);
        packageParams.put("sign", sign);
        String requestXML = PayCommonUtil.getRequestXml(packageParams);

        String resXml = HttpUtil.postData(PayConfigUtil.UFDODER_URL, requestXML);
        Map<String,String> map = XMLUtil.convertToMap(resXml);

        String returnCode = map.get("return_code");
        if ("FAIL".equals(returnCode)){
            String return_msg = map.get("return_msg");
            System.out.println("支付请求异常----------"+return_msg);
            return;
        }
        String resultCode = map.get("result_code");
        if ("SUCCESS".equals(returnCode) && "FAIL".equals(resultCode)){
            String errCodeDes = map.get("err_code_des");
            System.out.println("业务异常-----------"+errCodeDes);
            return;
        }

        String urlCode = map.get("code_url");

        BufferedImage bufferedImage = PayCommonUtil.writeInfoToJpgBuff(urlCode);
        try {
            ImageIO.write(bufferedImage, "png", response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 微信异步回调
     * @param request
     * @param response
     * @throws Exception
     */
    @PostMapping("/otify")
    public void weixin_notify(HttpServletRequest request, HttpServletResponse response) throws Exception{

        //读取参数
        InputStream inputStream ;
        StringBuffer sb = new StringBuffer();
        inputStream = request.getInputStream();
        String s ;
        BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        while ((s = in.readLine()) != null){
            sb.append(s);
        }
        in.close();
        inputStream.close();

        //解析xml成map
        Map<String, String> m = new HashMap<String, String>();
        m = XMLUtil.convertToMap(sb.toString());
        System.out.println(m);
        //过滤空 设置 TreeMap
        SortedMap<Object,Object> packageParams = new TreeMap<>();
        Iterator it = m.keySet().iterator();
        while (it.hasNext()) {
            String parameter = (String) it.next();
            String parameterValue = m.get(parameter);

            String v = "";
            if(null != parameterValue) {
                v = parameterValue.trim();
            }
            packageParams.put(parameter, v);
        }

        // 账号信息
        String key = PayConfigUtil.API_KEY;
        //logger.info(packageParams);
        //判断签名是否正确
        if(PayCommonUtil.isTenpaySign("UTF-8", packageParams,key)) {
            //------------------------------
            //处理业务开始
            //------------------------------
            String resXml = "";
            if("SUCCESS".equals(packageParams.get("result_code"))){
                // 这里是支付成功
                //执行自己的业务逻辑
                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");

                System.out.println("mch_id:"+mch_id);
                System.out.println("openid:"+openid);
                System.out.println("is_subscribe:"+is_subscribe);
                System.out.println("out_trade_no:"+out_trade_no);
                System.out.println("total_fee:"+total_fee);

                //执行自己的业务逻辑

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

            } else {
                //logger.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{
            //logger.info("通知签名验证失败");
            System.out.println("通知签名验证失败");
        }

    }

    /**
     * 订单是否注册
     * @return
     */
    @PostMapping("/ynPay")
    public String ynPayByOrderId(@PathVariable("orderId") String orderId){

        /*WechatPay wechatPay = WechatPayService.ynPayByOrderId(orderId);
        if (null != wechatPay && wechatPay.getOrderStatus() == 2){
            return "true";
        }*/
        return "false";
    }
}

(2)工具类

PayConfigUtil
把这个类里的“xxxxxxxxx”更换掉,就可以跑了

package com.util;

/**
 * @author zhubin
 * @Pacakage com.util
 * @Description
 * @create 2019/12/04 12:13
 **/
public class PayConfigUtil {

    /**
     * appid
     */
    public static final String APP_ID = "xxxxxxxxxx";

    /**
     * 商业号
     */
    public static final String MCH_ID = "xxxxxxxxxxx";

    /**
     * 交易类型
     */
    public static final String TRADE_TYPE = "NATIVE";

    /**
     * key
     */
    public static final String API_KEY = "xxxxxxxxxxxx";

    /**
     * 回调url
     */
    public static final String NOTIFY_URL = "http://xxxxxxxxx/wechat/wechatNotify";

    /**
     * 微信请求地址
     */
    public static final String UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
}

HttpUtil

package com.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;

/**
 * @author zhubin
 * @Pacakage com.util
 * @Description
 * @create 2019/12/04 13:49
 **/
public class HttpUtil {

    // in milliseconds
    private final static int CONNECT_TIMEOUT = 5000;
    private final static String DEFAULT_ENCODING = "UTF-8";

    public static String postData(String urlStr, String data){
        return postData(urlStr, data, null);
    }

    public static String postData(String urlStr, String data, String contentType){
        BufferedReader reader = null;
        try {
            URL url = new URL(urlStr);
            URLConnection conn = url.openConnection();
            conn.setDoOutput(true);
            conn.setConnectTimeout(CONNECT_TIMEOUT);
            conn.setReadTimeout(CONNECT_TIMEOUT);
            if(contentType != null){
                conn.setRequestProperty("content-type", contentType);
            }

            OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);
            if(data == null){
                data = "";
            }

            writer.write(data);
            writer.flush();
            writer.close();

            reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
                sb.append("\r\n");
            }
            return sb.toString();
        } catch (IOException e) {

        } finally {
            try {
                if (reader != null){
                    reader.close();
                }

            } catch (IOException e) {
            }
        }
        return null;
    }

}

MD5Util

package com.util;

import java.security.MessageDigest;

/**
 * @author zhubin
 * @Pacakage com.util
 * @Description
 * @create 2019/12/04 13:42
 **/
public class MD5Util {

    private static String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++)
            resultSb.append(byteToHexString(b[i]));

        return resultSb.toString();
    }

    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0){
            n += 256;
        }
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }

    public static String MD5Encode(String origin, String charsetname) {
        String resultString = null;
        try {
            resultString = new String(origin);
            MessageDigest md = MessageDigest.getInstance("MD5");
            if (charsetname == null || "".equals(charsetname)) {
                resultString = byteArrayToHexString(md.digest(resultString
                        .getBytes()));
            }else {
                resultString = byteArrayToHexString(md.digest(resultString
                        .getBytes(charsetname)));
            }
        } catch (Exception exception) {


        }
        return resultString;
    }

    private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };

}

PayCommonUtil

package com.util;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;

import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author zhubin
 * @Pacakage com.util
 * @Description
 * @create 2019/12/04 13:41
 **/
public class PayCommonUtil {

    /**
     * 是否签名正确,规则是:按参数名称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();

        //System.out.println(tenpaySign + "    " + mysign);
        return tenpaySign.equals(mysign);
    }

    /**
     * @author
     * @date 2016-4-22
     * @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
     * @date 2016-4-22
     * @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
     *
     * @return String
     */
    public static String getCurrTime() {
        Date now = new Date();
        SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        String s = outFormat.format(now);
        return s;
    }



    private static final int BLACK = 0xFF000000;
    private static final int WHITE = 0xFFFFFFFF;
    /**
       *二维码信息写成JPG BufferedImage
       * @param content 二维码信�?
       * @return JPG BufferedImage
       */
    public static BufferedImage writeInfoToJpgBuff(String content){
            BufferedImage re=null;
            MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
            Map hints = new HashMap();
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
            try {
            BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, 250, 250, hints);
            re=toBufferedImage(bitMatrix);
            } catch (Exception e) {
            e.printStackTrace();
            }

            return re;
    }

        /**
             * 根据二维矩阵的碎�? 生成对应的二维码图像缓冲
             * @param matrix  二维矩阵的碎�? 包含 宽高 行,字节
             * @see com.google.zxing.common.BitMatrix
             * @return 二维码图像缓�?
             */
     public 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;
     }

    /**
     * 获取发起者ip
     * @param request
     * @return
     */
    public static String getRemoteAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值,第一个ip才是真实ip
            if (ip.indexOf(",") != -1) {
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip.equals("127.0.0.1") || ip.equals("0:0:0:0:0:0:0:1")) {
            // 根据网卡获取本机配置的IP地址
            InetAddress inetAddress = null;
            try {
                inetAddress = InetAddress.getLocalHost();
            } catch (Exception e) {
                e.printStackTrace();
            }
            ip = inetAddress.getHostAddress();
        }
        return ip;
    }
}

XMLUtil

package com.util;

import org.w3c.dom.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author zhubin
 * @Pacakage com.util
 * @Description
 * @create 2019/12/04 13:51
 **/
public class XMLUtil {

    /**
     * xml转map
     * @param xml
     * @return
     */
    public static Map<String,String> convertToMap(String xml){
        Map<String, String> map = new LinkedHashMap<>();
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            StringReader sr = new StringReader(xml);
            InputSource is = new InputSource(sr);
            Document document = db.parse(is);

            Element root = document.getDocumentElement();
            if(root != null){
                NodeList childNodes = root.getChildNodes();
                if(childNodes != null && childNodes.getLength()>0){
                    for(int i = 0;i < childNodes.getLength();i++){
                        Node node = childNodes.item(i);
                        if( node != null && node.getNodeType() == Node.ELEMENT_NODE){
                            map.put(node.getNodeName(), node.getTextContent());
                        }
                    }
                }
            }
        } catch (DOMException e) {
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return map;
    }


}