微信小程序支付 V3版 对接

 引入依赖

<dependency>
    <groupId>com.github.javen205</groupId>
    <artifactId>IJPay-WxPay</artifactId>
    <version>2.9.6</version>
</dependency>

去微信下载证书文件 开发指引 - 小程序支付 | 微信支付商户文档中心 (qq.com)

配置证书并读取

创建: wxpay_v3.properties

#服务商/直连商户平台 公众号appid
v3.appId=wx5316............
#秘钥
v3.keyPath=cert/apiclient_key.pem
#平台证书路径
v3.certPath=cert/apiclient_cert.pem
#CA证书 格式.pemxq
v3.certP12Path=cert/apiclient_cert.p12
#CA证书 格式.p12
v3.platformCertPath=cert/platformCert.pem

#服务商id/商户id
v3.mchId=160.........
#自定义 apiv3 秘钥
v3.apiKey3=52UJDFCAP.....................
#自定义 api 秘钥
v3.apiKey=2542a43.........................


#项目域名(测试,用不着) 
#项目域名(正式)
v3.domain=http://223.00.00.00:6002/iot-sim


读取配置文件的配置类

创建配置类  WxPayV3Bean

package com.baymax.vpp.cop.pay.constant;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

/**
 * <p>微信配置 Bean</p> 
 */
@Component
@PropertySource("classpath:/production/wxpay_v3.properties")
@ConfigurationProperties(prefix = "v3")
public class WxPayV3Bean {

    // 服务商的appId
    private String appId;

    // 服务商私钥
    private String keyPath;

    // 服务商证书序列号  平台证书路径
    private String certPath;

    // CA证书 格式.pemxq
    private String certP12Path;

    //CA证书 格式.p12
    private String platformCertPath;

    //服务商商号
    private String mchId;

    //  自定义 api 秘钥
    private String apiKey;

    //服务商APIv3 #自定义 apiv3 秘钥
    private String apiKey3;
    private String domain;

    public String getAppId() {
        return appId;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public String getKeyPath() {
        return keyPath;
    }

    public void setKeyPath(String keyPath) {
        this.keyPath = keyPath;
    }

    public String getCertPath() {
        return certPath;
    }

    public void setCertPath(String certPath) {
        this.certPath = certPath;
    }

    public String getCertP12Path() {
        return certP12Path;
    }

    public void setCertP12Path(String certP12Path) {
        this.certP12Path = certP12Path;
    }

    public String getPlatformCertPath() {
        return platformCertPath;
    }

    public void setPlatformCertPath(String platformCertPath) {
        this.platformCertPath = platformCertPath;
    }

    public String getMchId() {
        return mchId;
    }

    public void setMchId(String mchId) {
        this.mchId = mchId;
    }

    public String getApiKey() {
        return apiKey;
    }

    public void setApiKey(String apiKey) {
        this.apiKey = apiKey;
    }

    public String getApiKey3() {
        return apiKey3;
    }

    public void setApiKey3(String apiKey3) {
        this.apiKey3 = apiKey3;
    }

    public String getDomain() {
        return domain;
    }

    public void setDomain(String domain) {
        this.domain = domain;
    }

    @Override
    public String toString() {
        return "WxPayV3Bean{" +
                "keyPath='" + keyPath + '\'' +
                ", certPath='" + certPath + '\'' +
                ", certP12Path='" + certP12Path + '\'' +
                ", platformCertPath='" + platformCertPath + '\'' +
                ", mchId='" + mchId + '\'' +
                ", apiKey='" + apiKey + '\'' +
                ", apiKey3='" + apiKey3 + '\'' +
                ", domain='" + domain + '\'' +
                '}';
    }
}

获取 微信调用接口的 CloseableHttpClient

    private CloseableHttpClient httpClientCreat() {
        String merchantId = wxPayV3Bean.getMchId();
        String merchantSerialNumber = WechatPayUtils.getSerialNumber();
        String apiV3Key = wxPayV3Bean.getApiKey3();
        try {
            PrivateKey merchantPrivateKey = WechatPayUtils.getPrivateKey();
            AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                    new WechatPay2Credentials(merchantId, new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)), apiV3Key.getBytes("utf-8"));
            CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
                    .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(verifier)).build();
            return httpClient;
        } catch (Exception e) {
            System.out.println(e);
        }
        return null;
    }

获取私钥    PrivateKey 、获取证书序列号、 从证书管理器中获取 verifier

package com.baymax.vpp.cop.pay.utils;

import cn.hutool.core.util.StrUtil;
import com.ijpay.core.kit.PayKit;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.springframework.core.io.ClassPathResource;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;

public class WechatPayUtils {

    // 获取私钥 返回字符串
    public static String getPrivateKeyStr() {
        try {
            ClassPathResource classPathResource = new ClassPathResource("/cert/apiclient_key.pem");
            InputStream inputStream = classPathResource.getInputStream();
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(inputStream);
            return merchantPrivateKey.toString();
        } catch (Exception e) {
            System.out.println(e);
        }
        return "";
    }

    // 获取私钥 PrivateKey
    public static PrivateKey getPrivateKey() {
        try {
            ClassPathResource classPathResource = new ClassPathResource("/cert/apiclient_key.pem");
            InputStream inputStream = classPathResource.getInputStream();
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(inputStream);
            return merchantPrivateKey;
        } catch (Exception e) {
            System.out.println(e);
        }
        return null;
    }


    // 获取证书序列号
    public static String getSerialNumber() {
        String serialNo = "";
        if (StrUtil.isEmpty(serialNo)) {
            try {
                //docker打包上传到Linux后,获取不到路径,改为以下方法
                ClassPathResource resource = new ClassPathResource("cert/apiclient_cert.pem");
                InputStream in = resource.getInputStream();
                // 获取证书序列号
                X509Certificate certificate = PayKit.getCertificate(in);
                serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
            } catch (Exception e) {
            }
        }
        return serialNo;
    }


    //从证书管理器中获取 verifier
    public static Verifier getVerifier(String mchId, String apiKey3) throws Exception {
        String serialnumber = getSerialNumber();
        //获取商户私钥
        PrivateKey merchantPrivateKey = getPrivateKey();
        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        // 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId, new PrivateKeySigner(serialnumber, merchantPrivateKey)), apiKey3.getBytes(StandardCharsets.UTF_8));
        // 从证书管理器中获取verifier
        Verifier verifier = certificatesManager.getVerifier(mchId);
        return verifier;
    }


}

    
调用微信接口

基础下单接口  
微信支付订单号查询订单  
商户订单号查询订单  
微信小程序-退款申请 
查询单笔退款(通过商户退款单号)

package com.baymax.vpp.cop.pay.wechat.pay;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHeaders;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.security.PrivateKey;
import java.text.MessageFormat;

/**
 * 微信支付接口封装
 *
 * @author hawods
 * @version 2023-09-04
 */
@Slf4j
@Service
public class WechatPayManager {


    @Resource
    WxPayV3Bean wxPayV3Bean;

 

    /**
     * 基础下单接口
     *
     * @param request
     * @return
     */
    public PrepayResponse prepay(PrepayRequest request) {
        CloseableHttpClient httpClient = httpClientCreat();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
        httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
        httpPost.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        String requestBody = JsonUtil.toJson(request);
        log.debug("微信支付请求体: {}", requestBody);
        httpPost.setEntity(new StringEntity(requestBody, "UTF-8"));
        int responseStatus;
        String responseBody;
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            responseStatus = response.getStatusLine().getStatusCode();
            responseBody = EntityUtils.toString(response.getEntity());
        } catch (IOException e) {
            throw new ServiceException("微信支付请求失败", e);
        }
        if (responseStatus != HttpStatus.OK.value() || StrUtil.isBlank(responseBody)) {
            log.error("微信支付请求异常,响应体: {}", responseBody);
            Object message = JsonUtil.toMap(responseBody).get("message");
            throw new ServiceException("微信支付请求异常: " + message);
        }
        log.debug("微信支付请求响应: {}", responseBody);
        return JsonUtil.parse(responseBody, PrepayResponse.class);
    }


    /**
     * 微信支付订单号查询订单
     *
     * @param transactionId 微信支付订单号查询订单
     * @return
     */
    public Transaction queryOrder(String transactionId) {
        try {
            CloseableHttpClient httpClient = httpClientCreat();
            //请求URL
            URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/pay/transactions/id/" + transactionId);
            uriBuilder.setParameter("mchid", wxPayV3Bean.getMchId());
            //完成签名并执行请求
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            httpGet.addHeader("Accept", "application/json");
            CloseableHttpResponse response = httpClient.execute(httpGet);
            int statusCode = response.getStatusLine().getStatusCode();
            String responseBody = EntityUtils.toString(response.getEntity());
            if (statusCode == HttpStatus.OK.value() || statusCode == HttpStatus.NO_CONTENT.value()) {
                if (StrUtil.isBlank(responseBody)) {
                    log.error("微信查询支付异常,响应体: {}", responseBody);
                    Object message = JsonUtil.toMap(responseBody).get("message");
                    throw new ServiceException("微信查询支付异常: " + message);
                }
                log.debug("微信支付请求响应: {}", responseBody);
                return JsonUtil.parse(responseBody, Transaction.class);
            }
        } catch (Exception e) {
            throw new ServiceException("微信支付请求失败", e);
        }
        return null;
    }


    /**
     * 商户订单号查询订单
     *
     * @param outTradeNo 商户订单号查询订单
     * @return
     */
    public Transaction queryOrderByOutTradeNo(String outTradeNo) {
        try {
            CloseableHttpClient httpClient = httpClientCreat();
            //请求URL
            URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + outTradeNo);
            uriBuilder.setParameter("mchid", wxPayV3Bean.getMchId());
            //完成签名并执行请求
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            httpGet.addHeader("Accept", "application/json");
            CloseableHttpResponse response = httpClient.execute(httpGet);
            int statusCode = response.getStatusLine().getStatusCode();
            String responseBody = EntityUtils.toString(response.getEntity());
            if (statusCode == HttpStatus.OK.value() || statusCode == HttpStatus.NO_CONTENT.value()) {
                if (StrUtil.isBlank(responseBody)) {
                    log.error("微信查询支付异常,响应体: {}", responseBody);
                    Object message = JsonUtil.toMap(responseBody).get("message");
                    throw new ServiceException("微信查询支付异常: " + message);
                }
                log.debug("微信支付请求响应: {}", responseBody);
                return JsonUtil.parse(responseBody, Transaction.class);
            }
        } catch (Exception e) {
            throw new ServiceException("微信支付请求失败", e);
        }
        return null;
    }


    /**
     * 微信小程序-退款申请
     *
     * @param request
     * @return
     */
    public ChinaUmsRefundVo refundWechat(RefundRequest request) {
        CloseableHttpClient httpClient = httpClientCreat();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");
        httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
        httpPost.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        String requestBody = JsonUtil.toJson(request);
        log.debug("微信退款请求体: {}", requestBody);
        httpPost.setEntity(new StringEntity(requestBody, "UTF-8"));
        int responseStatus;
        String responseBody;
        CloseableHttpResponse response = null;
        ChinaUmsRefundVo chinaUmsRefundVo = new ChinaUmsRefundVo();
        try {
            response = httpClient.execute(httpPost);
            responseStatus = response.getStatusLine().getStatusCode();
            responseBody = EntityUtils.toString(response.getEntity());
        } catch (IOException e) {
            chinaUmsRefundVo.setIsSuccess(false);
            throw new ServiceException("微信退款请求失败", e);
        }
        if (responseStatus != HttpStatus.OK.value() || StrUtil.isBlank(responseBody)) {
            chinaUmsRefundVo.setIsSuccess(false);
            log.error("微信退款请求异常,响应体: {}", responseBody);
            Object message = JsonUtil.toMap(responseBody).get("message");
            throw new ServiceException("微信退款请求异常: " + message);
        }
        log.debug("微信退款请求响应: {}", responseBody);

        chinaUmsRefundVo.setRefundAmount(Long.valueOf(String.valueOf(request.getAmount().getRefund())));
        chinaUmsRefundVo.setIsSuccess(true);
        chinaUmsRefundVo.setErrCode(null);
        chinaUmsRefundVo.setErrMsg(null);
        return chinaUmsRefundVo;
    }


    /**
     * 查询单笔退款(通过商户退款单号)
     *
     * @param outRefundNo
     * @return
     */
    public WechatRefundVO selectRefundByOutRefundNo(String outRefundNo) {
        CloseableHttpClient httpClient = httpClientCreat();
        String uriPattern = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/" + outRefundNo;
        String uri = MessageFormat.format(
                uriPattern,
                URLUtil.encodePathSegment(outRefundNo)
        );
        HttpGet httpGet = new HttpGet(uri);
        httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
        httpGet.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        int responseStatus;
        String responseBody;
        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            responseStatus = response.getStatusLine().getStatusCode();
            responseBody = EntityUtils.toString(response.getEntity());
        } catch (IOException e) {
            throw new ServiceException("查询单笔退款失败", e);
        }
        if (responseStatus != HttpStatus.OK.value() || StrUtil.isBlank(responseBody)) {
            log.error("查询单笔退款异常,响应体: {}", responseBody);
            Object message = JsonUtil.toMap(responseBody).get("message");
            throw new ServiceException("查询单笔退款异常: " + message);
        }

        log.debug("查询单笔退款响应: {}", responseBody);
        return JSONObject.parseObject(responseBody, WechatRefundVO.class);
    }


    private CloseableHttpClient httpClientCreat() {
        String merchantId = wxPayV3Bean.getMchId();
        String merchantSerialNumber = WechatPayUtils.getSerialNumber();
        String apiV3Key = wxPayV3Bean.getApiKey3();
        try {
            PrivateKey merchantPrivateKey = WechatPayUtils.getPrivateKey();
            AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                    new WechatPay2Credentials(merchantId, new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)), apiV3Key.getBytes("utf-8"));
            CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
                    .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(verifier)).build();
            return httpClient;
        } catch (Exception e) {
            System.out.println(e);
        }
        return null;
    }


}
   
     

对发起支付参数进行签名

    private PrepayResponseDto sign(PrepayResponseDto response) {
        StringBuilder sb = new StringBuilder();
        sb.append(response.getAppId()).append("\n")
                .append(response.getTimeStamp()).append("\n")
                .append(response.getNonceStr()).append("\n")
                .append(response.getPackageStr()).append("\n");
//        String paySign = wechatPayManager.sign(sb.toString());

        PrivateKey privateKey = WechatPayUtils.getPrivateKey();
        try {
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(privateKey);
            signature.update(sb.toString().getBytes(StandardCharsets.UTF_8));
            String paySign = Base64.encode(signature.sign());
            response.setPaySign(paySign);
        } catch (Exception e) {
        }
        return response;
    }

 签名返回实体类 PrepayResponseDto

package com.baymax.vpp.cop.pay.dto.wechatpay;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;

/**
 * 签名返回实体类 PrepayResponseDto
 * 
 */
@Data
@Accessors(chain = true)
public class PrepayResponseDto {
    /**
     * 应用ID
     */
    @ApiModelProperty(value = "应用ID")
    private String appId;
    /**
     * 时间戳
     */
    @ApiModelProperty(value = "时间戳")
    private String timeStamp;
    /**
     * 随机字符串
     */
    @ApiModelProperty(value = "随机字符串")
    private String nonceStr;
    /**
     * 订单详情扩展字符串
     */
    @ApiModelProperty(value = "订单详情扩展字符串")
    @JsonProperty("package")
    private String packageStr;
    /**
     * 签名方式
     */
    @ApiModelProperty(value = "签名方式")
    private String signType;
    /**
     * 签名
     */
    @ApiModelProperty(value = "签名")
    private String paySign;

    @ApiModelProperty(value = "是否发起成功")
    boolean isSuc;
}

WechatPayServiceImpl 接口实现类

package com.baymax.vpp.cop.pay.payment.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baymax.core.redis.cache.HulkRedis;
import com.baymax.core.secure.utils.AuthUtil;
import com.baymax.core.tool.api.R;
import com.baymax.core.tool.utils.Func;
import com.baymax.core.tool.utils.ObjectUtil;
import com.baymax.vpp.cop.common.enums.pay.ENewPayChannel;
import com.baymax.vpp.cop.common.enums.pay.ENoticeStatus;
import com.baymax.vpp.cop.common.enums.pay.EPayClient;
import com.baymax.vpp.cop.common.enums.pay.EPayStatus;
import com.baymax.vpp.cop.common.exception.ServiceException;
import com.baymax.vpp.cop.common.utils.DateUtils;
import com.baymax.vpp.cop.core.entity.OrderPayment;
import com.baymax.vpp.cop.core.feign.ICoreFeign;
import com.baymax.vpp.cop.core.feign.IPayCallbackClient;
import com.baymax.vpp.cop.pay.constant.WxPayV3Bean;
import com.baymax.vpp.cop.pay.dto.PayResult;
import com.baymax.vpp.cop.pay.dto.PayResultCallback;
import com.baymax.vpp.cop.pay.dto.WechatPayResult;
import com.baymax.vpp.cop.pay.dto.mpWechat.RefundRequest;
import com.baymax.vpp.cop.pay.dto.mpWechat.WechatRefundVO;
import com.baymax.vpp.cop.pay.dto.wechatpay.PrepayRequestDto;
import com.baymax.vpp.cop.pay.dto.wechatpay.PrepayResponseDto;
import com.baymax.vpp.cop.pay.entity.Merchant;
import com.baymax.vpp.cop.pay.entity.PayOrder;
import com.baymax.vpp.cop.pay.entity.TenantWechatConfig;
import com.baymax.vpp.cop.pay.mapper.CoreOrderPaymentMapper;
import com.baymax.vpp.cop.pay.payment.WechatPayService;
import com.baymax.vpp.cop.pay.service.IMerchantService;
import com.baymax.vpp.cop.pay.service.IOrderService;
import com.baymax.vpp.cop.pay.service.ITenantWechatConfigService;
import com.baymax.vpp.cop.pay.utils.WechatPayUtils;
import com.baymax.vpp.cop.pay.vo.ChinaUmsRefundVo;
import com.baymax.vpp.cop.pay.vo.wechatFen.WcMerchantConfigVo;
import com.baymax.vpp.cop.pay.wechat.pay.*;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.cert.SafeSingleScheduleExecutor;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 微信小程序支付
 * 
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WechatPayServiceImpl implements WechatPayService {
    private final WechatPayManager wechatPayManager;
    private final IMerchantService iMerchantService;
    private final ITenantWechatConfigService iTenantWechatConfigService;
    private final IOrderService iOrderService;
    private final ICoreFeign iCoreFeign;
    private ScheduledExecutorService executor;
    private final List<OrderRequest> orderRequests = new ArrayList<>();
    private final HulkRedis hulkRedis;
    private final IPayCallbackClient payCallbackClient;

    @Autowired
    CoreOrderPaymentMapper orderPaymentMapper;

    @Autowired
    WxPayV3Bean wxPayV3Bean;

    @Value("${wechatPay.notifyUrl.callback}")
    private String notifyUrlCallback;

    @Value("${wechatPay.notifyUrl.refundCallback}")
    private String notifyUrlRefundCallback;


    /**
     * 基础下单
     *
     * @param request
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public WechatPayResult prepay(PrepayRequestDto request) {
        String notifyUrl = notifyUrlCallback;
        Long id = IdUtil.getSnowflakeNextId();
        // 获取用户openid
        String openid = request.getOpenId();
        if (StrUtil.isEmpty(request.getOpenId())) {
            openid = AuthUtil.getUserAccount();
        }
        // 调用微信支付下单
        PrepayRequest req = new PrepayRequest()
                .setAppid(wxPayV3Bean.getAppId())
                .setMchid(wxPayV3Bean.getMchId())
                .setOutTradeNo(request.getWorkId()) // 订单支付表 id 传给微信 存到  cop_pay_order 里面的 workId 字段
                .setDescription(request.getDescription())
                //   外网回调地址
                .setNotifyUrl(notifyUrl)
                .setAmount(new PrepayRequest.Amount().setTotal(request.getAmount()))
                .setPayer(new PrepayRequest.Payer(openid));
        PrepayResponse res = wechatPayManager.prepay(req);

        // 创建支付记录
        PayOrder payOrder = new PayOrder();
        payOrder.setId(id);
        payOrder.setMerchantId(wxPayV3Bean.getMchId());
        payOrder.setWorkId(request.getWorkId());
        payOrder.setResMerchantId(Func.toStr(request.getResMerchantId()));
        payOrder.setPayStatus(EPayStatus.paying);
        payOrder.setCreateTime(new Date());
        payOrder.setOrderType("pay");
        payOrder.setPayClient(EPayClient.wechatMp);
        payOrder.setPayChannel(ENewPayChannel.wechatMiniProgram);
        payOrder.setNoticeStatus(ENoticeStatus.tempFail);
        payOrder.setNoticeUrl(notifyUrl);
        payOrder.setAmount(Long.valueOf(String.valueOf(request.getAmount())));
        iOrderService.save(payOrder);
        // 组织前端发起支付参数
        PrepayResponseDto response = new PrepayResponseDto();
        String nonceStr = IdUtil.objectId();
        response.setAppId(wxPayV3Bean.getAppId())
                .setTimeStamp(String.valueOf(System.currentTimeMillis() / 1000))
                .setNonceStr(nonceStr)
                .setPackageStr("prepay_id=" + res.getPrepayId())
                .setSignType("RSA");

        // 支付参数签名
        PrepayResponseDto sign = sign(response);
        WechatPayResult result = new WechatPayResult();
        com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
        jsonObject.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
        jsonObject.put("nonceStr", response.getNonceStr());
        jsonObject.put("package", response.getPackageStr());
        jsonObject.put("signType", response.getSignType());
        jsonObject.put("paySign", sign.getPaySign());

        result.setMiniPayRequest(jsonObject);
        result.setResultCode(0);
        result.setErrorMsg("操作成功");
//        result.setOrderId( orderId );
        return result;
    }

    /**
     * 微信支付订单号查询订单
     *
     * @param transactionId 微信支付订单号查询订单
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Transaction queryOrder(String transactionId) {
        Transaction transaction = wechatPayManager.queryOrder(transactionId);
        return transaction;
    }


    /**
     * 商户订单号查询订单
     *
     * @param outTradeNo 商户订单号查询订单
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Transaction queryOrderByOutTradeNo(String outTradeNo) {
        Transaction transaction = wechatPayManager.queryOrderByOutTradeNo(outTradeNo);
        return transaction;
    }


    //微信支付回调通知
    @Override
    public void callback(HttpServletRequest servletRequest, HttpServletResponse response) throws Exception {
        log.info("----------->微信支付回调开始");
        Map<String, String> map = new HashMap<>();
        String timeStamp = servletRequest.getHeader("Wechatpay-Timestamp");
        String nonce = servletRequest.getHeader("Wechatpay-Nonce");
        String signature = servletRequest.getHeader("Wechatpay-Signature");
        String certSn = servletRequest.getHeader("Wechatpay-Serial");
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(servletRequest.getInputStream()))) {
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line);
            }
            String obj = stringBuilder.toString();
            log.info("微信支付回调请求参数:{},{},{},{},{}", obj, timeStamp, nonce, signature, certSn);
            WechatPayUtils.getPrivateKey();
            Verifier verifier = WechatPayUtils.getVerifier(wxPayV3Bean.getMchId(), wxPayV3Bean.getApiKey3());

            String sn = verifier.getValidCertificate().getSerialNumber().toString(16).toUpperCase(Locale.ROOT);
            NotificationRequest request = new NotificationRequest.Builder().withSerialNumber(sn)
                    .withNonce(nonce)
                    .withTimestamp(timeStamp)
                    .withSignature(signature)
                    .withBody(obj)
                    .build();
            NotificationHandler handler = new NotificationHandler(verifier, wxPayV3Bean.getApiKey3().getBytes(StandardCharsets.UTF_8));
            // 验签和解析请求体
            com.wechat.pay.contrib.apache.httpclient.notification.Notification notification = handler.parse(request);
            JSONObject res = JSONObject.parseObject(notification.getDecryptData());

            log.info("微信支付回调响应参数:{}", res.toJSONString());
            if (res != null) {
                //如果支付成功
                String tradeState = res.getString("trade_state");
                if ("SUCCESS".equals(tradeState)) {
                    //拿到商户-订单支付表 id
                    String outTradeNo = res.getString("out_trade_no");   // 订单支付表 id 传给微信 存到 cop_pay_order 里面的 workId 字段
                    String transactionId = res.getString("transaction_id");  // 微信支付系统生成的订单号
                    JSONObject amountJson = res.getJSONObject("amount");
                    int payerTotal = amountJson.getInteger("payer_total");

                    hulkRedis.set("支付订单号-paymentId: " + outTradeNo, outTradeNo + " - " + transactionId + " - " + amountJson + " - " + DateUtils.dateToStr(new Date()));
                    LambdaQueryWrapper<PayOrder> queryWrapper = new LambdaQueryWrapper<>();
                    queryWrapper.eq(PayOrder::getWorkId, outTradeNo);
                    List<PayOrder> list = iOrderService.list(queryWrapper);
                    if (list == null || list.size() == 0) {
                        response.setStatus(500);
                        map.put("code", "ERROR");
                        map.put("message", "未查询到支付信息!");
                        response.setHeader("Content-type", ContentType.JSON.toString());
                        response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
                        response.flushBuffer();
                    }
                    PayOrder payOrder = list.get(0);
                    if (payOrder != null) {
                        if (payOrder.getPayStatus() == EPayStatus.suc) {
                            //如果支付状态为suc 说明订单已经支付成功了,直接响应微信服务器返回成功
                            map.put("code", "SUCCESS");
                            map.put("message", "SUCCESS");
                            response.setStatus(200);
                            response.setHeader("Content-type", ContentType.JSON.toString());
                            response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
                            response.flushBuffer();
                        }
                        //验证用户支付的金额和订单金额是否一致  paying
                        if (payOrder.getPayStatus() != EPayStatus.suc) {
                            if (payerTotal == payOrder.getAmount().intValue()) {
                                //修改订单状态
                                PayOrder payOrderUpdate = new PayOrder();
                                payOrderUpdate.setId(payOrder.getId());
                                payOrderUpdate.setPayStatus(EPayStatus.suc);
                                payOrderUpdate.setFlowNum(transactionId);   // 微信支付系统生成的订单号
                                payOrderUpdate.setUpdateTime(new Date());
                                // 修改订单状态
                                iOrderService.updateById(payOrderUpdate);

                                // 支付成功 修改订单的充电状态
                                PayResultCallback callback = new PayResultCallback();
                                callback.setWorkId(payOrder.getWorkId());
                                callback.setPayStatus(payOrderUpdate.getPayStatus());
                                callback.setFailMsg(payOrder.getFailMsg());
                                payCallbackClient.payResultCallback(callback);

                                //响应微信服务器
                                map.put("code", "SUCCESS");
                                map.put("message", "SUCCESS");
                                response.setStatus(200);
                                response.setHeader("Content-type", ContentType.JSON.toString());
                                response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
                                response.flushBuffer();
                            }
                        }
                    }
                } else {
                    map.put("code", "SUCCESS");
                    map.put("message", "SUCCESS");
                    response.setStatus(200);
                    response.setHeader("Content-type", ContentType.JSON.toString());
                    response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
                    response.flushBuffer();
                }
            } else {
                map.put("code", "SUCCESS");
                map.put("message", "SUCCESS");
                response.setStatus(200);
                response.setHeader("Content-type", ContentType.JSON.toString());
                response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
                response.flushBuffer();
            }
        } catch (Exception e) {
            log.error("微信支付回调失败 error!", e);
            response.setStatus(500);
            map.put("code", "ERROR");
            map.put("message", "签名错误");
            response.setHeader("Content-type", ContentType.JSON.toString());
            response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
            response.flushBuffer();
        }
    }


    /**
     * 微信小程序-退款申请
     *
     * @param request
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<ChinaUmsRefundVo> refundWechat(RefundRequest request) {
        String notifyUrl = notifyUrlRefundCallback;
        RefundRequest.Amount amount = request.getAmount();
        int refund = amount.getRefund();
        int total = amount.getTotal();
        if (refund > total) {
            return R.fail("退款金额不能大于订单支付总金额");
        }
        // 查询订单对应的支付记录  EFeeType.chargeFee
        PayOrder payOrder = orderPaymentMapper.selectPayOrderByOrderId(request.getOutTradeNo(), "suc", "chargeFee");
        if (payOrder == null) {
            return R.fail("未查询到付款记录!");
        }
        request.setNotifyUrl(notifyUrl);
        request.setTransactionId(payOrder.getFlowNum());
        request.setOutRefundNo(System.currentTimeMillis() + "");
        request.setOutTradeNo(payOrder.getWorkId());
        ChinaUmsRefundVo res = wechatPayManager.refundWechat(request);
        return R.data(res);
    }


    // 微信小程序-退款申请-回调
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refundCallback(HttpServletRequest servletRequest, HttpServletResponse response) {
        log.info("----------->微信小程序-退款申请-回调");
        Map<String, String> map = new HashMap<>(12);
        String timeStamp = servletRequest.getHeader("Wechatpay-Timestamp");
        String nonce = servletRequest.getHeader("Wechatpay-Nonce");
        String signature = servletRequest.getHeader("Wechatpay-Signature");
        String certSn = servletRequest.getHeader("Wechatpay-Serial");
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(servletRequest.getInputStream()))) {
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line);
            }
            String obj = stringBuilder.toString();
            log.info("微信退款回调请求参数:{},{},{},{},{}", obj, timeStamp, nonce, signature, certSn);
            WechatPayUtils.getPrivateKey();
            Verifier verifier = WechatPayUtils.getVerifier(wxPayV3Bean.getMchId(), wxPayV3Bean.getApiKey3());
            String sn = verifier.getValidCertificate().getSerialNumber().toString(16).toUpperCase(Locale.ROOT);
            NotificationRequest request = new NotificationRequest.Builder().withSerialNumber(sn)
                    .withNonce(nonce)
                    .withTimestamp(timeStamp)
                    .withSignature(signature)
                    .withBody(obj)
                    .build();
            NotificationHandler handler = new NotificationHandler(verifier, wxPayV3Bean.getApiKey3().getBytes(StandardCharsets.UTF_8));
            // 验签和解析请求体
            com.wechat.pay.contrib.apache.httpclient.notification.Notification notification = handler.parse(request);
            JSONObject res = JSONObject.parseObject(notification.getDecryptData());

            log.info("微信退款回调响应参数:{}", res.toJSONString());
            if (res != null) {
                //如果退款成功
                String tradeState = res.getString("refund_status");
                if ("SUCCESS".equals(tradeState)) {
                    //拿到商户订单号
                    String outTradeNo = res.getString("out_trade_no");   // 返回的商户订单号   订单支付表 id 传给微信 存到 cop_pay_order 里面的 workId 字段
/*//                    String transactionId = res.getString("transaction_id");  //微信支付订单号
                    String refundPaymentId = res.getString("out_refund_no");  // 商户退款单号
                    String refundId = res.getString("refund_id");  // 微信退款单号
                    JSONObject amountJson = res.getJSONObject("amount");
                    // 退款金额,币种的最小单位,只能为整数,不能超过原订单支付金额,如果有使用券,后台会按比例退。
                    int refund = amountJson.getInteger("refund");*/

                    OrderPayment orderPayment = orderPaymentMapper.selectById(outTradeNo);
                    if (orderPayment != null) {
                        //如果退款状态为 refundSuc 说明订单已经退款成功了,直接响应微信服务器返回成功
                        if (orderPayment.getPayStatus() == EPayStatus.refundSuc) {
                            response.setStatus(200);
                            map.put("code", "SUCCESS");
                            map.put("message", "SUCCESS");
                            response.setHeader("Content-type", ContentType.JSON.toString());
                            response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
                            response.flushBuffer();
                        }
                        if (orderPayment.getPayStatus() != EPayStatus.refundSuc) {
                            //修改订单状态
                            OrderPayment payment = new OrderPayment();
                            payment.setId(orderPayment.getId());
                            payment.setPayStatus(EPayStatus.refundSuc);
                            payment.setUpdateTime(new Date());
                            orderPaymentMapper.updateById(payment);
                            //响应微信服务器
                            response.setStatus(200);
                            map.put("code", "SUCCESS");
                            map.put("message", "SUCCESS");
                            response.setHeader("Content-type", ContentType.JSON.toString());
                            response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
                            response.flushBuffer();
                        }
                    }
                }
            } else {
                response.setStatus(500);
                map.put("code", "ERROR");
                map.put("message", "签名错误");
                response.setHeader("Content-type", ContentType.JSON.toString());
                response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
                response.flushBuffer();
            }
        } catch (Exception e) {
            log.error("微信支付回调失败", e);
        }
    }


    /**
     * 查询单笔退款(通过商户退款单号)
     *
     * @param outRefundNo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public WechatRefundVO selectRefundByOutRefundNo(String outRefundNo) {
        WechatRefundVO res = wechatPayManager.selectRefundByOutRefundNo(outRefundNo);
        return res;
    }


 
}

WechatPayService接口

package com.baymax.vpp.cop.pay.payment;

import com.baymax.core.tool.api.R;
import com.baymax.vpp.cop.pay.dto.WechatPayResult;
import com.baymax.vpp.cop.pay.dto.mpWechat.RefundRequest;
import com.baymax.vpp.cop.pay.dto.mpWechat.WechatRefundVO;
import com.baymax.vpp.cop.pay.dto.wechatpay.PrepayRequestDto;
import com.baymax.vpp.cop.pay.vo.ChinaUmsRefundVo;
import com.baymax.vpp.cop.pay.wechat.pay.Transaction;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 微信小程序支付
 */

public interface WechatPayService {


    /**
     * 基础下单
     *
     * @param request
     * @return
     */
    public WechatPayResult prepay(PrepayRequestDto request);

    /**
     * 微信支付回调通知
     * @param servletRequest
     * @param response
     * @throws Exception
     */
    public void callback(HttpServletRequest servletRequest, HttpServletResponse response) throws Exception;


    /**
     * 查询订单支付结果
     *
     * @param transactionId 微信支付订单号
     * @return
     */
    public Transaction queryOrder(String transactionId);


    /**
     * 商户订单号查询订单
     *
     * @param outTradeNo 商户订单号查询订单
     * @return
     */
    public Transaction queryOrderByOutTradeNo(String outTradeNo);


    /**
     * 退款申请
     *
     * @param request
     * @return
     */
    public R<ChinaUmsRefundVo> refundWechat(RefundRequest request);


    //微信小程序-退款申请-回调
    public void refundCallback(HttpServletRequest servletRequest, HttpServletResponse response);

    /**
     * 查询单笔退款(通过商户退款单号)
     *
     * @param outRefundNo
     * @return
     */
    public WechatRefundVO selectRefundByOutRefundNo(String outRefundNo);
}

WechatPayController

package com.baymax.vpp.cop.pay.controller;


import com.baymax.core.log.annotation.ApiLog;
import com.baymax.core.tool.api.R;
import com.baymax.vpp.cop.pay.dto.WechatPayResult;
import com.baymax.vpp.cop.pay.dto.mpWechat.RefundRequest;
import com.baymax.vpp.cop.pay.dto.mpWechat.WechatRefundVO;
import com.baymax.vpp.cop.pay.dto.wechatpay.PrepayRequestDto;
import com.baymax.vpp.cop.pay.payment.WechatPayService;
import com.baymax.vpp.cop.pay.vo.ChinaUmsRefundVo;
import com.baymax.vpp.cop.pay.wechat.pay.Transaction;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * 微信原生-微信小程序支付
 */
@RestController
@AllArgsConstructor
@RequestMapping("/wechatPay")
@Api(value = "微信原生-微信小程序支付", tags = "微信原生-微信小程序支付")
public class WechatPayController {


    @Autowired
    private WechatPayService wechatPayService;


    /**
     * 基础下单
     *
     * @param request
     * @return
     * @throws Exception
     */
    @ApiOperation(value = "基础下单")
    @ApiLog("基础下单")
    @PostMapping("/prepay")
    public WechatPayResult prepay(@RequestBody PrepayRequestDto request) throws Exception {
        WechatPayResult prepay = wechatPayService.prepay(request);
        return prepay;
    }

    /**
     * 微信支付订单号查询订单
     *
     * @param transactionId 微信支付订单号
     * @return
     */
    @ApiOperation(value = "微信支付订单号查询订单")
    @ApiLog("微信支付订单号查询订单")
    @ResponseBody
    @GetMapping("/queryOrder")
    public Transaction queryOrder(String transactionId) throws Exception {
        return wechatPayService.queryOrder(transactionId);
    }


    /**
     * 商户订单号查询订单
     *
     * @param outTradeNo 商户订单号查询订单
     * @return
     */
    @ApiOperation(value = "商户订单号查询订单")
    @ApiLog("商户订单号查询订单")
    @ResponseBody
    @GetMapping("/queryOrderByOutTradeNo")
    public Transaction queryOrderByOutTradeNo(@RequestParam("outTradeNo") String outTradeNo) throws Exception {
        return wechatPayService.queryOrderByOutTradeNo(outTradeNo);
    }


    /**
     * 处理微信支付回调
     *
     * @param servletRequest
     * @param response
     */
    @ApiOperation(value = "处理微信支付回调")
    @ApiLog("处理微信支付回调")
    @ResponseBody
    @RequestMapping("/callback")
    public void callback(HttpServletRequest servletRequest, HttpServletResponse response) throws Exception {
        wechatPayService.callback(servletRequest, response);
    }


    /**
     * 微信小程序-退款申请
     *
     * @param request
     * @return
     */
    @ApiOperation(value = "退款申请")
    @ApiLog("微信小程序-退款申请")
    @PostMapping("/refundWechat")
    public R<ChinaUmsRefundVo> refundWechat(@RequestBody RefundRequest request) {
        return wechatPayService.refundWechat(request);
    }


    /**
     * 微信小程序-退款申请 回调
     *
     * @param servletRequest
     * @param response
     */
    @ApiOperation(value = "微信小程序-退款申请-回调")
    @ApiLog("微信小程序-退款申请-回调")
    @ResponseBody
    @RequestMapping("/refundCallback")
    public void refundCallback(HttpServletRequest servletRequest, HttpServletResponse response) throws Exception {
        wechatPayService.refundCallback(servletRequest, response);
    }


    /**
     * 查询单笔退款(通过商户退款单号)
     *
     * @param outRefundNo
     * @return
     */
    @ApiOperation(value = "查询单笔退款(通过商户退款单号) ")
    @ApiLog("查询单笔退款(通过商户退款单号) ")
    @PostMapping("/selectRefundByOutRefundNo")
    public WechatRefundVO selectRefundByOutRefundNo(@RequestParam("tenantId") String outRefundNo) {
        return wechatPayService.selectRefundByOutRefundNo(outRefundNo);
    }


}

支付、退款回调配置

wechatPay:
  notifyUrl:
    callback: http://IP:端口/wechatPay/callback
    refundCallback: http://IP:端口/wechatPay/refundCallback

微信小程序 预支付响应结果 WechatPayResult

package com.baymax.vpp.cop.pay.dto;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;

/**
 * 微信小程序 预支付响应结果
 */
@Data
public class WechatPayResult implements Serializable {
    private static final long serialVersionUID = 1L;

    @ApiModelProperty("结果码,0成功 1失败")
    Integer resultCode = 1;

    @ApiModelProperty("错误码")
    Integer errorCode = 99;

    @ApiModelProperty("描述消息")
    String errorMsg = "请重新操作";

    @ApiModelProperty("订单id")
    Long orderId;

    @ApiModelProperty(value = "小程序支付用的请求报文,带有签名信息")
    private Object miniPayRequest;

}

微信支付订单  Transaction

package com.baymax.vpp.cop.pay.wechat.pay;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.ToString;
import lombok.experimental.Accessors;

/**
 * 微信支付订单  Transaction
 */
@Data
@ToString
@Accessors(chain = true)
public class Transaction {
    /**
     * amount
     */
    private TransactionAmount amount;
    /**
     * appid
     */
    private String appid;
    /**
     * mchid
     */
    private String mchid;
    /**
     * outTradeNo
     */
    @JsonProperty("out_trade_no")
    private String outTradeNo;
    /**
     * payer
     */
    private TransactionPayer payer;
    /**
     * successTime
     */
    @JsonProperty("success_time")
    private String successTime;
    /**
     * tradeState
     */
    @JsonProperty("trade_state")
    private TradeStateEnum tradeState;
    /**
     * tradeStateDesc
     */
    @JsonProperty("trade_state_desc")
    private String tradeStateDesc;
    /**
     * transactionId
     */
    @JsonProperty("transaction_id")
    private String transactionId;

    public enum TradeStateEnum {
        SUCCESS,
        REFUND,
        NOTPAY,
        CLOSED,
        REVOKED,
        USERPAYING,
        PAYERROR,
        ACCEPT
    }

    @Data
    @Accessors(chain = true)
    public static class TransactionAmount {
        /**
         * currency
         */
        private String currency;
        /**
         * payerCurrency
         */
        @JsonProperty("payer_currency")
        private String payerCurrency;
        /**
         * payerTotal
         */
        @JsonProperty("payer_total")
        private Integer payerTotal;
        /**
         * total
         */
        private Integer total;
    }

    @Data
    @Accessors(chain = true)
    public static class TransactionPayer {
        /**
         * openid
         */
        private String openid;
    }

}

基础下单请求DTO PrepayRequestDto 

package com.baymax.vpp.cop.pay.dto.wechatpay;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;

import javax.validation.constraints.NotNull;

/**
 * 基础下单请求DTO PrepayRequestDto 
 */
@Data
@Accessors(chain = true)
public class PrepayRequestDto {
    /**
     * 业务流水号   订单支付表 id
     */
    @ApiModelProperty(value = "业务流水号", required = true)
    private String workId;
    /**
     * 来源(外部)商户ID
     */
    @ApiModelProperty(value = "来源(外部)商户ID", required = true)
    private Long resMerchantId;
    /**
     * 商品描述
     */
    @ApiModelProperty(value = "商品描述", required = true)
    private String description;
    /**
     * 总金额 说明:订单总金额,单位为分
     */
    @ApiModelProperty(value = "总金额", required = true)
    private Integer amount;

    @NotNull
    @ApiModelProperty("微信用户标识")
    private String openId;


}

微信小程序支付 退款请求   RefundRequest

package com.baymax.vpp.cop.pay.dto.mpWechat;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

/**
 * 微信小程序支付 退款请求   RefundRequest
 */
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@Accessors(chain = true)
public class RefundRequest {


    /**
     * 【商户订单号】 原支付交易对应的商户订单号,与transaction_id二选一
     */
    @JsonProperty("out_trade_no")
    private String outTradeNo;


    /**
     * 【微信支付订单号】 原支付交易对应的微信订单号,与out_trade_no二选一
     */
    @JsonProperty("transaction_id")
    private String transactionId;


    /**
     * 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。
     */
    @JsonProperty("out_refund_no")
    private String outRefundNo;


    // 【金额信息】 订单金额信息
    private Amount amount;


    // 【退款结果回调url】 异步接收微信支付退款结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 如果参数中传了notify_url,则商户平台上配置的回调地址将不会生效,优先回调当前传的这个地址。
    @JsonProperty("notify_url")
    private String notifyUrl;


    @Data
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @Accessors(chain = true)
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Amount {

        // 【退款金额】 退款金额,单位为分,只能为整数,不能超过原订单支付金额。
        private Integer refund;

        // 原支付交易的订单总金额,单位为分,只能为整数。
        private Integer total;

        // 【退款币种】 符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。
        private String currency;
    }


}

基础下单请求  PrepayRequest

package com.baymax.vpp.cop.pay.wechat.pay;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

/**
 * 基础下单请求  PrepayRequest
 * 
 */
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@Accessors(chain = true)
public class PrepayRequest {
    /**
     * 公众号ID 说明:公众号ID
     */
    private String appid;
    /**
     * 直连商户号 说明:直连商户号
     */
    private String mchid;
    /**
     * 商品描述 说明:商品描述
     */
    private String description;
    /**
     * 商户订单号 说明:商户-订单号
     */
    @JsonProperty("out_trade_no")
    private String outTradeNo;
    /**
     * 交易结束时间 说明:订单失效时间,格式为rfc3339格式
     */
    @JsonProperty("time_expire")
    private String timeExpire;
    /**
     * 附加数据 说明:附加数据
     */
    private String attach;
    /**
     * 通知地址 说明:有效性:1. HTTPS;2. 不允许携带查询串。
     */
    @JsonProperty("notify_url")
    private String notifyUrl;
    /**
     * amount  【订单金额】 订单金额信息
     */
    private Amount amount;
    /**
     * payer  appid
     */
    private Payer payer;

    @Data
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @Accessors(chain = true)
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Amount {
        /**
         * 总金额 说明:订单总金额,单位为分
         */
        private Integer total;
        /**
         * 货币类型 说明:CNY:人民币,境内商户号仅支持人民币。
         */
        private String currency;
    }

    @Data
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @Accessors(chain = true)
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Payer {
        /**
         * 用户标识 说明:用户在商户appid下的唯一标识。
         */
        private String openid;
    }
}

微信小程序支付 退款请求  RefundRequest

package com.baymax.vpp.cop.pay.dto.mpWechat;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

/**
 * 微信小程序支付 退款请求  RefundRequest
 */
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@Accessors(chain = true)
public class RefundRequest {


    /**
     * 【商户订单号】 原支付交易对应的商户订单号,与transaction_id二选一
     */
    @JsonProperty("out_trade_no")
    private String outTradeNo;


    /**
     * 【微信支付订单号】 原支付交易对应的微信订单号,与out_trade_no二选一
     */
    @JsonProperty("transaction_id")
    private String transactionId;


    /**
     * 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。
     */
    @JsonProperty("out_refund_no")
    private String outRefundNo;


    // 【金额信息】 订单金额信息
    private Amount amount;


    // 【退款结果回调url】 异步接收微信支付退款结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 如果参数中传了notify_url,则商户平台上配置的回调地址将不会生效,优先回调当前传的这个地址。
    @JsonProperty("notify_url")
    private String notifyUrl;


    @Data
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @Accessors(chain = true)
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Amount {

        // 【退款金额】 退款金额,单位为分,只能为整数,不能超过原订单支付金额。
        private Integer refund;

        // 原支付交易的订单总金额,单位为分,只能为整数。
        private Integer total;

        // 【退款币种】 符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。
        private String currency;
    }


}

退款视图对象  ChinaUmsRefundVo

package com.baymax.vpp.cop.pay.vo;

import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 *  退款视图对象  ChinaUmsRefundVo
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ChinaUmsRefundVo {

    @ApiModelProperty("退款结果")
    private Boolean isSuccess;
    @ApiModelProperty("错误编号")
    private String errCode;
    @ApiModelProperty("错误信息")
    private String errMsg;
    @ApiModelProperty("退款金额")
    private Long refundAmount;
}

微信小程序支付退款响应  WechatRefundVO

package com.baymax.vpp.cop.pay.dto.mpWechat;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

/**
 * 微信小程序支付退款响应  WechatRefundVO
 */
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@Accessors(chain = true)
public class WechatRefundVO {


    /**
     * 【微信支付退款号】 微信支付退款号
     */
    @JsonProperty("refund_id")
    private String refundId;

    // 【商户退款单号】 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。
    @JsonProperty("out_refund_no")
    private String outRefundNo;

    // 【微信支付订单号】 微信支付交易订单号
    @JsonProperty("transaction_id")
    private String transactionId;

    //【商户订单号】 原支付交易对应的商户订单号
    @JsonProperty("out_trade_no")
    private String outTradeNo;

    //【退款渠道】 退款渠道  可选取值:  ORIGINAL: 原路退款;  BALANCE: 退回到余额;  OTHER_BALANCE: 原账户异常退到其他余额账户;  OTHER_BANKCARD: 原银行卡异常退到其他银行卡;
    @JsonProperty("channel")
    private String channel;

    //退款入账账户】 取当前退款单的退款入账方,有以下几种情况: 1)退回银行卡:{银行名称}{卡类型}{卡尾号} 2)退回支付用户零钱:支付用户零钱 3)退还商户:商户基本账户商户结算银行账户 4)退回支付用户零钱通:支付用户零钱通
    @JsonProperty("user_received_account")
    private String userReceivedAccount;

    // 【退款创建时间】 退款受理时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日13点29分35秒。
    @JsonProperty("create_time")
    private String createTime;

    // 【退款状态】 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台(pay.weixin.qq.com)-交易中心,手动处理此笔退款。;可选取值:;SUCCESS: 退款成功;CLOSED: 退款关闭;PROCESSING: 退款处理中;ABNORMAL: 退款异常
    @JsonProperty("status")
    private String status;

    // 【金额信息】 金额详细信息
    private Amount amount;


    @Data
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @Accessors(chain = true)
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Amount {
        //总金额 说明:订单总金额,单位为分
        private Integer total;

        //【订单金额】 订单总金额,单位为分
        private Integer refund;

        // 【用户支付金额】 现金支付金额,单位为分,只能为整数
        @JsonProperty("payer_total")
        private Integer payerTotal;

        // 【用户退款金额】 退款给用户的金额,单位为分,不包含所有优惠券金额
        @JsonProperty("payer_refund")
        private Integer payerRefund;

        // 【应结退款金额】 去掉非充值代金券退款金额后的退款金额,单位为分,退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额
        @JsonProperty("settlement_refund")
        private Integer settlementRefund;

        // 【应结订单金额】 应结订单金额=订单金额-免充值代金券金额,应结订单金额<=订单金额,单位为分
        @JsonProperty("settlement_total")
        private Integer settlementTotal;


        // 【优惠退款金额】 优惠退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠,单位为分
        @JsonProperty("discount_refund")
        private Integer discountRefund;


        // 【退款币种】 符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。
        private String currency;


    }


}

支付订单表实体类   PayOrder

package com.baymax.vpp.cop.pay.entity;


import com.baomidou.mybatisplus.annotation.TableName;
import com.baymax.vpp.cop.common.enums.pay.ENewPayChannel;
import com.baymax.vpp.cop.common.enums.pay.ENoticeStatus;
import com.baymax.vpp.cop.common.enums.pay.EPayClient;
import com.baymax.vpp.cop.common.enums.pay.EPayStatus;
import io.swagger.annotations.ApiModel;
import lombok.Data;

import java.util.Date;

/**
 * 支付订单表实体类   PayOrder
 */
@Data
@TableName("cop_pay_order")
@ApiModel(value = "支付订单表对象", description = "支付订单表")
public class PayOrder {

    /**
     * id
     */
    private Long id;

    /**
     * 业务流水号(由发起支付的服务生成)( 微信支付系统生成的订单号-微信支付id)
     */
    private String workId;
    /**
     * 来源(外部)商户ID* 用于区分不同商户的支付订单(微信的商户ID/小桔的运营商ID...)
     */
    private String resMerchantId;
    /**
     * 商户ID
     */
    private String merchantId;

    /**
     * 支付金额(分)
     */
    private Long amount;

    /**
     * 支付渠道(微信分/微信扫码/支付宝扫码/银座钱包...)
     */
    private ENewPayChannel payChannel;
    /**
     * 支付客户端(支付宝/微信小程序/微信网页支付)
     */
    private EPayClient payClient;

    /**
     * 订单类型pay,refund
     */
    private String orderType;
    /**
     * 支付状态(init,paying,suc,fail,cancel,close)
     */
    private EPayStatus payStatus;
    /**
     * 交易流水号(平台返回) 微信支付系统生成的订单号
     */
    private String flowNum;
    /**
     * 通知URL(通知支付发起方支付状态)
     */
    private String noticeUrl;
    /**
     * 通知状态(init,tempFail,suc,fail)
     */
    private ENoticeStatus noticeStatus;
    /**
     * 支付失败说明
     */
    private String failMsg;

    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * 更新时间
     */
    private Date updateTime;

}

微信小程序 预支付响应结果 WechatPayResult 

package com.baymax.vpp.cop.pay.dto;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;

/**
 * 微信小程序 预支付响应结果 WechatPayResult 
 */
@Data
public class WechatPayResult implements Serializable {
    private static final long serialVersionUID = 1L;

    @ApiModelProperty("结果码,0成功 1失败")
    Integer resultCode = 1;

    @ApiModelProperty("错误码")
    Integer errorCode = 99;

    @ApiModelProperty("描述消息")
    String errorMsg = "请重新操作";

    @ApiModelProperty("订单id")
    Long orderId;

    @ApiModelProperty(value = "小程序支付用的请求报文,带有签名信息")
    private Object miniPayRequest;

}

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值