springboot集成微信支付V3 SDK

微信支付开通支付方法在这里可以参考一下:申请开通微信支付教程_个人怎么申请微信支付_郑鹏川的博客-CSDN博客

因为微信支付回调需要一个外网可访问的地址,我本地调试是采用的内网穿透的方式进行调试的。

Cpolar是一款内网穿透工具,它支持http/https/tcp协议,不限制流量,操作简单,无需公网IP,也无需路由器,可以轻松把服务暴露到公网访问。

cpolar官网:cpolar - 安全的内网穿透工具

1 下载安装cpolar内网穿透

访问cpolar官网,注册一个账号,并下载安装cpolar客户端。详细可以参考文档教程进行下载安装。

2 创建隧道
cpolar安装成功后,我们在浏览器上访问本地9200端口,登录Cpolar的web ui界面:http://localhost:9200。

点击左侧仪表盘的隧道管理——创建隧道,由于tomcat中配置的是82端口,因此我们要来创建一条http隧道,指向82端口:

隧道名称:可自定义,注意不要与已有隧道名称重复
协议:http协议
本地地址:82
域名类型:免费选择随机域名
地区:选择China top

 

点击左侧仪表盘的状态——在线隧道列表,可以看到刚刚创建的隧道已经有生成了相应的公网地址,一个http协议,一个https协议(免去配置ssl证书的繁琐步骤),将其复制想下来 

接下来我们就直接上代码了:

一、引入官方提供的pom依赖

<!--  微信支付 -->
<dependency>
  <groupId>com.github.wechatpay-apiv3</groupId>
  <artifactId>wechatpay-apache-httpclient</artifactId>
  <version>0.4.2</version>
</dependency>

二、配置微信支付必要的参数

weixin:
  appid:  # appid
  mch-serial-no:  # 证书序列号
  private-key-path: apiclient_key.pem # 证书路径  我这边保存在resource文件夹下读取文件时通过ClassPathResource去读取的
  mch-id: # 商户号
  key:  # api秘钥
  domain: https://api.mch.weixin.qq.com # 微信服务器地址
  notify-domain: http://303fe2d4.r5.cpolar.top/v3/wechat/callback # 回调,自己的回调地址需要外网可访问
 

三、编写微信支付配置类

import com.qz.common.exception.CommonException;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
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.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Data;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;

/**
 * @author lihao
 * @create 2023-03-22 17:24
 * @desc
 **/
@Configuration
@PropertySource("classpath:application.yml") //读取配置文件
@ConfigurationProperties(prefix = "weixin") //读取weixin节点
@Data //使用set方法将weixin节点中的值填充到当前类的属性中
public class WxPayConfig {

    // 商户号
    private String mchId;

    // 商户API证书序列号
    private String mchSerialNo;

    // 商户私钥文件
    private String privateKeyPath;

    // APIv3密钥
    private String key;

    // APPID
    private String appid;

    // 微信服务器地址
    private String domain;

    // 接收结果通知地址
    private String notifyDomain;

    /**
     * 获取商户的私钥文件
     *
     * @param filename 证书地址
     * @return 私钥文件
     */
    public PrivateKey getPrivateKey(String filename) {
        try {
            ClassPathResource classPathResource = new ClassPathResource(filename);
            return PemUtil.loadPrivateKey(classPathResource.getInputStream());
        } catch (IOException e) {
            throw new CommonException("私钥文件不存在");
        }
    }


    /**
     * 获取签名验证器
     */
    @Bean
    public Verifier getVerifier() {
        // 获取商户私钥
        final PrivateKey privateKey = getPrivateKey(privateKeyPath);
        // 私钥签名对象
        PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, privateKey);
        // 身份认证对象
        WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);
        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        try {
            // 向证书管理器增加需要自动更新平台证书的商户信息
            certificatesManager.putMerchant(mchId, wechatPay2Credentials, key.getBytes(StandardCharsets.UTF_8));
        } catch (IOException | GeneralSecurityException | HttpCodeException e) {
            e.printStackTrace();
        }
        try {
            return certificatesManager.getVerifier(mchId);
        } catch (NotFoundException e) {
            e.printStackTrace();
            throw new CommonException("获取签名验证器失败");
        }
    }


    /**
     * 获取微信支付的远程请求对象
     *
     * @return Http请求对象
     */
    @Bean
    public CloseableHttpClient getWxPayClient() {
        // 获取签名验证器
        Verifier verifier = getVerifier();
        // 获取商户私钥
        final PrivateKey privateKey = getPrivateKey(privateKeyPath);
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create().withMerchant(mchId, mchSerialNo, privateKey)
                .withValidator(new WechatPay2Validator(verifier));
        return builder.build();
    }
}

四、微信支付枚举类

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * @author lihao
 * @create 2023-03-22 17:21
 * @desc 微信支付枚举类
 **/
@AllArgsConstructor
@Getter
public enum WxApiType {

    /**
     * Native下单
     */
    NATIVE_PAY("/v3/pay/transactions/native"),


    /**
     * jsapi下单
     */
    JSAPI_PAY("/v3/pay/transactions/jsapi"),

    /**
     * jsapi下单
     */
    H5_PAY("/v3/pay/transactions/h5"),

    /**
     * APP下单
     */
    APP_PAY("/v3/pay/transactions/app"),

    /**
     * 查询订单
     */
    ORDER_QUERY_BY_NO("/v3/pay/transactions/out-trade-no/%s/?mchid=%s"),

    /**
     * 查询订单
     */
    ORDER_QUERY_BY_ID("/v3/pay/transactions/id/%s?mchid=%s"),

    /**
     * 关闭订单
     */
    CLOSE_ORDER_BY_NO("/v3/pay/transactions/out-trade-no/%s/close"),

    /**
     * 申请退款
     */
    DOMESTIC_REFUNDS("/v3/refund/domestic/refunds"),

    /**
     * 查询单笔退款
     */
    DOMESTIC_REFUNDS_QUERY("/v3/refund/domestic/refunds/%s"),

    /**
     * 申请交易账单
     */
    TRADE_BILLS("/v3/bill/tradebill"),

    /**
     * 申请资金账单
     */
    FUND_FLOW_BILLS("/v3/bill/fundflowbill"),

    /**
     * 发起商家转账
     */
    TRANSFER_BATCHES("/v3/transfer/batches");


    /**
     * 类型
     */
    private final String type;
}

五、微信支付工具类(这边以native扫码支付为例)

(1)创建native扫码支付工具类  后面需要用到时传入参数直接调用即可

/**
 * @author lihao
 * @create 2023-03-23 9:58
 * @desc Native扫码支付工具类
 **/
@Slf4j
public class WxPayNativeUtils {


    public static Map<String, Object> nativePay(WxPayConfig wxPayConfig, WeChatPayNativeParam param, CloseableHttpClient wxPayClient) throws Exception {
        Gson gson = new Gson();
        // 创建POST请求对象,里面输入的是地址,也就是那个https://api.wxpayxxxxx 那个,只不过我们直接去配置文件里面读取然后拼接出来了,懒得每次都输入一遍
        HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY.getType()));
        Map paramsMap =new HashMap();
        paramsMap.put("appid",wxPayConfig.getAppid()); // 我们的APPID
        paramsMap.put("mchid",wxPayConfig.getMchId()); // 我们的商户ID
        paramsMap.put("description",param.getDescription());  // 扫码之后显示的标题
        paramsMap.put("out_trade_no",param.getOutTradeNo()); // 商户订单号 我们自己生成的那个
        // 这里就是微信响应给我们系统的那个地址 必须是能外网访问的
        paramsMap.put("notify_url",wxPayConfig.getNotifyDomain());
        Map amountMap = new HashMap();
        amountMap.put("total",param.getTotal()); // 支付金额
        amountMap.put("currency","CNY"); // 交易货币的类型
        paramsMap.put("amount",amountMap);
        paramsMap.put("attach",param.getDeviceInfo());
        // 将这个json对象转换成字符串,用于后面进行网络传输
        String jsonParams = gson.toJson(paramsMap);
        log.info("请求微信支付V3 Native扫码支付接口参数 =========> " + jsonParams);
        // 设置请求体
        StringEntity entity = new StringEntity(jsonParams,"utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");
        //完成签名并执行请求
        CloseableHttpResponse response = wxPayClient.execute(httpPost);
        HashMap<String, Object> returnMap = new HashMap<>();
        try {
            String body = EntityUtils.toString(response.getEntity());
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) { //处理成功 有返回就会进入这里
                log.info("请求微信支付V3 Native扫码支付接口成功,返回结果 =========> " + body);
            } else if (statusCode == 204) { //处理成功,无返回就会进入到这里
                System.out.println("success");
            } else { // 接口调用失败的话就会进入这里
                log.info("Native下单失败,响应码 =====> " + statusCode+ ",返回结果= " + body);
                HashMap<String,String> resultMap = gson.fromJson(body, HashMap.class);
                returnMap.put("err_code_des",resultMap.get("message"));
            }
            // 相微信那边返回的结果   我们将json返回转成一个Map对象
            HashMap<String,String> resultMap = gson.fromJson(body, HashMap.class);
            // 然后从这个Map里面拿到code_url,这个是微信定义的,我们拿这个结果生成二维码
            String code_url = resultMap.get("code_url");

            returnMap.put("code_url",code_url);
            return returnMap;
        } finally {
            response.close();
        }
    }

}

(2)native支付成功回调工具类

/**
 * @author lihao
 * @create 2023-03-23 11:40
 * @desc
 **/
@Slf4j
public class WxPayCallbackUtil {

    /**
     * 微信支付创建订单回调方法
     * @param verifier 证书
     * @param wxPayConfig 微信配置
     * @param businessCallback 回调方法,用于处理业务逻辑
     * @return json格式的string数据,直接返回给微信
     */
    public static String wxPaySuccessCallback(HttpServletRequest request, HttpServletResponse response, Verifier verifier, WxPayConfig wxPayConfig, Consumer<WxchatCallbackSuccessResult> businessCallback) {
        Gson gson = new Gson();

        // 1.处理通知参数
        final String body = HttpUtils.readData(request);
        HashMap<String, Object> bodyMap = gson.fromJson(body, HashMap.class);

        // 2.签名验证
        WechatPayValidatorForRequest wechatForRequest = new WechatPayValidatorForRequest(verifier, body, (String) bodyMap.get("id"));
        try {
            if (!wechatForRequest.validate(request)) {
                // 通知验签失败
                response.setStatus(500);
                final HashMap<String, Object> map = new HashMap<>();
                map.put("code", "ERROR");
                map.put("message", "通知验签失败");
                return gson.toJson(map);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }


        // 3.获取明文数据
        String plainText = decryptFromResource(bodyMap,wxPayConfig);
        HashMap<String,Object> plainTextMap = gson.fromJson(plainText, HashMap.class);
        log.info("plainTextMap:{}",plainTextMap);
        // 4.封装微信返回的数据
        WxchatCallbackSuccessResult callbackData = new WxchatCallbackSuccessResult();
        callbackData.setSuccessTime(String.valueOf(plainTextMap.get("success_time")));
        callbackData.setOrderId(String.valueOf(plainTextMap.get("out_trade_no")));
        callbackData.setTransactionId(String.valueOf(plainTextMap.get("transaction_id")));
        callbackData.setTradestate(String.valueOf(plainTextMap.get("trade_state")));
        callbackData.setTradetype(String.valueOf(plainTextMap.get("trade_type")));
        callbackData.setAttach(String.valueOf(plainTextMap.get("attach")));
        String amount = String.valueOf(plainTextMap.get("amount"));
        HashMap<String,Object> amountMap = gson.fromJson(amount, HashMap.class);
        String total = String.valueOf(amountMap.get("total"));
        callbackData.setTotalMoney(new BigDecimal(total).movePointLeft(2));
        log.info("callbackData:{}",callbackData);

        if ("SUCCESS".equals(callbackData.getTradestate())) {
            // 执行业务逻辑
            businessCallback.accept(callbackData);
        }

        // 5.成功应答
        response.setStatus(200);
        final HashMap<String, Object> resultMap = new HashMap<>();
        resultMap.put("code", "SUCCESS");
        resultMap.put("message", "成功");
        return gson.toJson(resultMap);
    }

    /**
     * 对称解密
     */
    private static String decryptFromResource(HashMap<String, Object> bodyMap, WxPayConfig wxPayConfig) {
        // 通知数据
        Map<String, String> resourceMap = (Map) bodyMap.get("resource");
        // 数据密文
        String ciphertext = resourceMap.get("ciphertext");
        // 随机串
        String nonce = resourceMap.get("nonce");
        // 附加数据
        String associateData = resourceMap.get("associated_data");
        AesUtil aesUtil = new AesUtil(wxPayConfig.getKey().getBytes(StandardCharsets.UTF_8));
        try {
            return aesUtil.decryptToString(associateData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
            throw new CommonException("解密失败");
        }
    }
}

(3)请求参数

/**
 * @author lihao
 * @create 2023-03-23 10:02
 * @desc 微信Native支付参数
 **/
@Data
public class WeChatPayNativeParam {

    /**
     * 扫码之后显示的标题
     */
    private String description;

    /**
     * 商户订单号 我们自己生成的那个
     */
    private String outTradeNo;

    /**
     * 支付金额
     */
    private BigDecimal total;

    /**
     * 自定义参数  具体看业务要求  无要求可不填
     */
    private String deviceInfo;

}

(4)controller中调用native扫码支付工具类

@Autowired
private WxPayConfig wxPayConfig;

@Autowired
private CloseableHttpClient wxPayClient;

@ApiOperation("微信支付v3版返回链接")
@SaPlatCheckLogin
@PostMapping("/nativePay")
public CommonResult<WeChatCodeResult> nativePay(@RequestBody WeChatPayParam payParam) throws Exception {
        WeChatPayNativeParam param = new WeChatPayNativeParam();
        param.setDescription(payParam.getDescription());
        //这里注意要保持商户订单号唯一  
        param.setOutTradeNo(payParam.getOrderId()+ RandomUtil.randomNumbers(4));
        param.setDeviceInfo(payParam.getOrderId());
        BigDecimal money = new BigDecimal(0.01);
        param.setTotal((money.multiply(new BigDecimal(100))).setScale(0,BigDecimal.ROUND_HALF_DOWN));
        Map<String, Object> map = WxPayNativeUtils.nativePay(wxPayConfig,param,wxPayClient);
        if(ObjectUtil.isEmpty(map) || ObjectUtil.isNotEmpty(map.get("err_code_des"))){
            return CommonResult.data(new WeChatCodeResult(null,"FAIL",map.get("err_code_des").toString()));
        }
        if(ObjectUtil.isEmpty(map.get("code_url"))){
            throw new CommonException("生成支付二维码失败!");
        }
        return CommonResult.data(new WeChatCodeResult(map.get("code_url").toString(),"SUCCESS",null));
    }

@ApiOperation("微信支付回调接口")
@PostMapping("/callback")
public String courseNative(HttpServletRequest request, HttpServletResponse response) {
        return WxPayCallbackUtil.wxPaySuccessCallback(request, response, verifier, wxPayConfig, callbackData -> {
         log.info("微信支付返回的信息:{}", callbackData);
         //这里处理自己的业务
               weChatPayService.payOrder(callbackData.getAttach(),callbackData.getTotalMoney().toString(),        callbackData.getTransactionId());
        });
    }

六、附下其他几种支付方式

(1)微信退款工具类

/**
 * @author lihao
 * @create 2023-03-23 11:18
 * @desc 微信退款工具类
 **/
@Slf4j
public class WxPayRefundUtil {

    /**
     * 发起微信退款申请
     *
     * @param wxPayConfig 微信配置信息
     * @param param       微信支付申请退款请求参数
     * @param wxPayClient 微信请求客户端
     * @return
     */
    public static String refundPay(WxPayConfig wxPayConfig, WeChatRefundParam param, CloseableHttpClient wxPayClient) {
        // 1.获取请求参数的Map格式
        Map<String, Object> paramsMap = getRefundParams(param);
        // 2.获取请求对象
        HttpPost httpPost = getHttpPost(wxPayConfig, WxApiType.DOMESTIC_REFUNDS, paramsMap);
        // 3.完成签名并执行请求
        CloseableHttpResponse response = null;
        try {
            response = wxPayClient.execute(httpPost);
        } catch (IOException e) {
            e.printStackTrace();
            throw new CommonException("微信支付请求失败");
        }
        // 4.解析response对象
        HashMap<String, String> resultMap = resolverResponse(response);
        log.info("============>发起微信退款参数:{}",resultMap);
        if (resultMap != null) {
            // 返回微信支付退款单号
            return resultMap.get("out_refund_no");
        }
        return null;
    }

    /**
     * 查询单笔退款
     * @param wxPayConfig 微信配置信息
     * @param outRefundNo 商户退款单号
     * @param wxPayClient 微信请求客户端
     * @return
     */
    public static Map<String,String> refundQuery(WxPayConfig wxPayConfig, String outRefundNo, CloseableHttpClient wxPayClient) {
        // 1.请求路径和对象
        String url = String.format(wxPayConfig.getDomain().concat(WxApiType.DOMESTIC_REFUNDS_QUERY.getType()),outRefundNo);
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("Accept", "application/json");
        // 2.完成签名并执行请求
        CloseableHttpResponse response = null;
        try {
            response = wxPayClient.execute(httpGet);
        } catch (IOException e) {
            e.printStackTrace();
            throw new CommonException("微信支付请求失败");
        }
        // 3.解析返回的数据
        Map<String,String> map = resolverResponse(response);
        log.info("微信支付查询单笔退款信息========>{}",map);
        return map;

    }


    /**
     * 解析响应数据
     *
     * @param response 发送请求成功后,返回的数据
     * @return 微信返回的参数
     */
    private static HashMap<String, String> resolverResponse(CloseableHttpResponse response) {
        try {
            // 1.获取请求码
            int statusCode = response.getStatusLine().getStatusCode();
            // 2.获取返回值 String 格式
            final String bodyAsString = EntityUtils.toString(response.getEntity());

            Gson gson = new Gson();
            if (statusCode == 200) {
                // 3.如果请求成功则解析成Map对象返回
                HashMap<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
                return resultMap;
            } else {
                if (StringUtils.isNoneBlank(bodyAsString)) {
                    log.error("微信支付请求失败,提示信息:{}", bodyAsString);
                    // 4.请求码显示失败,则尝试获取提示信息
                    HashMap<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
                    throw new CommonException(resultMap.get("message"));
                }
                log.error("微信支付请求失败,未查询到原因,提示信息:{}", response);
                // 其他异常,微信也没有返回数据,这就需要具体排查了
                throw new IOException("request failed");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new CommonException(e.getMessage());
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 获取请求对象(Post请求)
     *
     * @param wxPayConfig 微信配置类
     * @param apiType     接口请求地址
     * @param paramsMap   请求参数
     * @return Post请求对象
     */
    private static HttpPost getHttpPost(WxPayConfig wxPayConfig, WxApiType apiType, Map<String, Object> paramsMap) {
        // 1.设置请求地址
        HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(apiType.getType()));
        // 2.设置请求数据
        Gson gson = new Gson();
        String jsonParams = gson.toJson(paramsMap);
        // 3.设置请求信息
        StringEntity entity = new StringEntity(jsonParams, "utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");
        return httpPost;
    }

    /**
     * 封装微信支付申请退款请求参数
     *
     * @param param 微信支付申请退款请求参数
     * @return 封装后的map微信支付申请退款请求参数对象
     */
    private static Map<String, Object> getRefundParams(WeChatRefundParam param) {
        Map<String, Object> paramsMap = new HashMap<>();
        if (StringUtils.isNoneBlank(param.getTransactionId())) {
            paramsMap.put("transaction_id", param.getTransactionId());
        } else if (StringUtils.isNoneBlank(param.getOrderId())) {
            paramsMap.put("out_trade_no", param.getOrderId());
        } else {
            throw new CommonException("微信支付订单号和商户订单号必须填写一个");
        }
        paramsMap.put("out_refund_no", param.getRefundOrderId());
        if (StringUtils.isNoneBlank(param.getReason())) {
            paramsMap.put("reason", param.getReason());
        }
        //paramsMap.put("notify_url", wxPayConfig.getNotifyDomain().concat(param.getNotify().getType()));
        Map<String, Object> amountMap = new HashMap<>();
        amountMap.put("refund", param.getRefundMoney().multiply(new BigDecimal(100)).setScale(0,BigDecimal.ROUND_HALF_DOWN));
        amountMap.put("total", param.getTotalMoney().multiply(new BigDecimal(100)).setScale(0,BigDecimal.ROUND_HALF_DOWN));
        amountMap.put("currency", "CNY");
        paramsMap.put("amount", amountMap);
        return paramsMap;
    }
}

(2)订单查询工具类

/**
 * @author lihao
 * @create 2023-03-23 15:49
 * @desc 微信支付查询订单信息工具类
 **/
@Slf4j
public class WxPaySearchOrderUtil {


    /**
     * 根据微信支付系统生成的订单号查询订单详情
     * @param wxPayConfig 微信支付配置信息
     * @param transactionId 微信支付系统生成的订单号
     * @param wxPayClient 微信支付客户端请求对象
     * @return 微信订单对象
     */
    public static WxchatCallbackSuccessResult searchByTransactionId(WxPayConfig wxPayConfig, String transactionId, CloseableHttpClient wxPayClient) {
        // 1.请求路径和对象
        String url = String.format(wxPayConfig.getDomain().concat(WxApiType.ORDER_QUERY_BY_ID.getType()),transactionId,wxPayConfig.getMchId());;
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("Accept", "application/json");
        // 2.完成签名并执行请求
        CloseableHttpResponse response = null;
        try {
            response = wxPayClient.execute(httpGet);
        } catch (IOException e) {
            e.printStackTrace();
            throw new CommonException("微信支付请求失败");
        }

        // 3.解析返回的数据
        WxchatCallbackSuccessResult callbackData = resolverResponse(response);
        log.info("微信支付根据订单号查询信息========>callbackData:{}",callbackData);
        return callbackData;
    }

    /**
     * 根据微信支付系统生成的订单号查询订单详情
     * @param wxPayConfig 微信支付配置信息
     * @param orderId 我们自己的订单id
     * @param wxPayClient 微信支付客户端请求对象
     * @return 微信订单对象
     */
    public static WxchatCallbackSuccessResult searchByOrderId(WxPayConfig wxPayConfig, String orderId, CloseableHttpClient wxPayClient) {
        // 1.请求路径和对象
        String url = String.format(wxPayConfig.getDomain().concat(WxApiType.ORDER_QUERY_BY_NO.getType()),orderId,wxPayConfig.getMchId());
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("Accept", "application/json");
        // 2.完成签名并执行请求
        CloseableHttpResponse response = null;
        try {
            response = wxPayClient.execute(httpGet);
        } catch (IOException e) {
            e.printStackTrace();
            throw new CommonException("微信支付请求失败");
        }
        // 3.解析返回的数据
        WxchatCallbackSuccessResult callbackData = resolverResponse(response);
        log.info("微信支付根据商户订单号查询信息========>callbackData:{}",callbackData);
        return callbackData;
    }


    /**
     * 解析响应数据
     * @param response 发送请求成功后,返回的数据
     * @return 微信返回的参数
     */
    private static WxchatCallbackSuccessResult resolverResponse(CloseableHttpResponse response) {
        try {
            // 1.获取请求码
            int statusCode = response.getStatusLine().getStatusCode();
            // 2.获取返回值 String 格式
            final String bodyAsString = EntityUtils.toString(response.getEntity());
            Gson gson = new Gson();
            if (statusCode == 200) {
                // 3.如果请求成功则解析成Map对象返回
                HashMap<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
                // 4.封装成我们需要的数据
                WxchatCallbackSuccessResult callbackData = new WxchatCallbackSuccessResult();
                callbackData.setSuccessTime(String.valueOf(resultMap.get("success_time")));
                callbackData.setOrderId(String.valueOf(resultMap.get("out_trade_no")));
                callbackData.setTransactionId(String.valueOf(resultMap.get("transaction_id")));
                callbackData.setTradestate(String.valueOf(resultMap.get("trade_state")));
                callbackData.setTradetype(String.valueOf(resultMap.get("trade_type")));
                callbackData.setAttach(String.valueOf(resultMap.get("attach")));
                String amount = String.valueOf(resultMap.get("amount"));
                HashMap<String,Object> amountMap = gson.fromJson(amount, HashMap.class);
                String total = String.valueOf(amountMap.get("total"));
                callbackData.setTotalMoney(new BigDecimal(total).movePointLeft(2));
                return callbackData;
            } else {
                if (StringUtils.isNoneBlank(bodyAsString)) {
                    log.error("微信支付请求失败,提示信息:{}", bodyAsString);
                    // 4.请求码显示失败,则尝试获取提示信息
                    HashMap<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
                    throw new CommonException(resultMap.get("message"));
                }
                log.error("微信支付请求失败,未查询到原因,提示信息:{}", response);
                // 其他异常,微信也没有返回数据,这就需要具体排查了
                throw new IOException("request failed");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new CommonException(e.getMessage());
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }



}

(3)商家发起转账工具类

/**
 * @author lihao
 * @create 2023-03-22 17:21
 * @desc 发起商家转账工具类
 **/
@Slf4j
public class WxPayTransferBatchesUtils {

    /**
     * 发起商家转账,支持批量转账
     *
     * @param wxPayConfig 微信配置信息
     * @param param 转账请求参数
     * @param wxPayClient 微信请求客户端()
     * @return 微信支付二维码地址
     */
    public static String transferBatches(WxPayConfig wxPayConfig, WechatTransferBatchesParam param, CloseableHttpClient wxPayClient) {
        // 1.获取请求参数的Map格式
        Map<String, Object> paramsMap = getParams(wxPayConfig, param);
        // 2.获取请求对象,WxApiType.TRANSFER_BATCHES="/v3/transfer/batches"
        HttpPost httpPost = getHttpPost(wxPayConfig, WxApiType.TRANSFER_BATCHES , paramsMap);
        // 3.完成签名并执行请求
        CloseableHttpResponse response = null;
        try {
            response = wxPayClient.execute(httpPost);
        } catch (IOException e) {
            e.printStackTrace();
            throw new CommonException("商家转账请求失败");
        }
        // 4.解析response对象
        HashMap<String, String> resultMap = resolverResponse(response);
        if (resultMap != null) {
            // batch_id微信批次单号,微信商家转账系统返回的唯一标识
            return resultMap.get("batch_id");
        }
        return null;
    }


    /**
     * 解析响应数据
     * @param response 发送请求成功后,返回的数据
     * @return 微信返回的参数
     */
    private static HashMap<String, String> resolverResponse(CloseableHttpResponse response) {
        try {
            // 1.获取请求码
            int statusCode = response.getStatusLine().getStatusCode();
            // 2.获取返回值 String 格式
            final String bodyAsString = EntityUtils.toString(response.getEntity());

            Gson gson = new Gson();
            if (statusCode == 200) {
                // 3.如果请求成功则解析成Map对象返回
                HashMap<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
                return resultMap;
            } else {
                if (StringUtils.isNoneBlank(bodyAsString)) {
                    log.error("商户转账请求失败,提示信息:{}", bodyAsString);
                    // 4.请求码显示失败,则尝试获取提示信息
                    HashMap<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
                    throw new CommonException(resultMap.get("message"));
                }
                log.error("商户转账请求失败,未查询到原因,提示信息:{}", response);
                // 其他异常,微信也没有返回数据,这就需要具体排查了
                throw new IOException("request failed");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new CommonException(e.getMessage());
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 获取请求对象(Post请求)
     *
     * @param wxPayConfig 微信配置类
     * @param apiType     接口请求地址
     * @param paramsMap   请求参数
     * @return Post请求对象
     */
    private static HttpPost getHttpPost(WxPayConfig wxPayConfig, WxApiType apiType, Map<String, Object> paramsMap) {
        // 1.设置请求地址
        HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(apiType.getType()));

        // 2.设置请求数据
        Gson gson = new Gson();
        String jsonParams = gson.toJson(paramsMap);

        // 3.设置请求信息
        StringEntity entity = new StringEntity(jsonParams, "utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");
        return httpPost;
    }

    /**
     * 封装转账请求参数
     *
     * @param wxPayConfig 微信的配置文件
     * @param param 批量转账请求数据
     * @return 封装后的map对象
     */
    private static Map<String, Object> getParams(WxPayConfig wxPayConfig, WechatTransferBatchesParam param) {
        Map<String, Object> paramsMap = new HashMap<>();
        paramsMap.put("appid", wxPayConfig.getAppid());
        paramsMap.put("out_batch_no", param.getBatchId());
        paramsMap.put("batch_name", param.getTitle());
        paramsMap.put("batch_remark", param.getRemark());
        paramsMap.put("total_amount", param.getTotalMoney().multiply(new BigDecimal("100")).intValue());
        paramsMap.put("total_num", param.getTransferDetailList().size());

        // 存储转账明细,一次最多三千笔
        if (param.getTransferDetailList().size() > 3000) {
            throw new CommonException("发起批量转账一次最多三千笔");
        }
        List<Map<String, Object>> detailList = new ArrayList<>();
        for (WechatTransferBatchesParam.transferDetail detail : param.getTransferDetailList()) {
            Map<String, Object> detailMap = new HashMap<>();
            detailMap.put("out_detail_no",detail.getBatchId());
            detailMap.put("transfer_amount",detail.getTotalDetailMoney().multiply(new BigDecimal("100")).intValue());
            detailMap.put("transfer_remark",detail.getDetailRemark());
            detailMap.put("openid",detail.getOpenid());
            detailList.add(detailMap);
        }
        paramsMap.put("transfer_detail_list", detailList);
        return paramsMap;
    }
}

本人针对支付方式做了一个简单的通用类(包含native方式、jsapi方式、app方式),如果用不到可不采用

支付通用参数

/**
 * @author lihao
 * @create 2023-03-22 17:48
 * @desc 微信支付通用参数
 **/
@Data
public class WeChatBasePayData {

    /**
     * 商品描述
     */
    private String title;

    /**
     * 商家订单号,对应 out_trade_no
     */
    private String orderId;

    /**
     * 订单金额
     */
    private BigDecimal price;

    /**
     * 回调地址
     */
    private String notify;

}

微信支付通用配置

/**
 * @author lihao
 * @create 2023-03-22 17:45
 * @desc 微信通用配置
 **/
@Slf4j
public class WxPayCommon {

    /**
     * 创建微信支付订单-Native方式
     *
     * @param wxPayConfig 微信配置信息
     * @param basePayData 基础请求信息,商品标题、商家订单id、订单价格
     * @param wxPayClient 微信请求客户端
     * @return 微信支付二维码地址
     */
    public static String wxNativePay(WxPayConfig wxPayConfig, WeChatBasePayData basePayData, CloseableHttpClient wxPayClient) {
        // 1.获取请求参数的Map格式
        Map<String, Object> paramsMap = getBasePayParams(wxPayConfig, basePayData);

        // 2.获取请求对象
        HttpPost httpPost = getHttpPost(wxPayConfig, WxApiType.NATIVE_PAY, paramsMap);

        // 3.完成签名并执行请求
        CloseableHttpResponse response = null;
        try {
            response = wxPayClient.execute(httpPost);
        } catch (IOException e) {
            e.printStackTrace();
            throw new CommonException("微信支付请求失败");
        }

        // 4.解析response对象
        HashMap<String, String> resultMap = resolverResponse(response);
        if (resultMap != null) {
            // native请求返回的是二维码链接,前端将链接转换成二维码即可
            return resultMap.get("code_url");
        }
        return null;
    }


    /**
     * 创建微信支付订单-jsapi方式
     *
     * @param wxPayConfig 微信配置信息
     * @param basePayData 基础请求信息,商品标题、商家订单id、订单价格
     * @param openId 通过微信小程序或者公众号获取到用户的openId
     * @param wxPayClient 微信请求客户端
     * @return 微信支付二维码地址
     */
    public static String wxJsApiPay(WxPayConfig wxPayConfig, WeChatBasePayData basePayData, String openId,CloseableHttpClient wxPayClient) {
        // 1.获取请求参数的Map格式
        Map<String, Object> paramsMap = getBasePayParams(wxPayConfig, basePayData);
        // 1.1 添加支付者信息
        Map<String,String> payerMap = new HashMap();
        payerMap.put("openid",openId);
        paramsMap.put("payer",payerMap);

        // 2.获取请求对象
        HttpPost httpPost = getHttpPost(wxPayConfig, WxApiType.JSAPI_PAY, paramsMap);

        // 3.完成签名并执行请求
        CloseableHttpResponse response = null;
        try {
            response = wxPayClient.execute(httpPost);
        } catch (IOException e) {
            e.printStackTrace();
            throw new CommonException("微信支付请求失败");
        }

        // 4.解析response对象
        HashMap<String, String> resultMap = resolverResponse(response);
        if (resultMap != null) {
            // native请求返回的是二维码链接,前端将链接转换成二维码即可
            return resultMap.get("prepay_id");
        }
        return null;
    }


    /**
     * 创建微信支付订单-APP方式
     *
     * @param wxPayConfig 微信配置信息
     * @param basePayData 基础请求信息,商品标题、商家订单id、订单价格
     * @param wxPayClient 微信请求客户端
     * @return 微信支付二维码地址
     */
    public static String wxAppPay(WxPayConfig wxPayConfig, WeChatBasePayData basePayData, CloseableHttpClient wxPayClient) {
        // 1.获取请求参数的Map格式
        Map<String, Object> paramsMap = getBasePayParams(wxPayConfig, basePayData);

        // 2.获取请求对象
        HttpPost httpPost = getHttpPost(wxPayConfig, WxApiType.APP_PAY, paramsMap);

        // 3.完成签名并执行请求
        CloseableHttpResponse response = null;
        try {
            response = wxPayClient.execute(httpPost);
        } catch (IOException e) {
            e.printStackTrace();
            throw new CommonException("微信支付请求失败");
        }

        // 4.解析response对象
        HashMap<String, String> resultMap = resolverResponse(response);
        if (resultMap != null) {
            // native请求返回的是二维码链接,前端将链接转换成二维码即可
            return resultMap.get("prepay_id");
        }
        return null;
    }

    /**
     * 解析响应数据
     * @param response 发送请求成功后,返回的数据
     * @return 微信返回的参数
     */
    private static HashMap<String, String> resolverResponse(CloseableHttpResponse response) {
        try {
            // 1.获取请求码
            int statusCode = response.getStatusLine().getStatusCode();
            // 2.获取返回值 String 格式
            final String bodyAsString = EntityUtils.toString(response.getEntity());

            Gson gson = new Gson();
            if (statusCode == 200) {
                // 3.如果请求成功则解析成Map对象返回
                HashMap<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
                return resultMap;
            } else {
                if (StringUtils.isNoneBlank(bodyAsString)) {
                    log.error("微信支付请求失败,提示信息:{}", bodyAsString);
                    // 4.请求码显示失败,则尝试获取提示信息
                    HashMap<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
                    throw new CommonException(resultMap.get("message"));
                }
                log.error("微信支付请求失败,未查询到原因,提示信息:{}", response);
                // 其他异常,微信也没有返回数据,这就需要具体排查了
                throw new IOException("request failed");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new CommonException(e.getMessage());
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 获取请求对象(Post请求)
     *
     * @param wxPayConfig 微信配置类
     * @param apiType     接口请求地址
     * @param paramsMap   请求参数
     * @return Post请求对象
     */
    private static HttpPost getHttpPost(WxPayConfig wxPayConfig, WxApiType apiType, Map<String, Object> paramsMap) {
        // 1.设置请求地址
        HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(apiType.getType()));

        // 2.设置请求数据
        Gson gson = new Gson();
        String jsonParams = gson.toJson(paramsMap);

        // 3.设置请求信息
        StringEntity entity = new StringEntity(jsonParams, "utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");
        return httpPost;
    }

    /**
     * 封装基础通用请求参数
     *
     * @param wxPayConfig 微信的配置文件
     * @param basePayData 微信支付基础请求数据
     * @return 封装后的map对象
     */
    private static Map<String, Object> getBasePayParams(WxPayConfig wxPayConfig, WeChatBasePayData basePayData) {
        Map<String, Object> paramsMap = new HashMap<>();
        paramsMap.put("appid", wxPayConfig.getAppid());
        paramsMap.put("mchid", wxPayConfig.getMchId());
        // 如果商品名称过长则截取
        String title = basePayData.getTitle().length() > 62 ? basePayData.getTitle().substring(0, 62) : basePayData.getTitle();
        paramsMap.put("description", title);
        paramsMap.put("out_trade_no", basePayData.getOrderId());
        paramsMap.put("notify_url", wxPayConfig.getNotifyDomain());
        Map<String, Integer> amountMap = new HashMap<>();
        amountMap.put("total", basePayData.getPrice().multiply(new BigDecimal("100")).intValue());
        paramsMap.put("amount", amountMap);
        return paramsMap;
    }


}

支付返回值实体类

package com.qz.client.modular.pay.result;

import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;

/**
 * @author:lihao
 * @create: 2023-01-09 13:12
 * @Description:
 */
@Setter
@Getter
public class WeChatCodeResult {

    @ApiModelProperty(value = "微信支付二维码链接")
    private String code_url;

    @ApiModelProperty(value = "错误:FAIL 成功:SUCCESS")
    private String result_code;

    @ApiModelProperty(value = "错误详情")
    private String err_code_des;

    public WeChatCodeResult() {
    }

    public WeChatCodeResult(String code_url, String result_code, String err_code_des) {
        this.code_url = code_url;
        this.result_code = result_code;
        this.err_code_des = err_code_des;
    }
}

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
在Spring Boot中集成微信支付,你可以按照以下步骤进行操作: 1. 首先,你需要在微信商户平台注册账号并开通支付功能。获取到微信支付的商户号(mchId)、API密钥(apiKey)和应用ID(appId)等关键信息。 2. 在你的Spring Boot项目中添加相关依赖。你可以在项目的pom.xml文件中添加以下依赖信息: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.github.wxpay</groupId> <artifactId>wxpay-sdk</artifactId> <version>3.0.10</version> </dependency> ``` 3. 创建一个配置类,配置微信支付相关参数。在该配置类中,你需要使用上面获取到的商户号、API密钥等信息进行配置: ```java @Configuration public class WxPayConfig { @Value("${wxpay.appId}") private String appId; @Value("${wxpay.mchId}") private String mchId; @Value("${wxpay.apiKey}") private String apiKey; // 创建WxPayService Bean,并配置相关参数 @Bean public WxPayService wxPayService() { WxPayConfig wxPayConfig = new WxPayConfig(); wxPayConfig.setAppId(appId); wxPayConfig.setMchId(mchId); wxPayConfig.setMchKey(apiKey); wxPayConfig.setNotifyUrl("你的异步通知地址"); return new WxPayServiceImpl(wxPayConfig); } } ``` 4. 创建一个Controller处理支付请求。在该Controller中,你可以使用WxPayService来进行支付相关操作,例如生成支付订单、发起支付等。 ```java @RestController @RequestMapping("/pay") public class WxPayController { @Autowired private WxPayService wxPayService; @PostMapping("/createOrder") public String createOrder() { // 生成支付订单 WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest(); // 设置订单参数 // ... WxPayUnifiedOrderResult result = wxPayService.unifiedOrder(request); // 处理支付结果,返回给前端 // ... return "success"; } } ``` 这只是一个简单的示例,你可以根据实际需求进行更详细的配置和处理。同时,你还需要根据自己的业务逻辑来处理支付结果的异步通知和订单查询等操作。 希望以上信息对你有所帮助!如果你还有其他问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

麦兜没有兜111

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值