feat(payment): 微信支付支持公钥和证书两种验证方式- 在 WechatPaymentSetting 中添加 publicType 字段,用于选择验证方式

- 修改 WechatPlugin 中的支付、退款等方法,支持两种验证方式
- 新增 getPublicKeyConfig 和 getCertificateConfig 方法,分别用于获取公钥和证书配置
- 优化退款通知处理逻辑,使用 NotificationParser 进行验证和解析
This commit is contained in:
pikachu1995@126.com 2025-02-11 15:42:35 +08:00
parent 633b94c375
commit 0cf464e549
2 changed files with 119 additions and 66 deletions

View File

@ -1,9 +1,7 @@
package cn.lili.modules.payment.kit.plugin.wechat; package cn.lili.modules.payment.kit.plugin.wechat;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.net.URLDecoder; import cn.hutool.core.net.URLDecoder;
import cn.hutool.core.net.URLEncoder; import cn.hutool.core.net.URLEncoder;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import cn.lili.cache.Cache; import cn.lili.cache.Cache;
import cn.lili.common.enums.ResultCode; import cn.lili.common.enums.ResultCode;
@ -24,8 +22,6 @@ import cn.lili.modules.payment.entity.RefundLog;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum; import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
import cn.lili.modules.payment.kit.CashierSupport; import cn.lili.modules.payment.kit.CashierSupport;
import cn.lili.modules.payment.kit.Payment; import cn.lili.modules.payment.kit.Payment;
import cn.lili.modules.payment.kit.core.PaymentHttpResponse;
import cn.lili.modules.payment.kit.core.enums.RequestMethodEnums;
import cn.lili.modules.payment.kit.core.enums.SignType; import cn.lili.modules.payment.kit.core.enums.SignType;
import cn.lili.modules.payment.kit.core.kit.HttpKit; import cn.lili.modules.payment.kit.core.kit.HttpKit;
import cn.lili.modules.payment.kit.core.kit.IpKit; import cn.lili.modules.payment.kit.core.kit.IpKit;
@ -34,9 +30,8 @@ import cn.lili.modules.payment.kit.core.utils.DateTimeZoneUtil;
import cn.lili.modules.payment.kit.dto.PayParam; import cn.lili.modules.payment.kit.dto.PayParam;
import cn.lili.modules.payment.kit.dto.PaymentSuccessParams; import cn.lili.modules.payment.kit.dto.PaymentSuccessParams;
import cn.lili.modules.payment.kit.params.dto.CashierParam; import cn.lili.modules.payment.kit.params.dto.CashierParam;
import cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApiEnum; import cn.lili.modules.payment.kit.plugin.wechat.model.H5Info;
import cn.lili.modules.payment.kit.plugin.wechat.enums.WechatDomain; import cn.lili.modules.payment.kit.plugin.wechat.model.SceneInfo;
import cn.lili.modules.payment.kit.plugin.wechat.model.*;
import cn.lili.modules.payment.service.PaymentService; import cn.lili.modules.payment.service.PaymentService;
import cn.lili.modules.payment.service.RefundLogService; import cn.lili.modules.payment.service.RefundLogService;
import cn.lili.modules.system.entity.dos.Setting; import cn.lili.modules.system.entity.dos.Setting;
@ -58,7 +53,6 @@ import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationConfig; import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser; import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam; import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.core.util.StringUtil;
import com.wechat.pay.java.service.payments.app.AppService; import com.wechat.pay.java.service.payments.app.AppService;
import com.wechat.pay.java.service.payments.h5.H5Service; import com.wechat.pay.java.service.payments.h5.H5Service;
import com.wechat.pay.java.service.payments.jsapi.JsapiService; import com.wechat.pay.java.service.payments.jsapi.JsapiService;
@ -85,7 +79,6 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
/** /**
* 微信支付 * 微信支付
@ -164,7 +157,13 @@ public class WechatPlugin implements Payment {
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING); throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
} }
Config config =this.getConfig(setting); Config config =null;
if(setting.getPublicType().equals("CERT")){
config=this.getCertificateConfig(setting);
}else {
config=this.getPublicKeyConfig(setting);
}
// 构建service // 构建service
H5Service service = new H5Service.Builder().config(config).build(); H5Service service = new H5Service.Builder().config(config).build();
@ -221,7 +220,12 @@ public class WechatPlugin implements Payment {
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING); throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
} }
Config config =this.getConfig(setting); Config config =null;
if(setting.getPublicType().equals("CERT")){
config=this.getCertificateConfig(setting);
}else {
config=this.getPublicKeyConfig(setting);
}
// 构建service // 构建service
JsapiService service = new JsapiService.Builder().config(config).build(); JsapiService service = new JsapiService.Builder().config(config).build();
@ -275,7 +279,12 @@ public class WechatPlugin implements Payment {
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING); throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
} }
Config config =this.getConfig(setting); Config config =null;
if(setting.getPublicType().equals("CERT")){
config=this.getCertificateConfig(setting);
}else {
config=this.getPublicKeyConfig(setting);
}
// 构建service // 构建service
AppService service = new AppService.Builder().config(config).build(); AppService service = new AppService.Builder().config(config).build();
@ -331,7 +340,12 @@ public class WechatPlugin implements Payment {
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING); throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
} }
Config config =this.getConfig(setting); Config config =null;
if(setting.getPublicType().equals("CERT")){
config=this.getCertificateConfig(setting);
}else {
config=this.getPublicKeyConfig(setting);
}
// 构建service // 构建service
NativePayService service = new NativePayService.Builder().config(config).build(); NativePayService service = new NativePayService.Builder().config(config).build();
@ -395,7 +409,12 @@ public class WechatPlugin implements Payment {
WechatPaymentSetting setting = wechatPaymentSetting(); WechatPaymentSetting setting = wechatPaymentSetting();
Config config =this.getConfig(setting); Config config =null;
if(setting.getPublicType().equals("CERT")){
config=this.getCertificateConfig(setting);
}else {
config=this.getPublicKeyConfig(setting);
}
// 构建service // 构建service
JsapiService service = new JsapiService.Builder().config(config).build(); JsapiService service = new JsapiService.Builder().config(config).build();
@ -491,7 +510,12 @@ public class WechatPlugin implements Payment {
//获取微信设置 //获取微信设置
WechatPaymentSetting setting = wechatPaymentSetting(); WechatPaymentSetting setting = wechatPaymentSetting();
Config config =this.getConfig(setting); Config config =null;
if(setting.getPublicType().equals("CERT")){
config=this.getCertificateConfig(setting);
}else {
config=this.getPublicKeyConfig(setting);
}
// 构建service // 构建service
TransferBatchService service = new TransferBatchService.Builder().config(config).build(); TransferBatchService service = new TransferBatchService.Builder().config(config).build();
@ -547,14 +571,24 @@ public class WechatPlugin implements Payment {
.build(); .build();
WechatPaymentSetting setting = wechatPaymentSetting(); WechatPaymentSetting setting = wechatPaymentSetting();
NotificationConfig config=null;
NotificationConfig config = new RSAAutoCertificateConfig.Builder() if(setting.getPublicType().equals("CERT")) {
.merchantId(setting.getMchId()) config = new RSAAutoCertificateConfig.Builder()
.privateKey(setting.getApiclientKey()) .merchantId(setting.getMchId())
.merchantSerialNumber(setting.getSerialNumber()) .privateKey(setting.getApiclientKey())
.apiV3Key(setting.getApiKey3()) .merchantSerialNumber(setting.getSerialNumber())
.build(); .apiV3Key(setting.getApiKey3())
.build();
}else{
config = new RSAPublicKeyConfig.Builder()
.merchantId(setting.getMchId())
.apiV3Key(setting.getApiKey3())
.privateKey(setting.getApiclientKey())
.merchantSerialNumber(setting.getSerialNumber())
.publicKeyId(setting.getPublicId())
.publicKey(setting.getPublicKey())
.build();
}
// 初始化 NotificationParser // 初始化 NotificationParser
NotificationParser parser = new NotificationParser(config); NotificationParser parser = new NotificationParser(config);
@ -596,7 +630,12 @@ public class WechatPlugin implements Payment {
//获取微信设置 //获取微信设置
WechatPaymentSetting setting = wechatPaymentSetting(); WechatPaymentSetting setting = wechatPaymentSetting();
Config config =this.getConfig(setting); Config config =null;
if(setting.getPublicType().equals("CERT")){
config=this.getCertificateConfig(setting);
}else {
config=this.getPublicKeyConfig(setting);
}
// 构建service // 构建service
RefundService refundService = new RefundService.Builder().config(config).build(); RefundService refundService = new RefundService.Builder().config(config).build();
@ -619,49 +658,45 @@ public class WechatPlugin implements Payment {
@Override @Override
public void refundNotify(HttpServletRequest request) { public void refundNotify(HttpServletRequest request) {
String timestamp = request.getHeader("Wechatpay-Timestamp"); // 构造 RequestParam
String nonce = request.getHeader("Wechatpay-Nonce"); RequestParam requestParam = new RequestParam.Builder()
String serialNo = request.getHeader("Wechatpay-Serial"); .serialNumber(request.getHeader("Wechatpay-Serial"))
String signature = request.getHeader("Wechatpay-Signature"); .nonce(request.getHeader("Wechatpay-Nonce"))
.signature(request.getHeader("Wechatpay-Signature"))
.timestamp(request.getHeader("Wechatpay-Timestamp"))
.body(HttpKit.readData(request))
.build();
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
String result = HttpKit.readData(request);
log.info("微信退款通知密文 {}", result);
JSONObject ciphertext = JSONUtil.parseObj(result);
WechatPaymentSetting setting = wechatPaymentSetting(); WechatPaymentSetting setting = wechatPaymentSetting();
try { //校验服务器端响应¬ NotificationConfig config=null;
String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp, if(setting.getPublicType().equals("CERT")) {
wechatPaymentSetting().getApiKey3(), Objects.requireNonNull(setting.getPublicKey())); config = new RSAAutoCertificateConfig.Builder()
log.info("微信退款通知明文 {}", plainText); .merchantId(setting.getMchId())
.privateKey(setting.getApiclientKey())
.merchantSerialNumber(setting.getSerialNumber())
.apiV3Key(setting.getApiKey3())
.build();
}else{
config = new RSAPublicKeyConfig.Builder()
.merchantId(setting.getMchId())
.apiV3Key(setting.getApiKey3())
.privateKey(setting.getApiclientKey())
.merchantSerialNumber(setting.getSerialNumber())
.publicKeyId(setting.getPublicId())
.publicKey(setting.getPublicKey())
.build();
}
if (("REFUND.SUCCESS").equals(ciphertext.getStr("event_type"))) { // 初始化 NotificationParser
log.info("退款成功 {}", plainText); NotificationParser parser = new NotificationParser(config);
//校验服务器端响应 try {
JSONObject jsonObject = JSONUtil.parseObj(plainText); Refund refund = parser.parse(requestParam, Refund.class);
String transactionId = jsonObject.getStr("transaction_id"); RefundLog refundLog = refundLogService.getOne(new LambdaQueryWrapper<RefundLog>().eq(RefundLog::getPaymentReceivableNo,
String refundId = jsonObject.getStr("refund_id"); refund.getTransactionId()));
if (refundLog != null) {
RefundLog refundLog = refundLogService.getOne(new LambdaQueryWrapper<RefundLog>().eq(RefundLog::getPaymentReceivableNo, refundLog.setIsRefund(true);
transactionId)); refundLog.setReceivableNo(refund.getRefundId());
if (refundLog != null) { refundLogService.saveOrUpdate(refundLog);
refundLog.setIsRefund(true);
refundLog.setReceivableNo(refundId);
refundLogService.saveOrUpdate(refundLog);
}
} else {
log.info("退款失败 {}", plainText);
JSONObject jsonObject = JSONUtil.parseObj(plainText);
String transactionId = jsonObject.getStr("transaction_id");
String refundId = jsonObject.getStr("refund_id");
RefundLog refundLog = refundLogService.getOne(new LambdaQueryWrapper<RefundLog>().eq(RefundLog::getPaymentReceivableNo,
transactionId));
if (refundLog != null) {
refundLog.setReceivableNo(refundId);
refundLog.setErrorMessage(ciphertext.getStr("summary"));
refundLogService.saveOrUpdate(refundLog);
}
} }
} catch (Exception e) { } catch (Exception e) {
log.error("微信退款失败", e); log.error("微信退款失败", e);
@ -685,11 +720,11 @@ public class WechatPlugin implements Payment {
} }
/** /**
* 获取微信支付配置 * 获取微信公钥配置
* @param setting * @param setting
* @return * @return
*/ */
private RSAPublicKeyConfig getConfig(WechatPaymentSetting setting){ private RSAPublicKeyConfig getPublicKeyConfig(WechatPaymentSetting setting){
return return
new RSAPublicKeyConfig.Builder() new RSAPublicKeyConfig.Builder()
.merchantId(setting.getMchId()) .merchantId(setting.getMchId())
@ -701,6 +736,19 @@ public class WechatPlugin implements Payment {
.build(); .build();
} }
/**
* 获取微信证书配置
* @param setting
* @return
*/
private RSAAutoCertificateConfig getCertificateConfig(WechatPaymentSetting setting) {
return new RSAAutoCertificateConfig.Builder()
.merchantId(setting.getMchId())
.privateKey(setting.getApiclientKey())
.merchantSerialNumber(setting.getSerialNumber())
.apiV3Key(setting.getApiKey3())
.build();
}
/** /**
* 修改订单支付单号 * 修改订单支付单号

View File

@ -53,6 +53,11 @@ public class WechatPaymentSetting {
* 公钥 * 公钥
*/ */
private String publicKey; private String publicKey;
/**
* 微信验证方式公钥/证书(KEY/CERT)
*/
private String publicType;
// /** // /**
// * pem 证书 // * pem 证书
// */ // */