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

news2024/11/16 16:22:47

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

 一、微信配置申请

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
 */
@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<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
 */
@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<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
 */
@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<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就不写了,如果需要,联系我即可!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1369057.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

银河麒麟Kylin-Server-V10-SP3使用ISO镜像搭建本地内网YUM/DNF源cdrom/http

机房服务器安装一般是内网环境&#xff0c;需要配置本地的YUM/DNF源。本文介绍通过ISO镜像搭建内网环境的UM/DNF源 准备工作&#xff1a; 提前准备好Kylin-Server-V10-SP3的ISO镜像文件。 本机IP地址&#xff1a;192.168.40.201 镜像存放目录/data/iso/Kylin-Server-V10-SP3-Ge…

以unity技术开发视角对android权限的讲解

目录 前言 Android权限分类 普通权限 普通权限定义 普通权限有哪些 危险权限 危险权限的定义 危险权限有哪些 动态申请权限实例 申请单个权限实例 第一步&#xff1a;在清单文件中声明权限 第二步&#xff1a;在代码中进行动态申请权限 申请多个权限实例 第一步&am…

[C#]使用PaddleInference图片旋转四种角度检测

官方框架地址】 https://github.com/PaddlePaddle/PaddleDetection.git 【算法介绍】 PaddleDetection 是一个基于 PaddlePaddle&#xff08;飞桨&#xff09;深度学习框架的开源目标检测工具库。它提供了一系列先进的目标检测算法&#xff0c;包括但不限于 Faster R-CNN, Ma…

OV5640 摄像头的图像平滑处理

如图所示&#xff0c;这是整个视频采集系统的原理框图。 上电初始&#xff0c;FPGA 需要通过 IIC 接口对 CMOS Sensor 进行寄存器初始化配置。这些初始化的基本参数&#xff0c;即初始化地址对应的初始化数据都存储在一个预先配置好的 FPGA 片内 ROM中。在初始化配置完成后&…

视频监控系统EasyCVR如何通过调用API接口查询和下载设备录像?

智慧安防平台EasyCVR是基于各种IP流媒体协议传输的视频汇聚和融合管理平台。视频流媒体服务器EasyCVR采用了开放式的网络结构&#xff0c;支持高清视频的接入和传输、分发&#xff0c;平台提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联…

Python词云wordcloud库不显示中文

博主之前在项目中发现Python的词云库wordcloud显示的都是方框&#xff0c;别担心&#xff0c;我有一个妙招让你的中文词云变得美观又清晰&#xff01; 问题 wordcloud是一个基于python的词云生成库&#xff0c;它可以让你用简单的代码创建出各种形状和颜色的词云图像。 word…

qt初入门2:qt选择一个文件或者目录,获取当前目录,操作文件目录等整理

最近用qt操作文件或者目录的动作比较多&#xff0c;简单整理一下常用的接口&#xff0c;方便回顾。 总的来说&#xff0c;其实就是用文件选择对话框QFileDialog类&#xff0c;以及操作文件信息的QFileInfo类&#xff0c;以及相关QCoreApplication中静态成员函数获取一些信息&a…

js(JavaScript)数据结构之数组(Array)

什么是数据结构&#xff1f; 下面是维基百科的解释&#xff1a; 数据结构是计算机存储、组织数据的方式。数据结构意味着接口或封装&#xff1a;一个数据结构可被视为两个函数之间的接口&#xff0c;或者是由数据类型联合组成的存储内容的访问方法封装。 我们每天的编码中都会…

有关“修改地址”的回复话术大全

类型一:不能改地址 1.亲非常抱歉 这边发货后客服就没法帮您操作修改地址了 2.非常遗憾&#xff0c;订单一旦下单完成 地址是无法进行修改的。如果您这边需要修改地址的话也是可以尝试去和这个物流方进行协商的哦&#xff0c;这边没有修改的按钮没法操作的 3.抱歉呢亲亲。修改…

CentOS服务器之间免密登录和传输文件

使用过 Jenkins 的同学都知道&#xff0c;Jenkins 会在远程服务器上执行一些命令&#xff0c;如&#xff1a;cd /home/wwwroot/ && git pull&#xff0c;这时候就需要在 Jenkins 服务器上配置免密登录&#xff0c;以及在远程服务器上配置免密登录&#xff0c;这样才能实…

vue 自定义网页图标 favicon.ico 和 网页标题

效果预览 1. 添加配置 vue.config.js 在 module.exports { 内添加 // 自定义网页图标pwa: {iconPaths: {favicon32: "./favicon.ico",favicon16: "./favicon.ico",appleTouchIcon: "./favicon.ico",maskIcon: "./favicon.ico",msTil…

为什么选择嬴图?

图数据库、图计算、图中台都是用图论的方式去构造实体间的关联关系&#xff0c;实体用顶点来表达&#xff0c;而实体间的关系用边来表达。图数据库的这种简洁、自由、高维但100%还原世界的数据建模的方式让实体间的关联关系的计算比SQL类的数据库高效成千上万倍。 图&#xff1…

互联网外包公司干了2个月,技术退步明显了.......

先说一下自己的情况&#xff0c;本科毕业&#xff0c;18年通过校招进入南京某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能…

cosmos及特定应用程序的区块链

特定应用程序的区块链,简单来说&#xff0c;一个区块链就是一个专门的应用程序。为了实现某一特定的去中心化应用而专门实现一个区块链。 传统的用智能合约构建去中心化应用不行吗&#xff1f; 灵活性不足&#xff1a;智能合约本质上受到虚拟机本身的限制。例如&#xff0c;以…

详解Oracle数据库的启动

Oracle数据库的启动&#xff0c;其概念可参考Overview of Instance and Database Startup。 其过程可参见下图&#xff1a; 当数据库从关闭状态进入打开数据库状态时&#xff0c;它会经历以下阶段。 阶段Mount状态描述1实例在没有挂载数据库的情况下启动实例已启动&#xff…

【STM32F103】RCC复位和时钟控制

前言 之前介绍外设的时候总是没有提到RCC&#xff0c;但其实我们使用STM32的外设之前都需要做的一步就是打开外设时钟。原本想着没什么可说的&#xff0c;就是用什么外设的时候就在开头加一行代码打开外设时钟就好了。直到最近写到了TIM定时器&#xff0c;我才开始觉得应该说一…

scipy-interpolate整理

文章目录 scipy-interpolate整理Univariate interpolation 单变量插值Multivariate interpolation 多元插值Unstructured data 非结构化数据&#xff1a;:For data on a grid 对于网格上的数据&#xff1a;:Tensor product polynomials 张量积多项式&#xff1a;: 1-D Splines …

Python(33):数据断言(查询数据库数据和插入数据对比)

Python(33):数据断言(查询数据库数据和插入数据对比) 前言&#xff1a; 需求&#xff1a;需要针对查询数据库数据和插入的数据进行对比&#xff0c;用Python语言进行编写 数据库查询的结果可参考&#xff1a;https://blog.csdn.net/fen_fen/article/details/135462484 1、查…

java解析json复杂数据的两种思路

文章目录 一、原始需求二、简单分析三、具体实现一1. api接口2. 接口返回3. json 数据解析1.&#xff09;引入Jackson库2.&#xff09;定义实体3.&#xff09;解析json字符串4.&#xff09;运行结果 4. 过程分析 四、具体实现二1. 核心代码2.运行结果 五、方案比较六、源码传送…

Python 自学(七) 之面向对象

目录 1. 类的初始化函数 __init__ P186 2. 动态的为类和对象添加属性 P190 3. 类的访问限制 __xxx P192 4. 类的继承及方法重写 P197 1. 类的初始化函数 __init__ P186 每当创建一个类的实例时&#xff0c;__init__都会被执…