相关推荐recommended
【微信支付】【java】Springboot对接开发微信支付
作者:mmseoamin日期:2024-02-20

【微信支付】【java】Springboot对接开发微信支付,第1张

本文章是介绍java对接(微信小程序)微信支付,包括微信预下单、支付、退款等等。 

【微信支付】【java】Springboot对接开发微信支付,第2张

 一、微信配置申请

1、微信支付配置申请

详细操作流程参考官方文档:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml#part-1

配置完成需要以下信息:

  • APPID
  • 商户号(mchid)
  • 商户API私钥(apiclient_key.pem)
  • 商户证书序列号
  • 商户APIv3密钥

    二、开发环境

    1、开发环境

    开发语言:java ,编译工具:idea ,框架:springboot ,仓库:maven

    2、maven依赖

    
      com.github.wechatpay-apiv3
      wechatpay-java
      0.2.10
    

    3、application.yml文件配置

    #微信支付配置
    wx:
      pay:
        #应用id(小程序id)
        appId: wx6b5xxxxxxxxxxxx
        #商户号
        merchantId: 1xxxxxxxxx
        #商户API私钥
        privateKey: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        #商户证书序列号
        merchantSerialNumber: 315DDXXXXXXXXXXXXXXXXXXXXXXXXXXX
        #商户APIv3密钥
        apiV3Key: XXXXXXXXXXXXXXXXXXXXXXXXXX
        #支付通知地址
        payNotifyUrl: https://xxx.xxxx.xxx.xxx/xx/xxxx/xxxx/openapi/wx/payNotify
        #退款通知地址
        refundNotifyUrl: https://xxx.xxx.xxx.xxx/xxxx/xxxx/xxxx/openapi/wx/refundNotify

    三、代码开发

    1、配置类

    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    /**
     * @author caozhen
     * @ClassName WxPayConfig
     * @description: 微信支付配置类
     * @date 2024年01月03日
     * @version: 1.0
     */
    @Data
    @Component
    @ConfigurationProperties(prefix = "wx.pay")
    public class WxPayConfig {
        //APPID
        private String appId;
        //mchid
        private String merchantId;
        //商户API私钥
        private String privateKey;
        //商户证书序列号
        private String merchantSerialNumber;
        //商户APIv3密钥
        private String apiV3Key;
        //支付通知地址
        private String payNotifyUrl;
        //退款通知地址
        private String refundNotifyUrl;
    }

    2、初始化商户配置

    import com.wechat.pay.java.core.RSAAutoCertificateConfig;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import javax.annotation.Resource;
    /**
     * @author caozhen
     * @ClassName WxPayAutoCertificateConfig
     * @description: 微信支付证书自动更新配置
     * @date 2024年01月03日
     * @version: 1.0
     */
    @Configuration
    public class WxPayAutoCertificateConfig {
        @Resource
        private WxPayConfig wxPayConfig;
        /**
         * 初始化商户配置
         * @return
         */
        @Bean
        public RSAAutoCertificateConfig rsaAutoCertificateConfig() {
            RSAAutoCertificateConfig config = new RSAAutoCertificateConfig.Builder()
                    .merchantId(wxPayConfig.getMerchantId())
                    .privateKey(wxPayConfig.getPrivateKey())
                    .merchantSerialNumber(wxPayConfig.getMerchantSerialNumber())
                    .apiV3Key(wxPayConfig.getApiV3Key())
                    .build();
            return config;
        }
    }

    3、JSAPI微信预下单

    3.1、先建个WxPayService服务类

    import com.alibaba.fastjson.JSONObject;
    import com.baomidou.mybatisplus.mapper.EntityWrapper;
    import com.baomidou.mybatisplus.mapper.Wrapper;
    import com.hvit.user.exception.DataAccessException;
    import com.hvit.user.util.DateUtils;
    import com.hvit.user.util.R;
    import com.hvit.user.yst.entity.WxOrderEntity;
    import com.hvit.user.yst.entity.WxPayLogEntity;
    import com.hvit.user.yst.request.CreateOrderReq;
    import com.hvit.user.yst.request.QueryOrderReq;
    import com.hvit.user.yst.request.WxNotifyReq;
    import com.hvit.user.yst.service.WKShoppingMallService;
    import com.hvit.user.yst.service.data.WxOrderDataService;
    import com.hvit.user.yst.service.data.WxPayLogDataService;
    import com.wechat.pay.java.core.RSAAutoCertificateConfig;
    import com.wechat.pay.java.core.exception.HttpException;
    import com.wechat.pay.java.core.exception.MalformedMessageException;
    import com.wechat.pay.java.core.exception.ServiceException;
    import com.wechat.pay.java.core.notification.NotificationParser;
    import com.wechat.pay.java.core.notification.RequestParam;
    import com.wechat.pay.java.service.payments.jsapi.*;
    import com.wechat.pay.java.service.payments.jsapi.model.*;
    import com.wechat.pay.java.service.payments.jsapi.model.Amount;
    import com.wechat.pay.java.service.payments.model.Transaction;
    import com.wechat.pay.java.service.refund.RefundService;
    import com.wechat.pay.java.service.refund.model.*;
    import io.swagger.annotations.ApiModelProperty;
    import io.swagger.models.auth.In;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    import javax.annotation.Resource;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.math.BigDecimal;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.Map;
    /**
     * @author caozhen
     * @ClassName WxPayService
     * @description: 微信支付
     * @date 2024年01月03日
     * @version: 1.0
     */
    @Slf4j
    @Service
    public class WxPayService {
        @Resource
        private WxPayConfig wxPayConfig;
        @Autowired
        private RSAAutoCertificateConfig rsaAutoCertificateConfig;
        @Autowired
        private WxOrderDataService wxOrderDataService;
        @Autowired
        private WxPayLogDataService wxPayLogDataService;
        /***
         * 预支付订单
         * @param req
         * @return
         */
        public R createOrder(CreateOrderReq req) throws Exception {
            if (req == null) {
                return R.error("创建订单失败,缺少参数!");
            }
            //先解密
            
            String orderNo = req.getOutTradeNo();
            Integer totalFee = req.getTotal();
            //创建初始化订单
            //todo,创建订单这边你们自己来(后面我会放出表结构)
            //请求微信支付相关配置
            JsapiServiceExtension service =
                    new JsapiServiceExtension.Builder()
                            .config(rsaAutoCertificateConfig)
                            .signType("RSA") // 不填默认为RSA
                            .build();
            PrepayWithRequestPaymentResponse response = new PrepayWithRequestPaymentResponse();
            try {
                PrepayRequest request = new PrepayRequest();
                request.setAppid(wxPayConfig.getAppId());
                request.setMchid(wxPayConfig.getMerchantId());
                request.setDescription(description);
                request.setOutTradeNo(orderNo);
                request.setNotifyUrl(wxPayConfig.getPayNotifyUrl());
                Amount amount = new Amount();
                //amount.setTotal(totalFee.multiply(new BigDecimal("100")).intValue());
                amount.setTotal(totalFee);
                request.setAmount(amount);
                Payer payer = new Payer();
                payer.setOpenid(req.getWxOpenId());
                request.setPayer(payer);
                log.info("请求预支付下单,请求参数:{}", JSONObject.toJSONString(request));
                // 调用预下单接口
                response = service.prepayWithRequestPayment(request);
                log.info("订单【{}】发起预支付成功,返回信息:{}", orderNo, response);
            } catch (HttpException e) { // 发送HTTP请求失败
                log.error("微信下单发送HTTP请求失败,错误信息:{}", e.getHttpRequest());
                return R.error("下单失败");
            } catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
                log.error("微信下单服务状态错误,错误信息:{}", e.getErrorMessage());
                return R.error("下单失败");
            } catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
                log.error("服务返回成功,返回体类型不合法,或者解析返回体失败,错误信息:{}", e.getMessage());
                return R.error("下单失败");
            }
            return R.ok().put("data", response);
        }
    }

    3.1、R实体类

    import java.util.HashMap;
    import java.util.Map;
    /**
     *
     * @author 曹震
     * @date 2024-1-03
     */
    public class R extends HashMap {
        private static final long serialVersionUID = 1L;
        public R() {
            put("code", 0);
        }
        public R(Integer code) {
            put("code", code);
            put("data", new HashMap());
        }
        public R(Integer code, String msg) {
            put("code", code);
            put("msg", msg);
            put("data", new HashMap());
        }
        public static R error() {
            return error(500, "未知异常,请联系管理员");
        }
        public static R errorDebug(String message) {
            return error(500, "未知异常 " + message + ",请联系管理员");
        }
        public static R error(String msg) {
            return error(500, msg);
        }
       
        public static R error(int code, String msg) {
            R r = new R();
            r.put("code", code);
            r.put("msg", msg);
            return r;
        }
        public R errorInfo(String msg) {
            this.put("errorMsg", msg);
            return this;
        }
        public static R ok(String msg) {
            R r = new R();
            r.put("msg", msg);
            r.put("data", new HashMap());
            return r;
        }
        public static R ok(Map map) {
            R r = new R();
            r.putAll(map);
            r.put("data", new HashMap());
            return r;
        }
        public static R ok() {
            return new R().put("msg", "success").put("data", new HashMap());
        }
        public static R ok(Integer size) {
            return new R().put("data", new HashMap((int)Math.round(size / 0.75)));
        }
        @Override
        public R put(String key, Object value) {
            super.put(key, value);
            return this;
        }
        /**
         * 添加返回结果数据
         *
         * @param key
         * @param value
         * @return
         */
        public R putData(String key, Object value) {
            Map map = (HashMap)this.get("data");
            map.put(key, value);
            return this;
        }
    }

    3.2、CreateOrderReq类

    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    /**
     * @author caozhen
     * @ClassName CreateOrderReq
     * @description: TODO
     * @date 2024年01月03日
     * @version: 1.0
     */
    @Data
    public class CreateOrderReq {
        @ApiModelProperty(name = "description", value = "商品描述")
        private String description;
        @ApiModelProperty(name = "wxOpenId", value = "用户小程序openid")
        private String wxOpenId;
        @ApiModelProperty(name = "outTradeNo", value = "商户订单号")
        private String outTradeNo;
        @ApiModelProperty(name = "totalFee", value = "支付金额,单位:分")
        private Long totalFee;

    4、微信支付回调通知 

    /***
         * 微信支付回调通知
         * @param request
         * @return
         * @throws IOException
         */
        @Transactional
        public synchronized String payNotify(HttpServletRequest request) throws Exception {
            log.info("------收到支付通知------");
            // 请求头Wechatpay-Signature
            String signature = request.getHeader("Wechatpay-Signature");
            // 请求头Wechatpay-nonce
            String nonce = request.getHeader("Wechatpay-Nonce");
            // 请求头Wechatpay-Timestamp
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            // 微信支付证书序列号
            String serial = request.getHeader("Wechatpay-Serial");
            // 签名方式
            String signType = request.getHeader("Wechatpay-Signature-Type");
            // 构造 RequestParam
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(serial)
                    .nonce(nonce)
                    .signature(signature)
                    .timestamp(timestamp)
                    .signType(signType)
                    .body(HttpServletUtils.getRequestBody(request))
                    .build();
            // 初始化 NotificationParser
            NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
            // 以支付通知回调为例,验签、解密并转换成 Transaction
            log.info("验签参数:{}", requestParam);
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            log.info("验签成功!-支付回调结果:{}", transaction.toString());
            Map returnMap = new HashMap<>(2);
            returnMap.put("code", "FAIL");
            returnMap.put("message", "失败");
            //修改订单前,建议主动请求微信查询订单是否支付成功,防止恶意post
            Wrapper wrapper = new EntityWrapper();
            wrapper.eq("out_trade_no", transaction.getOutTradeNo());
            //wrapper.eq("transaction_id", transaction.getTransactionId());
            WxOrderEntity wxOrderEntity = wxOrderDataService.selectOne(wrapper);
            if (wxOrderEntity != null) {
                if (wxOrderEntity.getPayStatus() == 1) {
                    //此时已经是支付成功,不在处理订单信息
                    returnMap.put("code", "SUCCESS");
                    returnMap.put("message", "成功");
                    return JSONObject.toJSONString(returnMap);
                }
            }
            if (Transaction.TradeStateEnum.SUCCESS != transaction.getTradeState()) {
                log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", transaction.getOutTradeNo(), transaction.getTransactionId());
                if (wxOrderEntity != null) {
                    wxOrderEntity.setUpdateTime(new Date());
                    wxOrderEntity.setPayStatus(2);
                    wxOrderEntity.setPayNonce(nonce);
                    wxOrderEntity.setTransactionId(transaction.getTransactionId());
                    //修改订单信息
                    wxOrderDataService.updateById(wxOrderEntity);
                }
                return JSONObject.toJSONString(returnMap);
            }
            if (wxOrderEntity != null) {
                wxOrderEntity.setUpdateTime(new Date());
                wxOrderEntity.setPayTime(DateUtils.stringToDateTime(transaction.getSuccessTime()));
                wxOrderEntity.setPayDate(DateUtils.stringToDateTime(transaction.getSuccessTime()));
                wxOrderEntity.setPayStatus(1);
                wxOrderEntity.setPayNonce(nonce);
                wxOrderEntity.setTransactionId(transaction.getTransactionId());
                //修改订单信息
                wxOrderDataService.updateById(wxOrderEntity);
                //同时处理支付记录
                Wrapper payWrapper = new EntityWrapper();
                wrapper.eq("out_trade_no", transaction.getOutTradeNo());
                wrapper.eq("pay_status", 1);//支付
                WxPayLogEntity wxPayLogEntity = wxPayLogDataService.selectOne(payWrapper);
                if (wxPayLogEntity == null) {
                    wxPayLogEntity = new WxPayLogEntity();
                    wxPayLogEntity.setCreateTime(new Date());
                    wxPayLogEntity.setOutTradeNo(wxOrderEntity.getOutTradeNo());
                    wxPayLogEntity.setPayStatus(1);
                    wxPayLogEntity.setTotalFee(wxOrderEntity.getTotalFee());
                    wxPayLogEntity.setTransactionId(wxOrderEntity.getTransactionId());
                    wxPayLogEntity.setWxOpenId(wxOrderEntity.getWxOpenId());
                    wxPayLogDataService.insert(wxPayLogEntity);
                }
            }
      
            returnMap.put("code", "SUCCESS");
            returnMap.put("message", "成功");
            return JSONObject.toJSONString(returnMap);
        }

    5、根据商户订单号查询订单(out_trade_no)

    /***
         * 根据商户订单号查询订单 outTradeNo
         * @param req
         * @return
         */
        @Transactional
        public R queryOrderByOrderNo(QueryOrderReq req) {
            QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
            queryRequest.setMchid(wxPayConfig.getMerchantId());
            queryRequest.setOutTradeNo(req.getOrderNo());
            try {
                JsapiServiceExtension service =
                        new JsapiServiceExtension.Builder()
                                .config(rsaAutoCertificateConfig)
                                .signType("RSA") // 不填默认为RSA
                                .build();
                Transaction result = service.queryOrderByOutTradeNo(queryRequest);
                LinkedHashMap retmap = new LinkedHashMap();
                //支付成功
                if (Transaction.TradeStateEnum.SUCCESS == result.getTradeState()) {
                    log.info("内部订单号【{}】,微信支付订单号【{}】支付成功", result.getOutTradeNo(), result.getTransactionId());
                    retmap.put("out_trade_no", result.getOutTradeNo());
                    retmap.put("transaction_id", result.getTransactionId());
                    retmap.put("success", true);
                    retmap.put("msg", "支付成功!");
                    retmap.put("success_time", DateUtils.getDateTimeString(DateUtils.stringToDateTime(result.getSuccessTime())));
                    //主动查询
                    Wrapper wrapper = new EntityWrapper();
                    wrapper.eq("out_trade_no", req.getOrderNo());
                    WxOrderEntity wxOrderEntity = wxOrderDataService.selectOne(wrapper);
                    if (wxOrderEntity != null) {
                        if (wxOrderEntity.getPayStatus() != 1) {
                            wxOrderEntity.setPayStatus(1);
                            wxOrderEntity.setTransactionId(result.getTransactionId());
                            wxOrderEntity.setPayDate(DateUtils.stringToDateTime(result.getSuccessTime()));
                            wxOrderEntity.setPayTime(DateUtils.stringToDateTime(result.getSuccessTime()));
                            wxOrderEntity.setUpdateTime(new Date());
                            wxOrderDataService.updateById(wxOrderEntity);
                            //同时处理支付记录
                            Wrapper payWrapper = new EntityWrapper();
                            wrapper.eq("out_trade_no", req.getOrderNo());
                            WxPayLogEntity wxPayLogEntity = wxPayLogDataService.selectOne(payWrapper);
                            if (wxPayLogEntity == null) {
                                wxPayLogEntity = new WxPayLogEntity();
                                wxPayLogEntity.setCreateTime(new Date());
                                wxPayLogEntity.setOutTradeNo(wxOrderEntity.getOutTradeNo());
                                wxPayLogEntity.setPayStatus(1);
                                wxPayLogEntity.setTotalFee(wxOrderEntity.getTotalFee());
                                wxPayLogEntity.setTransactionId(result.getTransactionId());
                                wxPayLogEntity.setWxOpenId(wxOrderEntity.getWxOpenId());
                                wxPayLogDataService.insert(wxPayLogEntity);
                            }
                        }
                    }
                } else {
                    log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", result.getOutTradeNo(), result.getTransactionId());
                    retmap.put("out_trade_no", result.getOutTradeNo());
                    retmap.put("transaction_id", result.getTransactionId());
                    retmap.put("success", false);
                    retmap.put("msg", "支付失败!");
                    retmap.put("success_time", null);
                }
                return R.ok().put("data", retmap);
            } catch (ServiceException e) {
                log.error("订单查询失败,返回码:{},返回信息:{}", e.getErrorCode(), e.getErrorMessage());
                return R.error("订单查询失败!");
            }
        }

    5.1  QueryOrderReq类

    mport io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    /**
     * @author caozhen
     * @ClassName QueryOrderReq
     * @description: 支付查询
     * @date 2024年01月04日
     * @version: 1.0
     */
    @Data
    public class QueryOrderReq {
        @ApiModelProperty(name = "paymentNo", value = "微信支付订单号,同:transaction_id")
        private String paymentNo;
        @ApiModelProperty(name = "orderNo", value = "商户订单号,同:out_trade_no")
        private String orderNo;
    }

    6、根据支付订单号查询订单 (transaction_id)

    /***
         * 根据支付订单号查询订单 paymentNo
         * @param req
         * @return
         */
        @Transactional
        public R queryOrderByPaymentNo(QueryOrderReq req) {
            QueryOrderByIdRequest queryRequest = new QueryOrderByIdRequest();
            queryRequest.setMchid(wxPayConfig.getMerchantId());
            queryRequest.setTransactionId(req.getPaymentNo());
            try {
                JsapiServiceExtension service =
                        new JsapiServiceExtension.Builder()
                                .config(rsaAutoCertificateConfig)
                                .signType("RSA") // 不填默认为RSA
                                .build();
                Transaction result = service.queryOrderById(queryRequest);
                LinkedHashMap map = new LinkedHashMap();
                //支付成功
                if (Transaction.TradeStateEnum.SUCCESS == result.getTradeState()) {
                    log.info("内部订单号【{}】,微信支付订单号【{}】支付成功", result.getOutTradeNo(), result.getTransactionId());
                    map.put("out_trade_no", result.getOutTradeNo());
                    map.put("transaction_id", result.getTransactionId());
                    map.put("success", true);
                    map.put("msg", "支付成功!");
                    map.put("success_time", DateUtils.getDateTimeString(DateUtils.stringToDateTime(result.getSuccessTime())));
                    //主动查询
                    Wrapper wrapper = new EntityWrapper();
                    wrapper.eq("transaction_id", req.getPaymentNo());
                    WxOrderEntity wxOrderEntity = wxOrderDataService.selectOne(wrapper);
                    if (wxOrderEntity != null) {
                        if (wxOrderEntity.getPayStatus() != 1) {
                            wxOrderEntity.setPayStatus(1);
                            wxOrderEntity.setPayDate(DateUtils.stringToDateTime(result.getSuccessTime()));
                            wxOrderEntity.setPayTime(DateUtils.stringToDateTime(result.getSuccessTime()));
                            wxOrderEntity.setUpdateTime(new Date());
                            wxOrderDataService.updateById(wxOrderEntity);
                            //同时处理支付记录
                            Wrapper payWrapper = new EntityWrapper();
                            wrapper.eq("transaction_id", req.getPaymentNo());
                            WxPayLogEntity wxPayLogEntity = wxPayLogDataService.selectOne(payWrapper);
                            if (wxPayLogEntity == null) {
                                wxPayLogEntity = new WxPayLogEntity();
                                wxPayLogEntity.setCreateTime(new Date());
                                wxPayLogEntity.setOutTradeNo(wxOrderEntity.getOutTradeNo());
                                wxPayLogEntity.setPayStatus(1);
                                wxPayLogEntity.setTotalFee(wxOrderEntity.getTotalFee());
                                wxPayLogEntity.setTransactionId(result.getTransactionId());
                                wxPayLogEntity.setWxOpenId(wxOrderEntity.getWxOpenId());
                                wxPayLogDataService.insert(wxPayLogEntity);
                            }
                        }
                    }
                } else {
                    log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", result.getOutTradeNo(), result.getTransactionId());
                    map.put("out_trade_no", result.getOutTradeNo());
                    map.put("transaction_id", result.getTransactionId());
                    map.put("success", false);
                    map.put("msg", "支付失败!");
                    map.put("success_time", null);
                }
                return R.ok().put("data", map);
            } catch (ServiceException e) {
                log.error("订单查询失败,返回码:{},返回信息:{}", e.getErrorCode(), e.getErrorMessage());
                return R.error("订单查询失败!");
            }
        }

    7、微信申请退款

    /***
         * 微信申请退款
         * @param outTradeNo 商户订单号
         * @param totalAmount
         * @return
         */
        public R createRefund(String outTradeNo, Long totalAmount) {
            //返回参数
            LinkedHashMap map = new LinkedHashMap();
            map.put("out_trade_no", outTradeNo);
            map.put("success", false);
            map.put("msg", "正在申请退款中!");
            String outRefundNo = "REFUND_" + outTradeNo;
            map.put("out_refund_no", outRefundNo);
            //申请退款订单,需要变更订单记录
            Wrapper wrapper = new EntityWrapper();
            wrapper.eq("out_trade_no", outTradeNo);
            WxOrderEntity wxOrderEntity = wxOrderDataService.selectOne(wrapper);
            if (wxOrderEntity == null) {
                return R.error("订单不存在,申请退款不存在!");
            }
            wxOrderEntity.setPayStatus(4);//退款中
            wxOrderEntity.setUpdateTime(new Date());
            wxOrderDataService.updateById(wxOrderEntity);
            try {
                // 构建退款service
                RefundService service = new RefundService.Builder()
                        .config(rsaAutoCertificateConfig)
                        .build();
                CreateRequest request = new CreateRequest();
                // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
                request.setOutTradeNo(outTradeNo);
                request.setOutRefundNo(outRefundNo);
                AmountReq amount = new AmountReq();
                amount.setTotal(totalAmount);
                amount.setRefund(totalAmount);
                amount.setCurrency("CNY");
                request.setAmount(amount);
                request.setNotifyUrl(wxPayConfig.getRefundNotifyUrl());
                //接收退款返回参数
                Refund refund = service.create(request);
                log.info("退款返回信息:{}", refund);
                if (refund.getStatus().equals(Status.SUCCESS)) {
                    map.put("success", true);
                    map.put("msg", "退款成功!");
                    //说明退款成功,开始接下来的业务操作
                    //主动查询
                    Wrapper againWrapper = new EntityWrapper();
                    againWrapper.eq("out_trade_no", outTradeNo);
                    WxOrderEntity orderEntity = wxOrderDataService.selectOne(againWrapper);
                    if (orderEntity != null) {
                        orderEntity.setPayStatus(3);//退款成功
                        orderEntity.setUpdateTime(new Date());
                        wxOrderDataService.updateById(orderEntity);
                        //同时处理退款记录
                        Wrapper payWrapper = new EntityWrapper();
                        payWrapper.eq("out_trade_no", outTradeNo);
                        payWrapper.eq("pay_status", 2);//退款
                        WxPayLogEntity wxPayLogEntity = wxPayLogDataService.selectOne(payWrapper);
                        if (wxPayLogEntity == null) {
                            wxPayLogEntity = new WxPayLogEntity();
                            wxPayLogEntity.setCreateTime(new Date());
                            wxPayLogEntity.setOutTradeNo(outTradeNo);
                            wxPayLogEntity.setPayStatus(2);
                            wxPayLogEntity.setTotalFee(totalAmount.intValue());
                            wxPayLogEntity.setTransactionId(wxOrderEntity.getTransactionId());
                            wxPayLogEntity.setOutRefundNo(outRefundNo);
                            wxPayLogEntity.setWxOpenId(wxOrderEntity.getWxOpenId());
                            wxPayLogDataService.insert(wxPayLogEntity);
                        }
                    }
                }
            } catch (ServiceException e) {
                log.error("退款失败!,错误信息:{}", e.getMessage());
                return R.error("退款失败!");
            } catch (Exception e) {
                log.error("服务返回成功,返回体类型不合法,或者解析返回体失败,错误信息:{}", e.getMessage());
                return R.error("退款失败!");
            }
            return R.ok().put("data", map);
        }
    

    8、退款回调通知 

    待续......有需要的再联系我!

    四、mysql表结构

    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    -- ----------------------------
    -- Table structure for t_wx_order
    -- ----------------------------
    DROP TABLE IF EXISTS `t_wx_order`;
    CREATE TABLE `t_wx_order`  (
      `uuid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
      `trade_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '商品名称',
      `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '订单描述',
      `out_trade_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '(商户)订单流水号',
      `transaction_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '微信订单号',
      `total_fee` int(10) NULL DEFAULT NULL COMMENT '订单金额(单位:分)',
      `pay_nonce` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '支付成功后的随机32位字符串',
      `pay_time` datetime NULL DEFAULT NULL COMMENT '支付时间',
      `pay_date` date NULL DEFAULT NULL COMMENT '支付日期',
      `pay_status` int(3) NULL DEFAULT 0 COMMENT '0:待支付,1:支付成功,2:支付失败,3:退款成功,4:正在退款中,5:未知',
      `wx_open_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '微信小程序openid',
      `status` int(2) NULL DEFAULT 0 COMMENT '0:未删除,1:已删除',
      `create_time` datetime NULL DEFAULT NULL COMMENT '创建订单时间',
      `update_time` datetime NULL DEFAULT NULL COMMENT '修改订单时间',
      PRIMARY KEY (`uuid`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '微信商品订单表' ROW_FORMAT = Dynamic;
    -- ----------------------------
    -- Table structure for t_wx_pay_log
    -- ----------------------------
    DROP TABLE IF EXISTS `t_wx_pay_log`;
    CREATE TABLE `t_wx_pay_log`  (
      `uuid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
      `wx_open_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '微信用户openid',
      `out_trade_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '(商户)订单流水号',
      `out_refund_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '(商户)退款流水号',
      `transaction_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '微信订单号',
      `total_fee` int(10) NULL DEFAULT NULL COMMENT '支付金额',
      `pay_status` int(2) NULL DEFAULT NULL COMMENT '1:支付,2:退款',
      `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
      PRIMARY KEY (`uuid`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '微信用户支付记录表' ROW_FORMAT = Dynamic;
    SET FOREIGN_KEY_CHECKS = 1;

    五、controller类

    退款回调通知,和controller就不写了,如果需要,联系我即可!

    哎呀呀呀,缺少了一个类,代码如下:

    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    /**
     * @author caozhen
     * @ClassName HttpServletUtils
     * @description: TODO
     * @date 2024年01月04日
     * @version: 1.0
     */
    public class HttpServletUtils {
        /**
         * 获取请求体
         *
         * @param request
         * @return
         * @throws IOException
         */
        public static String getRequestBody(HttpServletRequest request) throws IOException {
            ServletInputStream stream = null;
            BufferedReader reader = null;
            StringBuffer sb = new StringBuffer();
            try {
                stream = request.getInputStream();
                // 获取响应
                reader = new BufferedReader(new InputStreamReader(stream));
                String line;
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
            } catch (IOException e) {
                throw new IOException("读取返回支付接口数据流出现异常!");
            } finally {
                reader.close();
            }
            return sb.toString();
        }
    }

    如果你在微信支付回调通知中出现 “签名错误”,并且你是windows服务器,请在HttpServletUtils 类中,将reader = new BufferedReader(new InputStreamReader(stream));   替换成:reader = new BufferedReader(new InputStreamReader(stream,"UTF-8"));

    替换完整代码:

    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    /**
     * @author caozhen
     * @ClassName HttpServletUtils
     * @description: TODO
     * @date 2024年01月04日
     * @version: 1.0
     */
    public class HttpServletUtils {
        /**
         * 获取请求体
         *
         * @param request
         * @return
         * @throws IOException
         */
        public static String getRequestBody(HttpServletRequest request) throws IOException {
            ServletInputStream stream = null;
            BufferedReader reader = null;
            StringBuffer sb = new StringBuffer();
            try {
                stream = request.getInputStream();
                // 获取响应
                reader = new BufferedReader(new InputStreamReader(stream,"UTF-8"));
                String line;
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
            } catch (IOException e) {
                throw new IOException("读取返回支付接口数据流出现异常!");
            } finally {
                reader.close();
            }
            return sb.toString();
        }
    }