springboot实现微信公众号支付

springboot简单实现微信公众号支付

支付dome微信公众号支付zip包
微信测试号获取方法:https://blog.csdn.net/Lc_2018/article/details/88353526
微信支付流程:
springboot实现微信公众号支付_第1张图片
详细请去参考微信公众号支付API文档
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
代码如下:

package com.example.wxpay.api;

import com.example.wxpay.sdk.WXPayUtil;
import com.example.wxpay.utils.HttpCertClientUtils;
import com.example.wxpay.utils.HttpClientUtils;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author: Administrator
 * @Date: 2019/12/23 16:03
 */
public class WXPayUtils {

    /**
     * 微信统一下单接口
     * @param notify_url     回调地址
     * @param out_trade_no   商户订单号
     * @param total_fee      订单总金额
     * @param ip             IP
     * @param body           商品内容描述
     * @param openid         微信用户 OPENID
     * @return
     * @throws Exception
     */
    public Map<String, String> wxPay(String notify_url, String out_trade_no, String total_fee, String ip, String body, String openid) throws Exception {
        Map<String, String> mp = new HashMap();
        mp.put("appid", WXPayConfig.getAppID());                        // 公众账号ID
        mp.put("mch_id", WXPayConfig.getMchID());                       // 商户号
        mp.put("nonce_str", WXPayUtil.generateNonceStr());              // 随机字符串
        mp.put("body", body);                                           // 商品描述
        mp.put("out_trade_no", out_trade_no);                           // 商户订单号
        mp.put("total_fee", total_fee);                                 // 订单总金额, 单位为分
        mp.put("spbill_create_ip", ip);                                 // 终端IP
        mp.put("notify_url", notify_url);                               // 异步接收微信支付结果通知的回调地址, 通知url必须为外网可访问的url, 不能携带参数
        mp.put("openid",openid);                                        //用户唯一标识
        mp.put("trade_type", "JSAPI");                                 // 交易类型(JSAPI公众号支付/NATIVE扫码支付/APP支付)
        mp.put("product_id", out_trade_no);                             // trade_type=NATIVE时(即扫码支付), 此参数必传. 此参数为二维码中包含的商品ID, 商户自行定义。

        String sign = WXPayUtil.generateSignature(mp, WXPayConfig.getKey());

        StringBuilder sb = new StringBuilder();
        sb.append("");
        sb.append("" + mp.get("appid") + "");
        sb.append(" + mp.get("mch_id") + "]]>");
        sb.append(" + mp.get("nonce_str") + "]]>");
        sb.append(" + mp.get("body") + "]]>");
        sb.append(" + mp.get("out_trade_no") + "]]>");
        sb.append("" + mp.get("total_fee") + "");
        sb.append(" + mp.get("spbill_create_ip") + "]]>");
        sb.append(" + mp.get("notify_url") + "]]>");
        sb.append("+mp.get("openid")+"]]>");
        sb.append(" + mp.get("trade_type") + "]]>");
        sb.append(" + mp.get("product_id") + "]]>");
        sb.append(" + sign + "]]>");
        sb.append("");

        String url = "https://api.mch.weixin.qq.com/sandboxnew/pay/unifiedorder";
        String str = HttpClientUtils.post(url, sb.toString());

        Map<String, String> retmap = new HashMap();
        try {
            retmap = WXPayUtil.xmlToMap(str);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return retmap;
    }
    /**
     * 微信查询
     * @param out_trade_no    商户订单号
     * @return
     * @throws Exception
     */
    public Map<String, String> wxOrderQuery(String out_trade_no) throws Exception {
        Map<String, String> mp = new HashMap<>();
        mp.put("appid", WXPayConfig.getAppID());                                // 公众账号ID
        mp.put("mch_id", WXPayConfig.getMchID());                               // 商户号
        // mp.put("transaction_id", transaction_id);                            // 微信订单号 (2 S 1)
        mp.put("out_trade_no", out_trade_no);                                   // 商户订单号 (2 S 1)
        mp.put("nonce_str", WXPayUtil.generateNonceStr());                      // 随机字符串
        String sign = WXPayUtil.generateSignature(mp, WXPayConfig.getKey());

        StringBuilder sb = new StringBuilder();
        sb.append("");
        sb.append("" + mp.get("appid") + "");
        sb.append(" + mp.get("mch_id") + "]]>");
        sb.append(" + mp.get("out_trade_no") + "]]>");
        sb.append("" + mp.get("nonce_str") + "");
        sb.append(" + sign + "]]>");
        sb.append("");

        String url = "https://api.mch.weixin.qq.com/pay/orderquery";
        String str = HttpClientUtils.post(url, sb.toString());

        Map<String, String> retmap = new HashMap<>();
        try {
            retmap = WXPayUtil.xmlToMap(str);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return retmap;
    }
//    订单关闭
    public Map<String, String> wxPayClose(String out_trade_no) throws Exception {
        Map<String, String> mp = new HashMap<>();
        mp.put("appid", WXPayConfig.getAppID());                                // 公众账号ID
        mp.put("mch_id", WXPayConfig.getMchID());                               // 商户号
        mp.put("out_trade_no", out_trade_no);                                   // 商户订单号 (2 S 1)
        mp.put("nonce_str", WXPayUtil.generateNonceStr());                      // 随机字符串
        String sign = WXPayUtil.generateSignature(mp, WXPayConfig.getKey());

        StringBuilder sb = new StringBuilder();
        sb.append("");
        sb.append("" + mp.get("appid") + "");
        sb.append(" + mp.get("mch_id") + "]]>");
        sb.append("" + mp.get("nonce_str") + "");
        sb.append(" + mp.get("out_trade_no") + "]]>");
        sb.append(" + sign + "]]>");
        sb.append("");

        String url = "https://api.mch.weixin.qq.com/pay/closeorder";
        String str = HttpClientUtils.post(url, sb.toString());

        Map<String, String> retmap = new HashMap<>();
        try {
            retmap = WXPayUtil.xmlToMap(str);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return retmap;

    }

    /**
     * 查询退款
     * @param out_trade_no   商户订单号
     * @return
     * @throws Exception
     */
    public Map<String, String> wxRefundQuery(String out_trade_no) throws Exception {
        Map<String, String> mp = new HashMap<>();
        mp.put("appid", WXPayConfig.getAppID());                                // 公众账号ID
        mp.put("mch_id", WXPayConfig.getMchID());                               // 商户号
        mp.put("nonce_str", WXPayUtil.generateNonceStr());                      // 随机字符串
        // mp.put("transaction_id", transaction_id);                            // 微信订单号 (4 S 1)
        // mp.put("out_refund_no", out_refund_no);                              // 商户退款单号 (4 S 1)
        // mp.put("refund_id", refund_id);                                      // 微信退款单号 (4 S 1)
        mp.put("out_trade_no", out_trade_no);                                   // 商户订单号 (4 S 1)
        String sign = WXPayUtil.generateSignature(mp, WXPayConfig.getKey());

        StringBuilder sb = new StringBuilder();
        sb.append("");
        sb.append("" + mp.get("appid") + "");
        sb.append(" + mp.get("mch_id") + "]]>");
        sb.append("" + mp.get("nonce_str") + "");
        sb.append(" + mp.get("out_trade_no") + "]]>");
        sb.append(" + sign + "]]>");
        sb.append("");

        String url = "https://api.mch.weixin.qq.com/pay/refundquery";
        String str = HttpClientUtils.post(url, sb.toString());

        Map<String, String> retmap = new HashMap<>();
        try {
            retmap = WXPayUtil.xmlToMap(str);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return retmap;
    }













    //h5pay


}
package com.example.wxpay.api;

import com.example.wxpay.sdk.WXPayConstants;
import com.example.wxpay.sdk.WXPayUtil;
import net.sf.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: Administrator
 * @Date: 2019/12/23 15:50
 * 创建一个WXPayConfig类,用于配置微信支付环境
 */

public class WXPayConfig {

    /**
     * 获取 App ID
     *
     * @return App ID
     */
    public static String getAppID() {
        return "xx";
    }

    /**
     * 获取 Mch ID  (安装证书的商户ID)
     *
     * @return Mch ID
     */
    public static String getMchID() {
        return "xx";
    }

    /**
     * 获取 API 密钥
     *
     * @return API密钥
     */
    public static String getKey() {
        return "xx";
    }

    public static final String APP_ID = "xx";     //填写开发者的APPID

    public static final String APP_SECRET = "xx";   //填写开发者的APPSECRET

    public static JSONObject doGetJson(String url) throws Exception, IOException {
        JSONObject jsonObject=null;
        //初始化httpClient
        DefaultHttpClient client=new DefaultHttpClient();
        //用Get方式进行提交
        HttpGet httpGet=new HttpGet(url);
        //发送请求
        HttpResponse response= client.execute(httpGet);
        //获取数据
        HttpEntity entity=response.getEntity();
        //格式转换
        if (entity!=null) {
            String result= EntityUtils.toString(entity,"UTF-8");
            jsonObject=JSONObject.fromObject(result);
        }
        //释放链接
        httpGet.releaseConnection();
        return jsonObject;
    }

    /**
     * 微信证书
     * @return
     */
    public static String getCert() {
        String separator = System.getProperty("file.separator");
        return WXPayConfig.getClassPath() + separator + "static" + separator + "cert" + separator +  "apiclient_cert.p12";
    }

    /**
     * 获取系统路径
     * @return
     */
    public static String getClassPath() {
        String path = Thread.currentThread().getContextClassLoader().getResource("").toString();
        String temp = path.replaceFirst("file:/", "");
        String separator = System.getProperty("file.separator");
        String resultPath = temp.replaceAll("/", separator + separator);
        return resultPath;
    }

}


package com.example.wxpay.controller;

import com.example.wxpay.api.WXPayConfig;
import com.example.wxpay.api.WXPayUtils;
import com.example.wxpay.sdk.WXPayConstants;
import com.example.wxpay.sdk.WXPayUtil;
import com.example.wxpay.utils.HttpUtil;
import com.example.wxpay.utils.ResponseUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;

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

/**
 * @Author: Administrator
 * @Date: 2020/1/5 13:32
 */
@Controller
@RequestMapping("/weixin")
public class WXPayController {
    @Value("${pays.http}")
    private String payHttp;

    private final String callBack = "/weixin/callBack";


    /**
     * @Description: 微信公众号登录授权
     * @auther: Sakura
     * @date: 2019/3/8 9:46
     * @param: [request, response]
     * @return: java.lang.String
     */
    @GetMapping(value = "/login")
    public void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 回调地址,该域名需要公众号验证,
        String backUrl = "http://2q85475y51.zicp.vip/weixin/wx";
        // 向用户申请授权
        String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WXPayConfig.APP_ID
                + "&redirect_uri=" +URLEncoder.encode(backUrl)
                + "&response_type=code"
                + "&scope=snsapi_userinfo"
                + "&state=STATE#wechat_redirect";
//        String url ="https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx5c659a84e995c4e2 &redirect_uri=http://2q85475y51.zicp.vip/wx/wx &response_type=code&scope=snsapi_login&state=NIMASIL#wechat_redirect";
        response.sendRedirect(url);
    }






    @GetMapping("/wx")
    public  String pay(HttpServletRequest request,ModelMap model) throws IOException {
        String code = request.getParameter("code");
        System.out.println(code);
        List<Object> list = accessToken(code);
        String openId=list.get(1).toString();
        System.out.println(openId);
        model.put("openId",openId);
        return "/pay";
    }
//    统一下单
    @PostMapping("/wx")
    public String paycode(ModelMap map, String orderNo, String body, Integer price,String openId) throws Exception {

        WXPayUtils wxPayUtils = new WXPayUtils();
        String url = payHttp + callBack;
        System.out.println(url);
//        http://2q85475y51.zicp.vip/weixin/callback
        System.out.println(orderNo);



        Map<String, String> rs = wxPayUtils.wxPay(url,orderNo, String.valueOf(price),"192.168.101.3", body,openId);
        String timeStamp=String.valueOf(System.currentTimeMillis() / 1000);
//        map.put("prepay_id", rs.toString());
        map.put("appId",rs.get("appid"));
        map.put("prepay_id",rs.get("prepay_id"));
        map.put("nonceStr",rs.get("nonce_str"));
        map.put("paySign",rs.get("sign"));
        map.put("timeStamp",timeStamp);
        return "paycode2" ;
    }

    //
    /**
     * 通过微信用户的code换取网页授权access_token
     * @return
     * @throws IOException
     * @throws
     */
    public List<Object> accessToken(String code) throws IOException {
        List<Object> list = new ArrayList<Object>();
        String url="https://api.weixin.qq.com/sns/oauth2/access_token?appid="+WXPayConfig.APP_ID
                + "&secret="+WXPayConfig.APP_SECRET
                + "&code="+code
                + "&grant_type=authorization_code";

        HttpClient client = new DefaultHttpClient();
        HttpPost post = new HttpPost(url);
        HttpResponse res = client.execute(post);
        if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            HttpEntity entity = res.getEntity();
            String str = EntityUtils.toString(entity, "utf-8");
            ObjectMapper mapper=new ObjectMapper();
            Map<String,Object> jsonOb=mapper.readValue(str, Map.class);
            list.add(jsonOb.get("access_token"));
            list.add(jsonOb.get("openid"));
        }
        return list;
    }
    /**
     * 支付结果通知
     * @return
     * @throws Exception
     */
    @PostMapping("/callBack")
    public void notifyOrder(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 获取request返回的值
        String retText = ResponseUtils.getRequestBody(request);
        Map<String, String> map = WXPayUtil.xmlToMap(retText);

        // 检查签名信息
        boolean sign = WXPayUtil.isSignatureValid(map, WXPayConfig.getKey());
        if (sign) {
            String wxouttradeno = map.get("out_trade_no");
            String wxappid = map.get("appid");
            String wxmchid = map.get("mch_id");
            String wxbanktype = map.get("bank_type");
            String wxcashfee = map.get("cash_fee");
            String wxtotalfee = map.get("total_fee");
            String wxissubscribe = map.get("is_subscribe");
            String wxopenid = map.get("openid");
            String wxtimeend = map.get("time_end");
            String wxtradetype = map.get("trade_type");
            String wxtransactionid = map.get("transaction_id");

            // 写入数据库
            System.out.println("===========支付回调===========");
            System.out.println("商户订单号:" + wxouttradeno);
            System.out.println("APPID:" + wxappid);
            System.out.println("商户ID:" + wxmchid);
            System.out.println("付款银行:" + wxbanktype);
            System.out.println("现金支付金额:" + wxcashfee);
            System.out.println("订单金额:" + wxtotalfee);
            System.out.println("是否关注公众账号:" + wxissubscribe);
            System.out.println("用户标识:" + wxopenid);
            System.out.println("支付完成时间:" + wxtimeend);
            System.out.println("交易类型:" + wxtradetype);  // JSAPI、NATIVE、APP
            System.out.println("微信支付订单号:" + wxtransactionid);
            // 响应完成到微信
            StringBuilder sb = new StringBuilder();
            sb.append("");
            sb.append("");
            sb.append("");
            sb.append("");

            ResponseUtils.response(response, sb.toString());
        }
    }
    //用来获取测试号的密钥
    @RequestMapping("/pay/pay")
    @ResponseBody
    public static String GetSignKey() throws Exception {
        String nonce_str = WXPayUtil.generateNonceStr();//生成随机字符
        Map<String, String> param = new HashMap<String, String>();
        param.put("mch_id", WXPayConfig.getMchID());//需要真实商户号
        param.put("nonce_str", nonce_str);//随机字符
        String sign = WXPayUtil.generateSignature(param,WXPayConfig.getKey(), WXPayConstants.SignType.MD5);//通过SDK生成签名其中API_KEY为商户对应的真实密钥
        param.put("sign", sign);
        String xml = WXPayUtil.mapToXml(param);//将map转换为xml格式
        String url = "https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey";//沙箱密钥获取api
        String SignKey = HttpUtil.postData(url, xml);//
        System.out.println("signkey+"+SignKey);
        Map<String, String> param1 = new HashMap<String, String>();
        param1 = WXPayUtil.xmlToMap(SignKey);
        String key = param1.get("sandbox_signkey");
        System.out.println(key);
        return key;
    }

//    h5pay



}

package com.example.wxpay.sdk;

import sun.net.www.http.HttpClient;

/**
 * 常量
 */
public class WXPayConstants {

    public enum SignType {
        MD5, HMACSHA256
    }

    public static final String DOMAIN_API = "api.mch.weixin.qq.com";
    public static final String DOMAIN_API2 = "api2.mch.weixin.qq.com";
    public static final String DOMAIN_APIHK = "apihk.mch.weixin.qq.com";
    public static final String DOMAIN_APIUS = "apius.mch.weixin.qq.com";


    public static final String FAIL     = "FAIL";
    public static final String SUCCESS  = "SUCCESS";
    public static final String HMACSHA256 = "HMAC-SHA256";
    public static final String MD5 = "MD5";

    public static final String FIELD_SIGN = "sign";
    public static final String FIELD_SIGN_TYPE = "sign_type";

    public static final String WXPAYSDK_VERSION = "WXPaySDK/3.0.9";
    public static final String USER_AGENT = WXPAYSDK_VERSION +
            " (" + System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version") +
            ") Java/" + System.getProperty("java.version") + " HttpClient/" + HttpClient.class.getPackage().getImplementationVersion();

    public static final String MICROPAY_URL_SUFFIX     = "/pay/micropay";
    public static final String UNIFIEDORDER_URL_SUFFIX = "/pay/unifiedorder";
    public static final String ORDERQUERY_URL_SUFFIX   = "/pay/orderquery";
    public static final String REVERSE_URL_SUFFIX      = "/secapi/pay/reverse";
    public static final String CLOSEORDER_URL_SUFFIX   = "/pay/closeorder";
    public static final String REFUND_URL_SUFFIX       = "/secapi/pay/refund";
    public static final String REFUNDQUERY_URL_SUFFIX  = "/pay/refundquery";
    public static final String DOWNLOADBILL_URL_SUFFIX = "/pay/downloadbill";
    public static final String REPORT_URL_SUFFIX       = "/payitil/report";
    public static final String SHORTURL_URL_SUFFIX     = "/tools/shorturl";
    public static final String AUTHCODETOOPENID_URL_SUFFIX = "/tools/authcodetoopenid";

    // sandbox
    public static final String SANDBOX_MICROPAY_URL_SUFFIX     = "/sandboxnew/pay/micropay";
    public static final String SANDBOX_UNIFIEDORDER_URL_SUFFIX = "/sandboxnew/pay/unifiedorder";
    public static final String SANDBOX_ORDERQUERY_URL_SUFFIX   = "/sandboxnew/pay/orderquery";
    public static final String SANDBOX_REVERSE_URL_SUFFIX      = "/sandboxnew/secapi/pay/reverse";
    public static final String SANDBOX_CLOSEORDER_URL_SUFFIX   = "/sandboxnew/pay/closeorder";
    public static final String SANDBOX_REFUND_URL_SUFFIX       = "/sandboxnew/secapi/pay/refund";
    public static final String SANDBOX_REFUNDQUERY_URL_SUFFIX  = "/sandboxnew/pay/refundquery";
    public static final String SANDBOX_DOWNLOADBILL_URL_SUFFIX = "/sandboxnew/pay/downloadbill";
    public static final String SANDBOX_REPORT_URL_SUFFIX       = "/sandboxnew/payitil/report";
    public static final String SANDBOX_SHORTURL_URL_SUFFIX     = "/sandboxnew/tools/shorturl";
    public static final String SANDBOX_AUTHCODETOOPENID_URL_SUFFIX = "/sandboxnew/tools/authcodetoopenid";

}


package com.example.wxpay.sdk;

import com.example.wxpay.sdk.WXPayConstants.SignType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.*;


public class WXPayUtil {

    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private static final Random RANDOM = new SecureRandom();

    /**
     * XML格式字符串转换为Map
     *
     * @param strXML XML字符串
     * @return XML数据转换后的Map
     * @throws Exception
     */
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
            throw ex;
        }

    }

    /**
     * 将Map转换为XML格式的字符串
     *
     * @param data Map类型数据
     * @return XML格式的字符串
     * @throws Exception
     */
    public static String mapToXml(Map<String, String> data) throws Exception {
        org.w3c.dom.Document document = WXPayXmlUtil.newDocument();
        org.w3c.dom.Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key: data.keySet()) {
            String value = data.get(key);
            if (value == null) {
                value = "";
            }
            value = value.trim();
            org.w3c.dom.Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
        try {
            writer.close();
        }
        catch (Exception ex) {
        }
        return output;
    }


    /**
     * 生成带有 sign 的 XML 格式字符串
     *
     * @param data Map类型数据
     * @param key API密钥
     * @return 含有sign字段的XML
     */
    public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
        return generateSignedXml(data, key, SignType.MD5);
    }

    /**
     * 生成带有 sign 的 XML 格式字符串
     *
     * @param data Map类型数据
     * @param key API密钥
     * @param signType 签名类型
     * @return 含有sign字段的XML
     */
    public static String generateSignedXml(final Map<String, String> data, String key, SignType signType) throws Exception {
        String sign = generateSignature(data, key, signType);
        data.put(WXPayConstants.FIELD_SIGN, sign);
        return mapToXml(data);
    }


    /**
     * 判断签名是否正确
     *
     * @param xmlStr XML格式数据
     * @param key API密钥
     * @return 签名是否正确
     * @throws Exception
     */
    public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
        Map<String, String> data = xmlToMap(xmlStr);
        if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
            return false;
        }
        String sign = data.get(WXPayConstants.FIELD_SIGN);
        return generateSignature(data, key).equals(sign);
    }

    /**
     * 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。
     *
     * @param data Map类型数据
     * @param key API密钥
     * @return 签名是否正确
     * @throws Exception
     */
        public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
        return isSignatureValid(data, key, SignType.MD5);
    }

    /**
     * 判断签名是否正确,必须包含sign字段,否则返回false。
     *
     * @param data Map类型数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名是否正确
     * @throws Exception
     */
    public static boolean isSignatureValid(Map<String, String> data, String key, SignType signType) throws Exception {
        if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
            return false;
        }
        String sign = data.get(WXPayConstants.FIELD_SIGN);
        return generateSignature(data, key, signType).equals(sign);
    }

    /**
     * 生成签名
     *
     * @param data 待签名数据
     * @param key API密钥
     * @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key) throws Exception {
        return generateSignature(data, key, SignType.MD5);
    }

    /**
     * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
     *
     * @param data 待签名数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {
        Set<String> keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(WXPayConstants.FIELD_SIGN)) {
                continue;
            }
            if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
                sb.append(k).append("=").append(data.get(k).trim()).append("&");
        }
        sb.append("key=").append(key);
        if (SignType.MD5.equals(signType)) {
            return MD5(sb.toString()).toUpperCase();
        }
        else if (SignType.HMACSHA256.equals(signType)) {
            return HMACSHA256(sb.toString(), key);
        }
        else {
            throw new Exception(String.format("Invalid sign_type: %s", signType));
        }
    }


    /**
     * 获取随机字符串 Nonce Str
     *
     * @return String 随机字符串
     */
    public static String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }


    /**
     * 生成 MD5
     *
     * @param data 待处理数据
     * @return MD5结果
     */
    public static String MD5(String data) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 生成 HMACSHA256
     * @param data 待处理数据
     * @param key 密钥
     * @return 加密结果
     * @throws Exception
     */
    public static String HMACSHA256(String data, String key) throws Exception {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 日志
     * @return
     */
    public static Logger getLogger() {
        Logger logger = LoggerFactory.getLogger("wxpay java sdk");
        return logger;
    }

    /**
     * 获取当前时间戳,单位秒
     * @return
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis()/1000;
    }

    /**
     * 获取当前时间戳,单位毫秒
     * @return
     */
    public static long getCurrentTimestampMs() {
        return System.currentTimeMillis();
    }

}

package com.example.wxpay.sdk;

import org.w3c.dom.Document;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

/**
 * 2018/7/3
 */
public final class WXPayXmlUtil {
    public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
        documentBuilderFactory.setXIncludeAware(false);
        documentBuilderFactory.setExpandEntityReferences(false);

        return documentBuilderFactory.newDocumentBuilder();
    }

    public static Document newDocument() throws ParserConfigurationException {
        return newDocumentBuilder().newDocument();
    }
}

package com.example.wxpay.utils;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Project Name: com.weixin.pay.utils
 * File Name: HttpClientUtils
 * Date: 2018-11-30 21:45
 * Copyright (c) 2018, [email protected] All Rights Reserved.
 *
 * @author Tobin
 */
public class HttpClientUtils {

    //编码格式。发送编码格式统一用UTF-8
    private static final String ENCODING = "UTF-8";

    private final static Logger logger = LoggerFactory.getLogger(HttpClientUtils.class);

    /**
     * Post JOSN
     * @param url
     * @param json
     * @return
     */
    public static String post(String url, String json) {
        logger.info("Post URL=" + url);
        logger.info("Post Data=" + json);

        StringBuffer requestText = new StringBuffer();

        CloseableHttpResponse response = null;
        CloseableHttpClient client = null;
        HttpPost httpPost = new HttpPost(url);
        StringEntity entityParams = null;
        try {
            entityParams = new StringEntity(json, "utf-8");
            httpPost.setEntity(entityParams);
            httpPost.setHeader("Content-Type", "type/json;charset=ISO-8859-1");
            client = HttpClients.createDefault();
            response = client.execute(httpPost);
            byte[] x = EntityUtils.toByteArray(response.getEntity());
            requestText.append(new String(x, "utf-8"));
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            logger.info("Request=" + requestText.toString());
        }
        return requestText.toString();
    }

    /**
     * 基于HttpClient 4.5的通用GET方法
     *
     * @param url       提交的URL
     * @return 提交响应
     */
    public static String get(String url) throws RuntimeException {
        CloseableHttpClient client = HttpClients.createDefault();
        String responseText = "";
        CloseableHttpResponse response = null;
        try {
            HttpGet get = new HttpGet(url);
            response = client.execute(get);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                responseText = EntityUtils.toString(entity, ENCODING);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (response != null)
                    response.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return responseText;
    }

}


package com.example.wxpay.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
//发起post请求,并且获取服务器返回
public class HttpUtil {
	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 coon =url.openConnection();
			HttpURLConnection coon = (HttpURLConnection) url.openConnection();
			coon.setRequestMethod("POST");
			coon.setDoOutput(true);//为true后可使用.getOutputStrean
			coon.setConnectTimeout(CONNECT_TIMEOUT);//设置连接主机超时(ms)
			coon.setReadTimeout(CONNECT_TIMEOUT);//设置从主机读取数据超时(ms)
			if(contentType!=null) {
				coon.setRequestProperty("content_type", contentType);
			}
			OutputStreamWriter writer = new OutputStreamWriter(coon.getOutputStream(),DEFAULT_ENCODING);//将写入的字符编码成字节后写入一个字节流
			if(data==null) {
				data="";
			}
			writer.write(data);
			writer.flush();
			writer.close();
			reader = new BufferedReader(new InputStreamReader(coon.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) {  
            //logger.error("Error connecting to " + urlStr + ": " + e.getMessage()); 
			e.printStackTrace();
        } finally {  
            try {  
                if (reader != null)  
                    reader.close();  
            } catch (IOException e) {  
            }  
        }  
        return null;  
	}
}

package com.example.wxpay.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * Project Name: com.weixin.pay.utils
 * File Name: ResponseUtils
 * Date: 2018-11-30 21:47
 * Copyright (c) 2018, [email protected] All Rights Reserved.
 *
 * @author Tobin
 */
public class ResponseUtils {

    private final static Logger logger = LoggerFactory.getLogger(ResponseUtils.class);

    /**
     * Response
     * @param response
     * @param responseMsg
     */
    public static void response(HttpServletResponse response, String responseMsg) {
        if (responseMsg == null)
            return;

        try {
            byte[] msgs = responseMsg.getBytes("UTF-8");

            response.setContentType("text/html;charset=UTF-8");
            response.setHeader("Cache-Control", "no-store, max-age=0, no-cache, must-revalidate");
            response.addHeader("Cache-Control", "post-check=0, pre-check=0");
            response.setHeader("Pragma", "no-cache");
            response.setDateHeader("Expires", 0);
            response.setContentLength(msgs.length);
            response.getOutputStream().write(msgs);
            response.getOutputStream().flush();
            response.getOutputStream().close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String getRequestBody(HttpServletRequest request) {
        String retText = null;
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(request.getInputStream(), "ISO-8859-1"));
            String tmp = null;
            StringBuffer htmlRet = new StringBuffer();
            while ((tmp = reader.readLine()) != null) {
                htmlRet.append(tmp).append("\n");
            }
            retText = new String(htmlRet.toString().trim().getBytes("ISO-8859-1"), "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return retText;
    }

}



<!DOCTYPE html>
<html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf8">
    <title>微信支付</title>
</head>

<body>
<h2>微信订单支付</h2>
<form action="http://2q85475y51.zicp.vip/weixin/wx" method="post">
    <div>
        <label>商户单号</label>
        <input name="orderNo" value=""/>
    </div>

    <div>
        <label>商品描述</label>
        <input name="body" value=""/>
    </div>
    <div>
        <label>openId</label>
        <input name="openId" th:value="${openId}"/>
    </div>

    <div>
        <label>商品价格</label>
        <input name="price" value=""/>
        <span style="color:red">在生产环境中,商品价格必须从数据库中读取,而不是从页面上传入参数给后台处理</span>
    </div>

    <input type="submit"/>
</form>
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>wxpay</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>wxpay</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- HTTP Client -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.5</version>
        </dependency>
        <!-- 微信退款解密 -->
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.47</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>2.12.1</version>
        </dependency>
        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib</artifactId>
            <version>2.4</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/net.sf.json-lib/json-lib -->


    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

server:
  port: 8080
pays:
  http://2q85475y51.zicp.vip/
jquery-3.3.1.js
jquery-qrcode-0.14.0.js

:备注
1.js的话自己去官网去找js官网
2.外网的话可以下载花生壳或者ngrok如果用ngrok的话注意测试后台上的域名是否一致
3.公众号因为只能在微信上使用,因此不能用postman来调试,可以去微信官网下载一个微信开发者工具来进行调试
4.如果报出scope错误可以去微信测试后台里面找到网页账号设置域名(不要http);
5.支付的价格正式的话要求数据库里查,测试的话要101,201这样的价格
6.支付账号和商户号是真实的,这个你需要花费300元去微信那里开通
7.如果还是运行不通,请在评论区说出来,如果运行的通点个赞呗.

成品展示:
springboot实现微信公众号支付_第2张图片

springboot实现微信公众号支付_第3张图片

springboot实现微信公众号支付_第4张图片


要是显示有回调的东西说明你成功了,

你可能感兴趣的:(springboot实现微信公众号支付)