欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

微信小程序支付(java)Springboot

程序员文章站 2022-06-15 09:08:12
...

微信小程序支付

获取微信参数

支付必须参数

字段名 变量名 描述
小程序ID appid 微信分配的小程序id
商户号 mch_id 微信支付分配的商户号
商户秘钥 paterner_key 微信支付的秘钥
随机字符串 nonce_str 随机字符串,长度要求在32位以内。
签名 sign 通过签名算法计算得出的签名值,
商品描述 body 商品简单描述,
商户订单号 out_trade_no 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-
标价金额 total_fee 订单总金额,单位为分
终端IP spbill_create_ip 支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP
通知地址 notify_url 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数,(微信支付完成后调用接口)
交易类型 trade_type JSAPI–JSAPI支付(或小程序支付)、NATIVE–Native支付、APP–app支付,MWEB–H5支付
用户标识 openid 微信小程序登录后的唯一标识 openid

获取方式:

小程序ID:

微信小程序支付(java)Springboot

商户号:

微信小程序支付(java)Springboot

商户秘钥:

微信小程序支付(java)Springboot微信小程序支付(java)Springboot

随机字符串:

​ nonce_str:WXPayUtil中的generateNonceStr()即可,就是生成UUID的方法;

签名:

​ sign:用WXPayUtil中的generateSignature(finalMap<String, String> data, String key)方法,data是将除了sign外,其他10个参数放到map中,key是四大配置参数中的API秘钥(paternerKey)

商品描述:

​ body:所支付的名称

商户订单号:

​ out_trade_no: 自己后台生成的订单号,只要保证唯一就好

标价金额:

​ total_fee:订单总金额,单位为分

终端IP:

​ spbill_create_ip: IP地址

通知地址:

​ notify_url: 微信支付成功后,微信那边携带参数,请求这个地址,作为业务处理,地址需要公网可以访问

交易类型:

​ trade_type:JSAPI–JSAPI支付(或小程序支付)、NATIVE–Native支付、APP–app支付,MWEB–H5支付

用户标识:

​ openid:微信小程序登录后的唯一标识

编码实现

1.maven导入依赖

		<dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>0.0.3</version>
        </dependency>

2.导入官方工具类

微信小程序支付(java)Springboot

3.编写Controller

1.预下单:

1.1 controller层

 	/**
     * 预下单
     * @param wxPayDto
     * @param request
     * @return
     */
    @PostMapping("/wxPay")
    public AjaxResult wxPay(WxPayDto wxPayDto, HttpServletRequest request){
        // 传入的值为 前台接收的封装类和IP地址
        return AjaxResult.success(wxPayService.wxPay(wxPayDto, IpUtils.getIpAddr(request)));
    }

1.2 Service 层(注释都在里面。。写的很详细。一步一步看我操作即可)

	@Override
    public Map<String,String> wxPay(WxPayDto wxPayDto,String ip) {
        //新建一个返回的map
        Map<String,String> result = new HashMap<>();
        //首先redis 中获取需要用到的参数
        Object o = redisUtil.get(RedisConstants.WX_PAY_CONFIG);
        //new 一个微信参数map
        Map<String,Object> wxPayConfig = new HashMap<>();
        if(o == null){
            //判断redis中的值是否为空,如果为空的话,就去数据库中查询,然后在放入redis中
            QueryWrapper<AppConfigEntity> wrapper = new QueryWrapper<>();
            wrapper.select("wx_appid","mch_id","paterner_key");
            // 查询所需要的参数
            AppConfigEntity appConfigEntity = appConfigMapper.selectOne(wrapper);
            if(appConfigEntity == null){
                throw  new CustomException("参数有误,请联系管理员!",500);
            }
            //放入map中
            wxPayConfig.put("wx_appid",appConfigEntity.getWxAppid());
            wxPayConfig.put("mch_id",appConfigEntity.getMchId());
            wxPayConfig.put("paternerKey",appConfigEntity.getPaternerKey());
            //将map转换为json字符串,并放入到redis中
            redisUtil.set(RedisConstants.WX_PAY_CONFIG,GsonUtil.toJson(wxPayConfig));
        }else{
            // redis中不为空,则将数据转换为map类型的。
            wxPayConfig = GsonUtil.toMap(o.toString());
        }
        // 通过订单ID获取用户ID
        OrderEntity orderEntity = orderMapper.selectById(wxPayDto.getOrderId());
        if(orderEntity == null){
            throw  new CustomException("订单异常,请重新下单!",500);
        }
        // 通过用户ID获取openid(这里需要在小程序登录哪里获取openid),我这里在数据库中,所以通过id来查询出来openid。
        UserEntity userEntity = userMapper.selectById(orderEntity.getUserId());

        try {
            //拼接参数
            Map<String,String> param = new HashMap<>();
            param.put("appid",wxPayConfig.get("wx_appid").toString());
            param.put("body",wxPayDto.getName());
            param.put("mch_id",wxPayConfig.get("mch_id").toString());
            param.put("nonce_str", WXPayUtil.generateNonceStr());
            // 回调接口。此路径为微信服务器调用支付结果通知路径 也就是说,用户支付完成之后,微信服务器会调用此接口,路径必须为公网路径,也就是已经部署到服务器上,我这里调试使用的是花生壳路径
            param.put("notify_url","www.*******.com/***/**");
            // 用户的opneid
            param.put("openid",userEntity.getOpenId());
            // 商户订单(商户自己生成的订单号)
            param.put("out_trade_no",wxPayDto.getOrderNumber());
            // ip地址
            param.put("spbill_create_ip", ip);
            //把数据库中的金额转换为分,并转换为字符串。
            param.put("total_fee",String.valueOf((int)(wxPayDto.getFlowmoney() * 100)));
            // 交易的类型 JSAPI--JSAPI支付(或小程序支付)、NATIVE--Native支付、APP--app支付,MWEB--H5支付
            param.put("trade_type","JSAPI");
            // 调用微信支付官网提供的工具类生成sign签名,第一个参数为param中的上面的参数,第二个为支付的秘钥
            String sign = WXPayUtil.generateSignature(param, wxPayConfig.get("paternerKey").toString());
            //生成之后放入参数中
            param.put("sign",sign);
            //调用微信官网提供的工具类将map转换为xml格式的文件
            String xml = WXPayUtil.mapToXml(param);
            log.debug("XML数据为 [{}]",xml);
            //拼接请求路径
            String url = "https://" + WXPayConstants.DOMAIN_API + "/" + WXPayConstants.UNIFIEDORDER_URL_SUFFIX;
            log.debug("url 路径为:{}",url);
            //发送post请求,携带xml数据
            String xmlStr = HttpUtils.sendPost(url, xml);
            log.debug("返回数据为:[{}]",xmlStr);
            // 返回的数据转换为map数据,
            Map<String, String> stringStringMap = WXPayUtil.xmlToMap(xmlStr);
            //将结果放入result map中,返回前台用
            result.put("appId",wxPayConfig.get("wx_appid").toString());
            result.put("timeStamp",WXPayUtil.getCurrentTimestamp()+"");
            result.put("nonceStr",WXPayUtil.generateNonceStr());
            result.put("signType",WXPayConstants.MD5);
            result.put("package","prepay_id="+stringStringMap.get("prepay_id"));
            result.put("paySign", WXPayUtil.generateSignature(result, wxPayConfig.get("paternerKey").toString()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        //返回前台
        return result;
    }
xml数据为:
<xml>
   <nonce_str>ZFd2BtolOr3wsePjtlyaftizGgGgpmYF</nonce_str>
   <out_trade_no>12020011413250682063662178618273</out_trade_no>
   <openid>openid</openid>
   <appid>appid</appid>
   <total_fee>2222</total_fee>
   <sign>614DC04A69F0B8CC22054C4FD4277EDB</sign>
   <trade_type>JSAPI</trade_type>
   <body>A6包年套餐(365天)</body>
   <mch_id>111</mch_id>
    <notify_url>www.*******.com/***/**</notify_url>
   <spbill_create_ip>127.0.0.1</spbill_create_ip>
</xml>

1.3 httpUtils

package com.hlyun.utils;

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

import javax.net.ssl.*;
import java.io.*;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.security.cert.X509Certificate;

/**
 * 通用http发送方法
 *
 * @author ruoyi
 */
public class HttpUtils {
    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);

    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url 发送请求的 URL
     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param) {
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try {
            String urlNameString = url;
            if(StringUtils.isNotNull(param)){
                urlNameString = url + "?" + param;
            }

            log.info("sendGet - {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection connection = realUrl.openConnection();
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            connection.connect();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            log.info("recv - {}", result);
        }catch (ConnectException e) {
            log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
        }catch (SocketTimeoutException e) {
            log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
        }catch (IOException e) {
            log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
        }catch (Exception e){
            log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            }catch (Exception ex) {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url 发送请求的 URL
     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            log.info("sendPost - {}", url);
            URL realUrl = new URL(url);
            URLConnection conn = realUrl.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            out.print(param);
            out.flush();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            log.info("recv - {}", result);
        }catch (ConnectException e) {
            log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
        } catch (SocketTimeoutException e) {
            log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        } catch (IOException e) {
            log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
        } catch (Exception e) {
            log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

    public static String sendSSLPost(String url, String param) {
        StringBuilder result = new StringBuilder();
        String urlNameString = url + "?" + param;
        try {
            log.info("sendSSLPost - {}", urlNameString);
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
            URL console = new URL(urlNameString);
            HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            conn.setDoOutput(true);
            conn.setDoInput(true);

            conn.setSSLSocketFactory(sc.getSocketFactory());
            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
            conn.connect();
            InputStream is = conn.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String ret = "";
            while ((ret = br.readLine()) != null) {
                if (ret != null && !ret.trim().equals("")) {
                    result.append(new String(ret.getBytes("ISO-8859-1"), "utf-8"));
                }
            }
            log.info("recv - {}", result);
            conn.disconnect();
            br.close();
        }catch (ConnectException e) {
            log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
        } catch (SocketTimeoutException e) {
            log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        } catch (IOException e) {
            log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
        } catch (Exception e) {
            log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
        }
        return result.toString();
    }

    private static class TrustAnyTrustManager implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers()
        {
            return new X509Certificate[] {};
        }
    }

    private static class TrustAnyHostnameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String hostname, SSLSession session)
        {
            return true;
        }
    }
}

2.回调函数

1.1 controller层

/**
     * 微信支付回调函数
     * @return
     */
    @RequestMapping("/callback")
    public AjaxResult wxPayCallBack(HttpServletRequest request, HttpServletResponse response){
        log.debug("微信支付成功,微信发送的callback信息");
        InputStream is = null;
        String xml = "";
        try {
            // 获取流
            is = request.getInputStream();
            //把流转换为string类型的xml
            xml = WXPayUtil.inputStream2String(is, "UTF-8");
            //告诉微信服务器收到信息了,不要在调用回调action了========这里很重要回复微信服务器信息用流发送一个xml即可
            response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
            // 关闭流
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //传到Service
        return toAjax(wxPayService.wxPayCallBack(xml));
    }

1.2 Service层

		@Override
    public int wxPayCallBack(String xml) {
        int res = 0;
        try {
            //将微信发的xml转map
            Map<String, String> notifyMap = WXPayUtil.xmlToMap(xml);
            if(notifyMap.get("return_code").equals("SUCCESS")){
                //以下根据具体的业务逻辑编写,仅供参考。请根据实际情况编写
                //支付成功,获取商户订单号
                String out_trade_no = notifyMap.get("out_trade_no");
                //支付订单号(微信官网提供)
                String transaction_id = notifyMap.get("transaction_id");
                // 交易时间
                String time_end = notifyMap.get("time_end");
                //获取实际支付金额 单位 分
                Double total_fee = Double.valueOf(coinToYuan(notifyMap.get("total_fee")));
                QueryWrapper<OrderEntity> wrapper = new QueryWrapper<>();
                wrapper.eq("order_number",out_trade_no);
                OrderEntity orderEntity = orderMapper.selectOne(wrapper);
                if(orderEntity != null){
                    orderEntity.setPayNumber(transaction_id);
                    orderEntity.setTotalFee(total_fee);
                    orderEntity.setPayType("1");
                    orderEntity.setPayTime(DateUtils.parseDate(time_end));
                    orderEntity.setIsPay("1");
                    res = orderMapper.updateById(orderEntity);
                }else{
                    throw new CustomException("交易查询失败,请联系管理员核实!",500);
                }
            }else{
                throw new CustomException("交易查询失败,请联系管理员核实!",500);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return res;
    }

小白一枚,代码奉上,大家参考下,有好的建议希望大家也评论下

相关标签: 笔记 java