当前位置:首页 » 《资源分享》 » 正文

【微信支付】【java】Springboot对接开发微信支付

19 人参与  2024年09月16日 13:21  分类 : 《资源分享》  评论

点击全文阅读


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

目录

 一、微信配置申请

1、微信支付配置申请

二、开发环境

1、开发环境

2、maven依赖

3、application.yml文件配置

三、代码开发

1、配置类

2、初始化商户配置

3、JSAPI微信预下单

3.1、先建个WxPayService服务类

3.1、R实体类

3.2、CreateOrderReq类

4、微信支付回调通知 

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

5.1  QueryOrderReq类

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

7、微信申请退款

8、退款回调通知 

四、mysql表结构

五、controller类

六、很多人问我要退款接口,来今日补充下! 

1.退款controller

2.退款service

七、退款回调通知


 一、微信配置申请

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依赖

<dependency>  <groupId>com.github.wechatpay-apiv3</groupId>  <artifactId>wechatpay-java</artifactId>  <version>0.2.10</version></dependency>

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 */@Configurationpublic 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@Servicepublic 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<String, Object> {    private static final long serialVersionUID = 1L;    public R() {        put("code", 0);    }    public R(Integer code) {        put("code", code);        put("data", new HashMap<String, Object>());    }    public R(Integer code, String msg) {        put("code", code);        put("msg", msg);        put("data", new HashMap<String, Object>());    }    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<String, Object>());        return r;    }    public static R ok(Map<String, Object> map) {        R r = new R();        r.putAll(map);        r.put("data", new HashMap<String, Object>());        return r;    }    public static R ok() {        return new R().put("msg", "success").put("data", new HashMap<String, Object>());    }    public static R ok(Integer size) {        return new R().put("data", new HashMap<String, Object>((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<String, Object> map = (HashMap<String, Object>)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 */@Datapublic 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<String, String> returnMap = new HashMap<>(2);        returnMap.put("code", "FAIL");        returnMap.put("message", "失败");        //修改订单前,建议主动请求微信查询订单是否支付成功,防止恶意post        Wrapper wrapper = new EntityWrapper<WxOrderEntity>();        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<WxPayLogEntity>();            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<WxOrderEntity>();                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<WxPayLogEntity>();                        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 */@Datapublic 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<WxOrderEntity>();                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<WxPayLogEntity>();                        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<WxOrderEntity>();        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<WxOrderEntity>();                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<WxPayLogEntity>();                    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();    }}

六、很多人问我要退款接口,来今日补充下! 

1.退款controller

 @RequestMapping(value = "/xcx/createRefund", method = RequestMethod.POST)    @ApiOperation(value = "微信支付-微信申请退款")    public ResponseEntity<?> createRefund(String outTradeNo, Long totalAmount, String outRefundNo) {        return ResponseEntity.ok(wxPayService.createRefund(outTradeNo, totalAmount, outRefundNo));    }

2.退款service

/***     * 微信申请退款     * @param outTradeNo 商户订单号     * @param totalAmount 退款金额,单位分     * @return     */    public R createRefund(String outTradeNo, Long totalAmount, String outRefundNo) {        log.info("申请退款参数,outTradeNo:{},totalAmount:{},outRefundNo:{}", outTradeNo, totalAmount, outRefundNo);        //返回参数        LinkedHashMap map = new LinkedHashMap();        map.put("out_trade_no", outTradeNo);        map.put("success", false);        map.put("msg", "退款失败!");        if (StringUtils.isEmpty(outRefundNo)) {            outRefundNo = "REFUND_" + outTradeNo;        }        map.put("out_refund_no", outRefundNo);        //申请退款订单,需要变更订单记录        Wrapper wrapper = new EntityWrapper<WxOrderEntity>();        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(wxOrderEntity.getTotalFee().longValue());            amount.setRefund(totalAmount);            amount.setCurrency("CNY");            request.setAmount(amount);            request.setNotifyUrl(wxPayConfig.getRefundNotifyUrl());            //接收退款返回参数            Refund refund = service.create(request);            log.info("退款返回信息:{}", refund);            //SUCCESS:退款成功,PROCESSING:正在处理中            if (refund.getStatus().equals(Status.SUCCESS)) {                map.put("success", true);                map.put("msg", "退款成功!");                map.put("transaction_id", refund.getTransactionId());                //说明退款成功,开始接下来的业务操作                //主动查询                Wrapper againWrapper = new EntityWrapper<WxOrderEntity>();                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<WxPayLogEntity>();                    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(refund.getTransactionId());                        wxPayLogEntity.setOutRefundNo(outRefundNo);                        wxPayLogEntity.setWxOpenId(wxOrderEntity.getWxOpenId());                        wxPayLogDataService.insert(wxPayLogEntity);                    }                }            } else if (refund.getStatus().equals(Status.ABNORMAL)) {                map.put("success", false);                map.put("msg", "退款失败,账户异常!");                map.put("transaction_id", refund.getTransactionId());            } else if (refund.getStatus().equals(Status.PROCESSING)) {                map.put("success", true);                map.put("msg", "正在退款中!");                map.put("transaction_id", refund.getTransactionId());            } else if (refund.getStatus().equals(Status.CLOSED)) {                map.put("success", false);                map.put("msg", "用户余额不足或者订单超过退款期限!");                map.put("transaction_id", refund.getTransactionId());            }        } catch (ServiceException e) {            log.error("退款失败!,错误信息:{}", e.getErrorMessage());            return R.error("退款失败!错误信息:" + e.getErrorMessage());        } catch (Exception e) {            log.error("服务返回成功,返回体类型不合法,或者解析返回体失败,错误信息:{}", e.getMessage());            return R.error("退款失败!错误信息:" + e.getMessage());        }        log.info("微信退款响应参数:{}", map);        return R.ok().put("data", map);    }

其中outRefundNo自定义退款号。

七、退款回调通知

 /***     * 微信退款回调     * @param request     * @throws Exception     */    public void refundNotify(HttpServletRequest request) throws Exception {        try {            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);            RefundNotification parse = parser.parse(requestParam, RefundNotification.class);            log.info("验签成功!-退款回调结果:{}", parse.toString());            //parse.getRefundStatus().equals("SUCCESS");说明退款成功            String refundStatus = parse.getRefundStatus().toString();            log.info("getRefundStatus状态:{}", refundStatus);            if (refundStatus.equals("SUCCESS")) {                log.info("成功进入退款回调,状态:{}", parse.getRefundStatus());                //你的业务代码                Wrapper wrapper = new EntityWrapper<WxOrderEntity>();                wrapper.eq("out_trade_no", parse.getOutTradeNo());                WxOrderEntity wxOrderEntity = wxOrderDataService.selectOne(wrapper);                log.info("订单数据:{}", wxOrderEntity);                if (wxOrderEntity != null) {                    wxOrderEntity.setUpdateTime(new Date());                    wxOrderEntity.setPayStatus(3);//退款成功                    wxOrderEntity.setPayNonce(nonce);                    wxOrderDataService.updateById(wxOrderEntity);                    //同时处理退款记录                    Wrapper payWrapper = new EntityWrapper<WxPayLogEntity>();                    payWrapper.eq("out_trade_no", parse.getOutTradeNo());                    payWrapper.eq("pay_status", 2);//退款                    payWrapper.eq("out_refund_no", parse.getOutRefundNo());//退款                    WxPayLogEntity wxPayLogEntity = wxPayLogDataService.selectOne(payWrapper);                    if (wxPayLogEntity == null) {                        wxPayLogEntity = new WxPayLogEntity();                        wxPayLogEntity.setCreateTime(new Date());                        wxPayLogEntity.setOutTradeNo(parse.getOutTradeNo());                        wxPayLogEntity.setPayStatus(2);                        wxPayLogEntity.setTotalFee(parse.getAmount().getRefund().intValue());                        wxPayLogEntity.setTransactionId(parse.getTransactionId());                        wxPayLogEntity.setOutRefundNo(parse.getOutRefundNo());                        wxPayLogEntity.setWxOpenId(wxOrderEntity.getWxOpenId());                        wxPayLogDataService.insert(wxPayLogEntity);                    }                }            }        } catch (Exception e) {            log.info("退款回调失败!错误信息:{}", e.getMessage());        }    }
 @RequestMapping(value = "/wx/refundNotify", method = RequestMethod.POST)    @ApiOperation(value = "微信支付-微信退款回调通知")    public void refundNotify(HttpServletRequest request) throws Exception {        wxPayService.refundNotify(request);    }


点击全文阅读


本文链接:http://zhangshiyu.com/post/160675.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1