微信支付开通支付方法在这里可以参考一下:申请开通微信支付教程_个人怎么申请微信支付_郑鹏川的博客-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:  # appidmch-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;// APPIDprivate 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("私钥文件不存在");}}/*** 获取签名验证器*/@Beanpublic 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请求对象*/@Beanpublic 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()); // 我们的APPIDparamsMap.put("mchid",wxPayConfig.getMchId()); // 我们的商户IDparamsMap.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;}}

springboot集成微信支付V3 SDK相关推荐

  1. SpringBoot集成微信支付V3

    河南循中网络科技有限公司 - 精心创作,详细分解,按照步骤,均可成功! 文章目录 吐槽! 此文章包含实现了哪些接口? 学习资料 集成微信支付V3 什么是"商户证书"?什么是&quo ...

  2. SpringBoot集成微信支付微信退款

    微信支付 备注:本次支付接入的是微信支付v2.0版本,其中所有的请求格式均为XML. 如有需求可直接参阅官方文档 https://pay.weixin.qq.com/wiki/doc/api/inde ...

  3. java异步调用微信接口_微信支付V3 SDK(Java版,支持同步异步调用)

    我们在开发微信支付时,发现微信官方已经对SDK做了升级,V3版本的SDK从设计上符合RESTful规范. 我们再在开源库中寻找是否有现成de开箱即用.并且支持响应式编程的SDK版本.经过一凡寻找,令我 ...

  4. springboot集成微信支付

    一.先去微信申请相应的appid等,然后在yml文件增加相应配置 pay:   wxpay:     appID: ******     mchID: *****     key: *****     ...

  5. springboot利用官方SDK(wechatpay-apache-httpclient)接入微信支付V3

    利用微信官方提供的SDK wechatpay-apache-httpclient 实现.以微信小程序支付为例,其他支付也是一样的,就是参数和接口地址不同. 微信支付V3文档 首先要在微信商户平台设置好 ...

  6. 小程序微信支付V3版本Java集成

    一.简介 1.关于API v3 相较于之前的微信支付API,主要区别是: 遵循统一的REST的设计风格 使用JSON作为数据交互的格式,不再使用XML 使用基于非对称密钥的SHA256-RSA的数字签 ...

  7. APICloud模拟微信支付调用(非集成微信支付SDK)

    最近因为公司业务发展,需要研究APICloud的使用,主要是针对iOS模块化开发这块.我在APICloud官网搜索半天也没得到解决方案,而下载的APICloud Demo过于简单,故经过几天的钻研,找 ...

  8. PHP微信支付V3利用官方SDK从申请到代码完成保姆级教程

    申请流程及配置 申请以及配置流程参考官方:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_6_1.shtml 根据官方的指导,我们 ...

  9. springboot集成支付宝支付2.0

    springboot集成微信APP支付V3最新版 流程详解 创建应用 添加支付能力 接口加签 获取证书 集成支付到springboot项目 流程详解 相对于集成微信支付来说,支付宝相对简单些,支付宝对 ...

最新文章

  1. 聊聊Oracle 11g的Snapshot Standby Database(上)
  2. java 动态读取文件_Java窗体动态加载磁盘文件的实现方法
  3. python怎么读xlsx_用python读取xlsx文件
  4. Mysql 字符串处理函数
  5. 并发编程:原子性问题,可见性问题,有序性问题。
  6. Kubernetes 稳定性保障手册 -- 日志专题
  7. linux修改upd53端口,Centos iptables打开端口53
  8. python列表生成式原理_三元表达式/和/或如何在Python中工作/真与假的性质/列表生成/生成器,and,or,执行,原理,True,False,本质,生成式...
  9. C#以文件夹共享方式实现2G以上大文件传输
  10. react环境搭建(-)
  11. 员工入职是一连串事件(转)
  12. Elasticsearch(ES)创建索引
  13. 推荐书籍:网络流量整形与带宽控制技术
  14. 计算机efs加密,2分钟让你学会电脑EFS文件加密
  15. workbench中施加预紧力进行模态分析
  16. 2019苹果全球开发者大会:起售价5999美元,史上最强大Mac电脑发布
  17. 3dsmaxC4DbodypainterPS画贴图一、展开模型UV。
  18. 力扣杯2023春-个人赛、战队赛
  19. 简单的python小程序祝福母亲,母亲节快乐!
  20. 4核服务器型号,服务器厂商、型号、参数。

热门文章

  1. idel打开Run Dashboard
  2. Java基本信息采集程序
  3. 图像处理——边缘提取
  4. 有哪些蓝牙耳机的音质比较好?2022好音质蓝牙耳机推荐
  5. arcgis Server 站点创建报错 Server machine is not a local server machine
  6. echarts初始化显示到市级地图
  7. 解压压缩的文件没有管理员权限
  8. 习题1.3从五色球中取三色球的取法
  9. 通过PowerShell启用AADC的密码同步功能
  10. [C++学习日记]-08-函数