文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

springboot中怎么对接微信支付

2023-06-20 19:10

关注

springboot中怎么对接微信支付,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

对接的完整流程如下

首先是配置

gzh.appid=公众号appidwxPay.mchId=商户号wxPay.key=支付密钥wxPay.notifyUrl=域名回调地址

常量:

    public static final String unifiedOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";      public static String SUCCESSxml = "<xml> \r\n" +            "\r\n" +            "  <return_code><![CDATA[SUCCESS]]></return_code>\r\n" +            "   <return_msg><![CDATA[OK]]></return_msg>\r\n" +            " </xml> \r\n" +            "";    public static String ERRORxml =  "<xml> \r\n" +            "\r\n" +            "  <return_code><![CDATA[FAIL]]></return_code>\r\n" +            "   <return_msg><![CDATA[invalid sign]]></return_msg>\r\n" +            " </xml> \r\n" +            "";

工具类准备:

package com.jc.utils.util; import org.apache.commons.codec.digest.DigestUtils;import org.apache.commons.lang.StringUtils;import org.apache.http.HttpEntity;import org.apache.http.client.ClientProtocolException;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpPost;import org.apache.http.conn.ssl.SSLConnectionSocketFactory;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.ssl.SSLContexts;import org.apache.http.util.EntityUtils;import org.jdom2.Document;import org.jdom2.Element;import org.jdom2.input.SAXBuilder;import org.slf4j.Logger;import org.slf4j.LoggerFactory; import javax.net.ssl.SSLContext;import javax.servlet.http.HttpServletRequest;import java.io.*;import java.net.HttpURLConnection;import java.net.URL;import java.security.KeyStore;import java.security.KeyStoreException;import java.text.SimpleDateFormat;import java.util.*; public class CommUtils {    private static Logger logger = LoggerFactory.getLogger(CommUtils.class);    // 连接超时时间,默认10秒    private static int socketTimeout = 60000;     // 传输超时时间,默认30秒    private static int connectTimeout= 60000;        public static String getRandomStringByLength(int length) {        String base = "abcdefghijklmnopqrstuvwxyz0123456789";        Random random = new Random();        StringBuffer sb = new StringBuffer();        for (int i = 0; i < length; i++) {            int number = random.nextInt(base.length());            sb.append(base.charAt(number));        }        return sb.toString();    }         public static String getOrderNo(){         SimpleDateFormat ft = new SimpleDateFormat("yyyyMMddHHmmss");        String time = ft.format(new Date());        int mathCode = (int) ((Math.random() * 9 + 1) * 10000);// 5位随机数        String resultCode = time+mathCode;        return resultCode;    }         public static String getIpAddr(HttpServletRequest request) {        String ip = request.getHeader("X-Forwarded-For");        if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {            //多次反向代理后会有多个ip值,            int index = ip.indexOf(",");            if (index != -1) {                return ip.substring(0, index);            } else {                return ip;            }        }        ip = request.getHeader("X-Real-IP");        if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {            return ip;        }        return request.getRemoteAddr();     }         public static String sign(String text, String key, String input_charset) {        text = text + "&key=" + key;        System.out.println(text);        return DigestUtils.md5Hex(getContentBytes(text, input_charset));    }         public static boolean verify(String text, String sign, String key, String input_charset) {        text = text + key;        String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));        if (mysign.equals(sign)) {            return true;        } else {            return false;        }    }        public static byte[] getContentBytes(String content, String charset) {        if (charset == null || "".equals(charset)) {            return content.getBytes();        }        try {            return content.getBytes(charset);        } catch (UnsupportedEncodingException e) {            throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);        }    }         public static String createCode(int codeLength) {        String code = "";        for (int i = 0; i < codeLength; i++) {            code += (int) (Math.random() * 9);        }        return code;    }     @SuppressWarnings("unused")    private static boolean isValidChar(char ch) {        if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))            return true;        if ((ch >= 0x4e00 && ch <= 0x7fff) || (ch >= 0x8000 && ch <= 0x952f))            return true;// 简体中文汉字编码        return false;    }         public static Map<String, String> paraFilter(Map<String, String> sArray) {        Map<String, String> result = new HashMap<>();        if (sArray == null || sArray.size() <= 0) {            return result;        }        for (String key : sArray.keySet()) {            String value = sArray.get(key);            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")                    || key.equalsIgnoreCase("sign_type")) {                continue;            }            result.put(key, value);        }        return result;    }         public static String createLinkString(Map<String, String> params) {        List<String> keys = new ArrayList<>(params.keySet());        Collections.sort(keys);        String prestr = "";        for (int i = 0; i < keys.size(); i++) {            String key = keys.get(i);            String value = params.get(key);            if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符                prestr = prestr + key + "=" + value;            } else {                prestr = prestr + key + "=" + value + "&";            }        }        return prestr;    }        public static String httpRequest(String requestUrl,String requestMethod,String outputStr){        logger.warn("请求报文:"+outputStr);        StringBuffer buffer = null;        try{            URL url = new URL(requestUrl);            HttpURLConnection conn = (HttpURLConnection) url.openConnection();            conn.setRequestMethod(requestMethod);            conn.setDoOutput(true);            conn.setDoInput(true);            conn.connect();            //往服务器端写内容            if(null !=outputStr){                OutputStream os=conn.getOutputStream();                os.write(outputStr.getBytes("utf-8"));                os.close();            }            // 读取服务器端返回的内容            InputStream is = conn.getInputStream();            InputStreamReader isr = new InputStreamReader(is, "utf-8");            BufferedReader br = new BufferedReader(isr);            buffer = new StringBuffer();            String line = null;            while ((line = br.readLine()) != null) {                buffer.append(line);            }            br.close();        }catch(Exception e){            e.printStackTrace();        }        logger.warn("返回报文:"+buffer.toString());        return buffer.toString();    }         public static String post(String url, String xmlParam,String apiclient,String mch_id) throws Exception {        logger.warn("请求报文:"+xmlParam);        StringBuilder sb = new StringBuilder();        try {            KeyStore keyStore = KeyStore.getInstance("PKCS12");            FileInputStream instream = new FileInputStream(new File(apiclient));            try {                keyStore.load(instream, mch_id.toCharArray());            } finally {                instream.close();            }            // 证书            SSLContext sslcontext = SSLContexts.custom()                    .loadKeyMaterial(keyStore, mch_id.toCharArray()).build();            // 只允许TLSv1协议            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(                    sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);            //创建基于证书的httpClient,后面要用到            CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslsf).build();            HttpPost httpPost = new HttpPost(url);//退款接口            StringEntity reqEntity = new StringEntity(xmlParam,"UTF-8");            // 设置类型            reqEntity.setContentType("application/x-www-form-urlencoded");            httpPost.setEntity(reqEntity);            CloseableHttpResponse response = client.execute(httpPost);            try {                HttpEntity entity = response.getEntity();                System.out.println(response.getStatusLine());                if (entity != null) {                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));                    String text = "";                    while ((text = bufferedReader.readLine()) != null) {                        sb.append(text);                    }                }                EntityUtils.consume(entity);             } catch (Exception e) {                e.printStackTrace();            } finally {                try {                    response.close();                } catch (IOException e) {                    e.printStackTrace();                }            }         } catch (KeyStoreException e) {            e.printStackTrace();        } catch (ClientProtocolException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } catch (Exception e) {            e.printStackTrace();        }        logger.warn("返回报文:"+sb.toString());        return sb.toString();    }     public static String urlEncodeUTF8(String source){        String result=source;        try {            result=java.net.URLEncoder.encode(source, "UTF-8");        } catch (UnsupportedEncodingException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        return result;    }        public static Map doXMLParse(String strxml) throws Exception {        if(null == strxml || "".equals(strxml)) {            return null;        }         Map m = new HashMap();        InputStream in = String2Inputstream(strxml);        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 = getChildrenText(children);            }             m.put(k, v);        }        in.close();         return m;    }         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(getChildrenText(list));                }                sb.append(value);                sb.append("</" + name + ">");            }        }         return sb.toString();    }    public static InputStream String2Inputstream(String str) {        return new ByteArrayInputStream(str.getBytes());    }    }

controller:

package com.jch.mng.controller; import com.jch.boot.component.CommonInfo;import com.jch.boot.component.Result;import com.jch.boot.component.ServiceCommonInfo;import com.jch.mng.dto.input.gzh.WxPayDto;import com.jch.mng.service.WxPayService;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse; @RestController@RequestMapping("/wxPay")public class WxPayController {         @Autowired    private WxPayService payService;          @PostMapping("/pay")    public Result<String> pay(@RequestBody WxPayDto dto, HttpServletRequest request) throws Exception {        ServiceCommonInfo<Object> result = payService.pay(dto,request);        return CommonInfo.controllerBack(result);    }          @PostMapping("/notify")    public void notify(HttpServletRequest request, HttpServletResponse response) throws Exception {        payService.notify(request,response);    } }

service接口:

package com.jch.mng.service; import com.jch.boot.component.ServiceCommonInfo;import com.jch.mng.dto.input.gzh.WxPayDto; import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse; public interface WxPayService {     ServiceCommonInfo<Object> pay(WxPayDto dto, HttpServletRequest request) throws Exception;     void notify(HttpServletRequest request, HttpServletResponse response) throws Exception;}

接口实现:

package com.jch.mng.service.impl; import com.alibaba.fastjson.JSON;import com.jc.utils.util.CommUtils;import com.jch.boot.component.ServiceCommonInfo;import com.jch.mng.constant.WeChatConstants;import com.jch.mng.dto.input.gzh.WxPayDto;import com.jch.mng.service.WxPayService;import com.jch.mng.utils.DoubleUtil;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service; import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.BufferedOutputStream;import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.HashMap;import java.util.Map; @Servicepublic class WxPayServiceImpl implements WxPayService {    public  String appId;     public  String mch_id;     public  String notify_url;     public  String key;     @Value("${gzh.appid}")    public void setAppId(String appId) {        this.appId = appId;    }    @Value("${wxPay.mchId}")    public void setMch_id(String mch_id) {        this.mch_id = mch_id;    }    @Value("${wxPay.notifyUrl}")    public void setNotify_url(String notify_url) {        this.notify_url = notify_url;    }    @Value("${wxPay.key}")    public void setKey(String key) {        this.key = key;    }      private static Logger logger = LoggerFactory.getLogger(WxPayServiceImpl.class);          @Override    public ServiceCommonInfo<Object> pay(WxPayDto dto, HttpServletRequest request) throws Exception {        logger.info("公众号微信支付, 入参:{}", JSON.toJSONString(dto));        String openid = dto.getOpenid();        String outTradeNo = dto.getOutTradeNo();        String body = dto.getBody();        Double totalFee = dto.getTotalFee();        String nonce_str = CommUtils.getRandomStringByLength(32);        String spbill_create_ip = CommUtils.getIpAddr(request);        Map<String, String> packageParams = new HashMap<>();        packageParams.put("appid", appId);        packageParams.put("mch_id",mch_id);        packageParams.put("nonce_str", nonce_str);        packageParams.put("body", body);        packageParams.put("out_trade_no", outTradeNo);        double t = DoubleUtil.parseDouble(totalFee);//保留两位小数        int aDouble = Integer.parseInt(new java.text.DecimalFormat("0").format(t*100));        packageParams.put("total_fee", aDouble+"");        packageParams.put("spbill_create_ip", spbill_create_ip);        packageParams.put("notify_url", notify_url);        packageParams.put("trade_type","JSAPI");        packageParams.put("openid", openid);         packageParams = CommUtils.paraFilter(packageParams);        String prestr = CommUtils.createLinkString(packageParams);        String sign = CommUtils.sign(prestr, key, "utf-8").toUpperCase();        logger.info("统一下单请求签名:" + sign );        String xml = "<xml version='1.0' encoding='gbk'>" + "<appid>" + appId + "</appid>"                + "<body><![CDATA[" + body + "]]></body>"                + "<mch_id>" + mch_id + "</mch_id>"                + "<nonce_str>" + nonce_str + "</nonce_str>"                + "<notify_url>" + notify_url+ "</notify_url>"                + "<openid>" + openid + "</openid>"                + "<out_trade_no>" + outTradeNo + "</out_trade_no>"                + "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>"                + "<total_fee>" + aDouble+"" + "</total_fee>"                + "<trade_type>" + "JSAPI" + "</trade_type>"                + "<sign>" + sign + "</sign>"                + "</xml>";         String result = CommUtils.httpRequest(WeChatConstants.unifiedOrderUrl, "POST", xml);        Map map = CommUtils.doXMLParse(result);        Object return_code =  map.get("return_code");        logger.info("统一下单返回return_code:" + return_code );        if(return_code == "SUCCESS"  || return_code.equals(return_code)){            Map<String,String> resultMap=new HashMap<String, String>();            String prepay_id = (String) map.get("prepay_id");            resultMap.put("appId", appId);            Long timeStamp = System.currentTimeMillis() / 1000;            resultMap.put("timeStamp", timeStamp + "");            resultMap.put("nonceStr", nonce_str);            resultMap.put("package", "prepay_id=" + prepay_id);            resultMap.put("signType", "MD5");            logger.info("参与paySign签名数据, 入参:{}", JSON.toJSONString(resultMap));            String linkString = CommUtils.createLinkString(resultMap);            String paySign = CommUtils.sign(linkString, key, "utf-8").toUpperCase();            logger.info("获取到paySign:"+paySign);            resultMap.put("paySign", paySign);            return ServiceCommonInfo.success("ok", resultMap);        }        return ServiceCommonInfo.serviceFail("支付失败", null);    }          @Override    public void notify(HttpServletRequest request, HttpServletResponse response) throws Exception {        logger.info("进入支付回调啦啦啦啦*-*");        String resXml = "";        BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream()));        String line = null;        StringBuilder sb = new StringBuilder();        while ((line = br.readLine()) != null) {            sb.append(line);        }        br.close();        String notityXml = sb.toString();        logger.info("支付回调返回数据:"+notityXml);        Map map = CommUtils.doXMLParse(notityXml);        Object returnCode = map.get("return_code");        Object result_code = map.get("result_code");        if ("SUCCESS".equals(returnCode) && "SUCCESS".equals(result_code)) {            Map<String, String> validParams = CommUtils.paraFilter(map);  //回调验签时需要去除sign和空值参数            String validStr = CommUtils.createLinkString(validParams);//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串            String sign = CommUtils.sign(validStr, key , "utf-8").toUpperCase();//拼装生成服务器端验证的签名            logger.info("支付回调生成签名:"+sign);            String transaction_id = (String) map.get("transaction_id");            String order_no = (String) map.get("out_trade_no");            String time_end = (String) map.get("time_end");            String total_fee = (String) map.get("total_fee");            //签名验证,并校验返回的订单金额是否与商户侧的订单金额一致            if (sign.equals(map.get("sign"))) {                logger.info("支付回调验签通过");                //通知微信服务器已经支付成功                resXml = WeChatConstants.SUCCESSxml;            } else {                logger.info("微信支付回调失败!签名不一致");            }        }else{            resXml = WeChatConstants.ERRORxml;        }        System.out.println(resXml);        logger.info("微信支付回调返回数据:"+resXml);        logger.info("微信支付回调数据结束");        BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());        out.write(resXml.getBytes());        out.flush();        out.close();    }}

前端页面:

springboot中怎么对接微信支付

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>Title</title>    <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;" name="viewport" /></head><body>    body: <input type="text" class="inp-body"><br>    outTradeNo: <input type="text" class="inp-outTradeNo"><br>    totalFee: <input type="text" class="inp-totalFee"><br>    openid: <input type="text" class="inp-openid"><br>    <button onclick="handleWxPay()">支付</button></body></html><script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script><script>    function handleWxPay(){        let obj = {            "body":$(".inp-body").val(),            "outTradeNo":$(".inp-outTradeNo").val(),            "totalFee":$(".inp-totalFee").val(),            "openid":$(".inp-openid").val(),        }        $.ajax({            type: "POST",            url: "微信支付接口地址",            data:JSON.stringify(obj),            beforeSend: function(request) {                request.setRequestHeader("Content-Type","application/json");            },            success: result=> {                let obj = JSON.parse(result.data)                onBridgeReady(obj)            }        });    }      function onBridgeReady(obj){        WeixinJSBridge.invoke(            'getBrandWCPayRequest', obj,            function(res){                alert(JSON.stringify(res))                if(res.err_msg == "get_brand_wcpay_request:ok" ){                    // 使用以上方式判断前端返回,微信团队郑重提示:                    //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。                }            });    }</script>

 访问前端页面记得加依赖:

<dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-thymeleaf</artifactId>        </dependency>

访问页面需要写控制类:

package com.jch.mng.controller; import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletResponse; @Controllerpublic class TestPageController {      @RequestMapping("/wxPayTest")    public String test(HttpServletResponse response)  {        return "wxPay";    }}

运行项目访问。

springboot中怎么对接微信支付

部署项目到服务器,用手机访问即可拉起支付。

附:名词解释

商户号:微信支付分配的商户号。支付审核通过后,申请人邮箱会收到腾讯下发的开户邮件, 邮件中包含商户平台的账号、密码等重要信息。

appid:商户通过微信管理后台,申请服务号、订阅号、小程序或APP应用成功之后,微信会为每个应用分配一个唯一标识id。

openid:用户在公众号内的身份标识,一旦确认,不会再变;同一用户在不同公众号拥有不同的openid。商户后台系统通过登录授权、支付通知、查询订单等API可获取到用户的openid。主要用途是判断同一个用户,对用户发送客服消息、模版消息等。

微信管理后台:微信有很多管理平台,容易混淆,我们主要关注下面三个平台:

微信公众平台 微信公众账号申请入口和管理后台。商户可以在公众平台提交基本资料、业务资料、财务资料申请开通微信支付功能。帐号分类:服务号、订阅号、小程序、企业微信(也叫企业号,类似于企业OA)。

微信商户平台 微信支付相关的商户功能集合,包括参数配置、支付数据查询与统计、在线退款、代金券或立减优惠运营等功能。

微信开放平台 商户APP接入微信支付开放接口的申请入口,通过此平台可申请微信APP支付。

签名:商户后台和微信支付后台根据相同的密钥和算法生成一个结果,用于校验双方身份合法性。签名的算法 由微信支付制定并公开,常用的签名方式有:MD5、SHA1、SHA256、HMAC等。

密钥:作为签名算法中的盐,需要在微信平台及商户业务系统各存一份,要妥善保管。 key设置路径:微信商户平台(http://pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置。

关于springboot中怎么对接微信支付问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注编程网行业资讯频道了解更多相关知识。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯