refactor(payment): 重构微信支付模块

- 更新微信支付相关工具类和接口实现
- 新增微信支付配置项
- 优化微信支付流程和参数处理
- 引入微信支付官方SDK
This commit is contained in:
pikachu1995@126.com 2025-01-08 16:13:39 +08:00
parent db1a3566ae
commit e706431df5
8 changed files with 1194 additions and 1219 deletions

View File

@ -468,6 +468,11 @@
<scope>runtime</scope>
<classifier>osx-aarch_64</classifier>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.15</version>
</dependency>
</dependencies>

View File

@ -125,13 +125,21 @@ public final class CurrencyUtil {
return (int) price;
}
public static Long getFenLong(Double money) {
BigDecimal bigDecimalValue = BigDecimal.valueOf(money);
// 乘以 100 并四舍五入到最接近的整数
BigDecimal fenValue = bigDecimalValue.multiply(BigDecimal.valueOf(100)).setScale(0, RoundingMode.HALF_UP);
return fenValue.longValue();
}
/**
* 金额转分
*
* @param money 金额
* @return double类型分
*/
public static double reversalFen(Double money) {
public static double reversalFen(Integer money) {
return div(money, 100);
}
}

View File

@ -327,6 +327,24 @@ public class PayKit {
return RsaKit.encryptByPrivateKey(signMessage, privateKey);
}
/**
* v3 接口创建签名
*
* @param signMessage 待签名的参数
* @param key key.pem 证书
* @return 生成 v3 签名
* @throws Exception 异常信息
*/
public static String createPublicSign(String signMessage, String key) throws Exception {
if (StrUtil.isEmpty(signMessage)) {
return null;
}
//获取商户私钥
PrivateKey privateKey = PayKit.getPublicKey(key);
//生成签名
return RsaKit.encryptByPrivateKey(signMessage, privateKey);
}
/**
* v3 接口创建签名
*
@ -392,6 +410,23 @@ public class PayKit {
return RsaKit.loadPrivateKey(privateKey);
}
/**
* 获取商户公钥
*
* @param key 商户私钥证书
* @return {@link PrivateKey} 商户私钥
* @throws Exception 异常信息
*/
public static PrivateKey getPublicKey(String key) throws Exception {
// String originalKey = FileUtil.readUtf8String(keyPath);
String privateKey = key
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replaceAll("\\s+", "");
return RsaKit.loadPrivateKey(privateKey);
}
/**
* 获取证书
*

View File

@ -312,13 +312,18 @@ public class RsaKit {
*/
public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
try {
byte[] buffer = Base64.decode(privateKeyStr);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
return keyFactory.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException e) {
throw new Exception("无此算法");
} catch (InvalidKeySpecException e) {
e.printStackTrace();
throw new Exception("私钥非法");
} catch (NullPointerException e) {
throw new Exception("私钥数据为空");

View File

@ -450,6 +450,7 @@ public class WxPayKit {
* @param mchId 商户Id
* @param serialNo 商户 API 证书序列号
* @param key key.pem 证书
* @param publicKey 公钥证书
* @param body 接口请求参数
* @param nonceStr 随机字符库
* @param timestamp 时间戳
@ -458,11 +459,12 @@ public class WxPayKit {
* @throws Exception 异常信息
*/
public static String buildAuthorization(RequestMethodEnums method, String urlSuffix, String mchId,
String serialNo, String key, String body, String nonceStr,
String serialNo, String key, String publicKey ,String body, String nonceStr,
long timestamp, String authType) throws Exception {
//构建签名参数
String buildSignMessage = PayKit.buildSignMessage(method, urlSuffix, timestamp, nonceStr, body);
String signature = PayKit.createSign(buildSignMessage, key);
String publicKeySignature = PayKit.createPublicSign(buildSignMessage, publicKey);
String signature = PayKit.createSign(publicKeySignature, key);
//根据平台规则生成请求头 authorization
return PayKit.getAuthorization(mchId, serialNo, nonceStr, String.valueOf(timestamp), signature, authType);
}
@ -492,27 +494,27 @@ public class WxPayKit {
return PayKit.getAuthorization(mchId, serialNo, nonceStr, String.valueOf(timestamp), signature, authType);
}
/**
* 构建 v3 接口所需的 Authorization
*
* @param method {@link RequestMethodEnums} 请求方法
* @param urlSuffix 可通过 WxApiType 来获取URL挂载参数需要自行拼接
* @param mchId 商户Id
* @param serialNo 商户 API 证书序列号
* @param keyPath key.pem 证书路径
* @param body 接口请求参数
* @return {@link String} 返回 v3 所需的 Authorization
* @throws Exception 异常信息
*/
public static String buildAuthorization(RequestMethodEnums method, String urlSuffix, String mchId,
String serialNo, String keyPath, String body) throws Exception {
long timestamp = System.currentTimeMillis() / 1000;
String authType = "WECHATPAY2-SHA256-RSA2048";
String nonceStr = IdUtil.fastSimpleUUID();
return buildAuthorization(method, urlSuffix, mchId, serialNo, keyPath, body, nonceStr, timestamp, authType);
}
// /**
// * 构建 v3 接口所需的 Authorization
// *
// * @param method {@link RequestMethodEnums} 请求方法
// * @param urlSuffix 可通过 WxApiType 来获取URL挂载参数需要自行拼接
// * @param mchId 商户Id
// * @param serialNo 商户 API 证书序列号
// * @param keyPath key.pem 证书路径
// * @param body 接口请求参数
// * @return {@link String} 返回 v3 所需的 Authorization
// * @throws Exception 异常信息
// */
// public static String buildAuthorization(RequestMethodEnums method, String urlSuffix, String mchId,
// String serialNo, String keyPath, String body) throws Exception {
//
// long timestamp = System.currentTimeMillis() / 1000;
// String authType = "WECHATPAY2-SHA256-RSA2048";
// String nonceStr = IdUtil.fastSimpleUUID();
//
// return buildAuthorization(method, urlSuffix, mchId, serialNo, keyPath, body, nonceStr, timestamp, authType);
// }
/**
* 构建 v3 接口所需的 Authorization

View File

@ -1,16 +1,14 @@
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.URLEncoder;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.lili.cache.Cache;
import cn.lili.cache.CachePrefix;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.enums.ResultUtil;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.properties.ApiProperties;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.utils.CurrencyUtil;
import cn.lili.common.utils.SnowFlake;
@ -29,7 +27,9 @@ 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.kit.*;
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.WxPayKit;
import cn.lili.modules.payment.kit.core.utils.DateTimeZoneUtil;
import cn.lili.modules.payment.kit.dto.PayParam;
import cn.lili.modules.payment.kit.dto.PaymentSuccessParams;
@ -51,6 +51,30 @@ import cn.lili.modules.wallet.entity.dto.TransferResultDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.google.gson.Gson;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.RSAPublicKeyConfig;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
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.h5.H5Service;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.Amount;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferRequest;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferResponse;
import com.wechat.pay.java.service.transferbatch.model.TransferDetailInput;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@ -58,8 +82,6 @@ import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -141,31 +163,28 @@ public class WechatPlugin implements Payment {
if (appid == null) {
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
}
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
.setAppid(appid)
.setMchid(setting.getMchId())
.setDescription(cashierParam.getDetail())
.setOut_trade_no(outOrderNo)
.setTime_expire(timeExpire)
.setAttach(attach)
.setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(),PaymentMethodEnum.WECHAT))
.setAmount(new Amount().setTotal(fen)).setScene_info(sceneInfo);
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.H5_PAY.toString(),
setting.getMchId(),
setting.getSerialNumber(),
null,
setting.getApiclient_key(),
JSONUtil.toJsonStr(unifiedOrderModel)
);
Config config =this.getConfig(setting);
// 构建service
H5Service service = new H5Service.Builder().config(config).build();
com.wechat.pay.java.service.payments.h5.model.PrepayRequest prepayRequest = new com.wechat.pay.java.service.payments.h5.model.PrepayRequest();
com.wechat.pay.java.service.payments.h5.model.Amount amount = new com.wechat.pay.java.service.payments.h5.model.Amount();
amount.setTotal(fen);
prepayRequest.setAmount(amount);
prepayRequest.setAppid(appid);
prepayRequest.setMchid(setting.getMchId());
prepayRequest.setDescription(cashierParam.getDetail());
prepayRequest.setNotifyUrl(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
prepayRequest.setAttach(attach);
prepayRequest.setTimeExpire(timeExpire);
prepayRequest.setOutTradeNo(outOrderNo);
// 调用下单方法得到应答
com.wechat.pay.java.service.payments.h5.model.PrepayResponse response = service.prepay(prepayRequest);
updateOrderPayNo(payParam,outOrderNo);
return ResultUtil.data(JSONUtil.toJsonStr(response.getBody()));
return ResultUtil.data(response.getH5Url());
} catch (Exception e) {
log.error("微信H5支付错误", e);
throw new ServiceException(ResultCode.PAY_ERROR);
@ -183,8 +202,7 @@ public class WechatPlugin implements Payment {
return null;
}
Payer payer = new Payer();
payer.setOpenid(connect.getUnionId());
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
@ -202,46 +220,33 @@ public class WechatPlugin implements Payment {
if (appid == null) {
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
}
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
.setAppid(appid)
.setMchid(setting.getMchId())
.setDescription(cashierParam.getDetail())
.setOut_trade_no(outOrderNo)
.setTime_expire(timeExpire)
.setAttach(attach)
.setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT))
.setAmount(new Amount().setTotal(fen))
.setPayer(payer);
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.JS_API_PAY.toString(),
setting.getMchId(),
setting.getSerialNumber(),
null,
setting.getApiclient_key(),
JSONUtil.toJsonStr(unifiedOrderModel)
);
//根据证书序列号查询对应的证书来验证签名结果
boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
log.info("verifySignature: {}", verifySignature);
log.info("统一下单响应 {}", response);
Config config =this.getConfig(setting);
// 构建service
JsapiService service = new JsapiService.Builder().config(config).build();
if (verifySignature) {
String body = response.getBody();
JSONObject jsonObject = JSONUtil.parseObj(body);
String prepayId = jsonObject.getStr("prepay_id");
Map<String, String> map = WxPayKit.jsApiCreateSign(appid, prepayId, setting.getApiclient_key());
log.info("唤起支付参数:{}", map);
com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest prepayRequest = new com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest();
com.wechat.pay.java.service.payments.jsapi.model.Amount amount = new com.wechat.pay.java.service.payments.jsapi.model.Amount();
updateOrderPayNo(payParam,outOrderNo);
com.wechat.pay.java.service.payments.jsapi.model.Payer payer = new com.wechat.pay.java.service.payments.jsapi.model.Payer();
payer.setOpenid(connect.getUnionId());
amount.setTotal(fen);
prepayRequest.setAmount(amount);
prepayRequest.setAppid(appid);
prepayRequest.setMchid(setting.getMchId());
prepayRequest.setDescription(cashierParam.getDetail());
prepayRequest.setNotifyUrl(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
prepayRequest.setAttach(attach);
prepayRequest.setTimeExpire(timeExpire);
prepayRequest.setOutTradeNo(outOrderNo);
prepayRequest.setPayer(payer);
// 调用下单方法得到应答
com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse response = service.prepay(prepayRequest);
updateOrderPayNo(payParam,outOrderNo);
return ResultUtil.data(map);
}
log.error("微信支付参数验证错误,请及时处理");
throw new ServiceException(ResultCode.PAY_ERROR);
Map<String, String> map = WxPayKit.jsApiCreateSign(appid, response.getPrepayId(), setting.getApiclientKey());
log.info("唤起支付参数:{}", map);
return ResultUtil.data(map);
} catch (Exception e) {
log.error("支付异常", e);
throw new ServiceException(ResultCode.PAY_ERROR);
@ -269,48 +274,34 @@ public class WechatPlugin implements Payment {
if (appid == null) {
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
}
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
.setAppid(appid)
.setMchid(setting.getMchId())
.setDescription(cashierParam.getDetail())
.setOut_trade_no(outOrderNo)
.setTime_expire(timeExpire)
.setAttach(attach)
.setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT))
.setAmount(new Amount().setTotal(fen));
Config config =this.getConfig(setting);
// 构建service
AppService service = new AppService.Builder().config(config).build();
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.APP_PAY.toString(),
com.wechat.pay.java.service.payments.app.model.PrepayRequest prepayRequest = new com.wechat.pay.java.service.payments.app.model.PrepayRequest();
com.wechat.pay.java.service.payments.app.model.Amount amount = new com.wechat.pay.java.service.payments.app.model.Amount();
amount.setTotal(fen);
prepayRequest.setAmount(amount);
prepayRequest.setAppid(appid);
prepayRequest.setMchid(setting.getMchId());
prepayRequest.setDescription(cashierParam.getDetail());
prepayRequest.setNotifyUrl(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
prepayRequest.setAttach(attach);
prepayRequest.setTimeExpire(timeExpire);
prepayRequest.setOutTradeNo(outOrderNo);
// 调用下单方法得到应答
com.wechat.pay.java.service.payments.app.model.PrepayResponse response = service.prepay(prepayRequest);
updateOrderPayNo(payParam,outOrderNo);
Map<String, String> map = WxPayKit.appPrepayIdCreateSign(appid,
setting.getMchId(),
setting.getSerialNumber(),
null,
setting.getApiclient_key(),
JSONUtil.toJsonStr(unifiedOrderModel)
);
//根据证书序列号查询对应的证书来验证签名结果
boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
log.info("verifySignature: {}", verifySignature);
log.info("统一下单响应 {}", response);
if (verifySignature) {
JSONObject jsonObject = JSONUtil.parseObj(response.getBody());
String prepayId = jsonObject.getStr("prepay_id");
Map<String, String> map = WxPayKit.appPrepayIdCreateSign(appid,
setting.getMchId(),
prepayId,
setting.getApiclient_key(), SignType.MD5);
log.info("唤起支付参数:{}", map);
updateOrderPayNo(payParam,outOrderNo);
return ResultUtil.data(map);
}
log.error("微信支付参数验证错误,请及时处理");
throw new ServiceException(ResultCode.PAY_ERROR);
response.getPrepayId(),
setting.getApiclientKey(), SignType.MD5);
log.info("唤起支付参数:{}", map);
//修改付款单号
updateOrderPayNo(payParam,outOrderNo);
return ResultUtil.data(map);
} catch (Exception e) {
log.error("支付异常", e);
throw new ServiceException(ResultCode.PAY_ERROR);
@ -339,41 +330,28 @@ public class WechatPlugin implements Payment {
if (appid == null) {
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
}
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
.setAppid(appid)
.setMchid(setting.getMchId())
.setDescription(cashierParam.getDetail())
.setOut_trade_no(outOrderNo)
.setTime_expire(timeExpire)
//回传参数
.setAttach(attach)
.setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT))
.setAmount(new Amount().setTotal(fen));
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.NATIVE_PAY.toString(),
setting.getMchId(),
setting.getSerialNumber(),
null,
setting.getApiclient_key(),
JSONUtil.toJsonStr(unifiedOrderModel)
);
log.info("统一下单响应 {}", response);
//根据证书序列号查询对应的证书来验证签名结果
boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
log.info("verifySignature: {}", verifySignature);
Config config =this.getConfig(setting);
// 构建service
NativePayService service = new NativePayService.Builder().config(config).build();
if (verifySignature) {
updateOrderPayNo(payParam,outOrderNo);
PrepayRequest prepayRequest = new PrepayRequest();
Amount amount = new Amount();
amount.setTotal(fen);
prepayRequest.setAmount(amount);
prepayRequest.setAppid(appid);
prepayRequest.setMchid(setting.getMchId());
prepayRequest.setDescription(cashierParam.getDetail());
prepayRequest.setNotifyUrl(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
prepayRequest.setAttach(attach);
prepayRequest.setTimeExpire(timeExpire);
prepayRequest.setOutTradeNo(outOrderNo);
// 调用下单方法得到应答
PrepayResponse response = service.prepay(prepayRequest);
updateOrderPayNo(payParam,outOrderNo);
return ResultUtil.data(response.getCodeUrl());
return ResultUtil.data(new JSONObject(response.getBody()).getStr("code_url"));
} else {
log.error("微信支付参数验证错误,请及时处理");
throw new ServiceException(ResultCode.PAY_ERROR);
}
} catch (ServiceException e) {
log.error("支付异常", e);
throw new ServiceException(ResultCode.PAY_ERROR);
@ -381,6 +359,7 @@ public class WechatPlugin implements Payment {
log.error("支付异常", e);
throw new ServiceException(ResultCode.PAY_ERROR);
}
}
@Override
@ -394,7 +373,7 @@ public class WechatPlugin implements Payment {
return null;
}
Payer payer = new Payer();
com.wechat.pay.java.service.payments.jsapi.model.Payer payer = new com.wechat.pay.java.service.payments.jsapi.model.Payer();
payer.setOpenid(connect.getUnionId());
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
@ -415,44 +394,32 @@ public class WechatPlugin implements Payment {
String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
WechatPaymentSetting setting = wechatPaymentSetting();
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
.setAppid(appid)
.setMchid(setting.getMchId())
.setDescription(cashierParam.getDetail())
.setOut_trade_no(outOrderNo)
.setTime_expire(timeExpire)
.setAttach(attach)
.setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT))
.setAmount(new Amount().setTotal(fen))
.setPayer(payer);
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.JS_API_PAY.toString(),
setting.getMchId(),
setting.getSerialNumber(),
null,
setting.getApiclient_key(),
JSONUtil.toJsonStr(unifiedOrderModel)
);
//根据证书序列号查询对应的证书来验证签名结果
boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
log.info("verifySignature: {}", verifySignature);
log.info("统一下单响应 {}", response);
Config config =this.getConfig(setting);
// 构建service
JsapiService service = new JsapiService.Builder().config(config).build();
com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest prepayRequest = new com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest();
com.wechat.pay.java.service.payments.jsapi.model.Amount amount = new com.wechat.pay.java.service.payments.jsapi.model.Amount();
amount.setTotal(fen);
prepayRequest.setAmount(amount);
prepayRequest.setAppid(appid);
prepayRequest.setMchid(setting.getMchId());
prepayRequest.setDescription(cashierParam.getDetail());
prepayRequest.setNotifyUrl(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
prepayRequest.setAttach(attach);
prepayRequest.setTimeExpire(timeExpire);
prepayRequest.setOutTradeNo(outOrderNo);
prepayRequest.setPayer(payer);
// 调用下单方法得到应答
com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse response = service.prepay(prepayRequest);
updateOrderPayNo(payParam,outOrderNo);
Map<String, String> map = WxPayKit.jsApiCreateSign(appid, response.getPrepayId(), setting.getApiclientKey());
log.info("唤起支付参数:{}", map);
return ResultUtil.data(map);
if (verifySignature) {
String body = response.getBody();
JSONObject jsonObject = JSONUtil.parseObj(body);
String prepayId = jsonObject.getStr("prepay_id");
Map<String, String> map = WxPayKit.jsApiCreateSign(appid, prepayId, setting.getApiclient_key());
log.info("唤起支付参数:{}", map);
updateOrderPayNo(payParam,outOrderNo);
return ResultUtil.data(map);
}
log.error("微信支付参数验证错误,请及时处理");
throw new ServiceException(ResultCode.PAY_ERROR);
} catch (Exception e) {
log.error("支付异常", e);
throw new ServiceException(ResultCode.PAY_ERROR);
@ -514,48 +481,46 @@ public class WechatPlugin implements Payment {
}
}
//获取微信设置
WechatPaymentSetting wechatPaymentSetting = wechatPaymentSetting();
//获取用户openId
Connect connect = connectService.queryConnect(
ConnectQueryDTO.builder().userId(memberWithdrawApply.getMemberId())
.unionType(source).build()
);
//构建提现发起申请
TransferModel transferModel = new TransferModel()
.setAppid(withdrawalSetting.getWechatAppId())
.setOut_batch_no(SnowFlake.createStr("T"))
.setBatch_name("用户提现")
.setBatch_remark("用户提现")
.setTotal_amount(CurrencyUtil.fen(memberWithdrawApply.getApplyMoney()))
.setTotal_num(1)
.setTransfer_scene_id("1000");
//获取微信设置
WechatPaymentSetting setting = wechatPaymentSetting();
Config config =this.getConfig(setting);
// 构建service
TransferBatchService service = new TransferBatchService.Builder().config(config).build();
InitiateBatchTransferRequest request = new InitiateBatchTransferRequest();
request.setAppid(withdrawalSetting.getWechatAppId());
request.setOutBatchNo(SnowFlake.createStr("T"));
request.setBatchName("用户提现");
request.setBatchRemark("用户提现");
request.setTotalAmount(CurrencyUtil.getFenLong(memberWithdrawApply.getApplyMoney()));
request.setTotalNum(1);
request.setTransferSceneId("1000");
List<TransferDetailInput> transferDetailListList = new ArrayList<>();
{
TransferDetailInput transferDetailInput = new TransferDetailInput();
transferDetailInput.setOut_detail_no(SnowFlake.createStr("TD"));
transferDetailInput.setTransfer_amount(CurrencyUtil.fen(memberWithdrawApply.getApplyMoney()));
transferDetailInput.setTransfer_remark("用户提现");
transferDetailInput.setOutDetailNo(SnowFlake.createStr("TD"));
transferDetailInput.setTransferAmount(CurrencyUtil.getFenLong(memberWithdrawApply.getApplyMoney()));
transferDetailInput.setTransferRemark("用户提现");
transferDetailInput.setOpenid(connect.getUnionId());
transferDetailListList.add(transferDetailInput);
}
transferModel.setTransfer_detail_list(transferDetailListList);
request.setTransferDetailList(transferDetailListList);
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.TRANSFER_BATCHES.toString(),
wechatPaymentSetting.getMchId(),
wechatPaymentSetting.getSerialNumber(),
null,
wechatPaymentSetting.getApiclient_key(),
JSONUtil.toJsonStr(transferModel)
);
// 调用下单方法得到应答
InitiateBatchTransferResponse response = service.initiateBatchTransfer(request);
log.info("微信提现响应 {}", response);
String body = response.getBody();
JSONObject jsonObject = JSONUtil.parseObj(body);
return TransferResultDTO.builder().result(jsonObject.getStr("batch_id") != null).response(body).build();
return TransferResultDTO.builder().result(response.getBatchId()!= null).build();
//根据自身业务进行接下来的任务处理
} catch (Exception e) {
e.printStackTrace();
@ -572,41 +537,51 @@ public class WechatPlugin implements Payment {
*/
private void verifyNotify(HttpServletRequest request) throws Exception {
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String serialNo = request.getHeader("Wechatpay-Serial");
String signature = request.getHeader("Wechatpay-Signature");
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
String result = HttpKit.readData(request);
log.info("微信支付通知密文 {}", result);
// 构造 RequestParam
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(request.getHeader("Wechatpay-Serial"))
.nonce(request.getHeader("Wechatpay-Nonce"))
.signature(request.getHeader("Wechatpay-Signature"))
.timestamp(request.getHeader("Wechatpay-Timestamp"))
.body(HttpKit.readData(request))
.build();
WechatPaymentSetting setting = wechatPaymentSetting();
//校验服务器端响应¬
String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
setting.getApiKey3(), Objects.requireNonNull(getPlatformCert()));
log.info("微信支付通知明文 {}", plainText);
JSONObject jsonObject = JSONUtil.parseObj(plainText);
String payParamStr = jsonObject.getStr("attach");
String payParamJson = URLDecoder.decode(payParamStr, StandardCharsets.UTF_8);
PayParam payParam = JSONUtil.toBean(payParamJson, PayParam.class);
NotificationConfig config = new RSAAutoCertificateConfig.Builder()
.merchantId(setting.getMchId())
.privateKey(setting.getApiclientKey())
.merchantSerialNumber(setting.getSerialNumber())
.apiV3Key(setting.getApiKey3())
.build();
String tradeNo = jsonObject.getStr("transaction_id");
Double totalAmount = CurrencyUtil.reversalFen(jsonObject.getJSONObject("amount").getDouble("total"));
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser(config);
PaymentSuccessParams paymentSuccessParams = new PaymentSuccessParams(
PaymentMethodEnum.WECHAT.name(),
tradeNo,
totalAmount,
payParam
);
try {
// 以支付通知回调为例验签解密并转换成 Transaction
Transaction transaction = parser.parse(requestParam, Transaction.class);
String payParamJson = URLDecoder.decode(transaction.getAttach(), StandardCharsets.UTF_8);
PayParam payParam = JSONUtil.toBean(payParamJson, PayParam.class);
Double totalAmount = CurrencyUtil.reversalFen(transaction.getAmount().getTotal());
PaymentSuccessParams paymentSuccessParams = new PaymentSuccessParams(
PaymentMethodEnum.WECHAT.name(),
transaction.getTransactionId(),
totalAmount,
payParam
);
paymentService.success(paymentSuccessParams);
} catch (ValidationException e) {
// 签名验证失败返回 401 UNAUTHORIZED 状态码
log.error("sign verification failed", e);
}
paymentService.success(paymentSuccessParams);
log.info("微信支付回调:支付成功{}", plainText);
}
@Override
@ -614,39 +589,28 @@ public class WechatPlugin implements Payment {
try {
Amount amount = new Amount().setRefund(CurrencyUtil.fen(refundLog.getTotalAmount()))
.setTotal(CurrencyUtil.fen(orderService.getPaymentTotal(refundLog.getOrderSn())));
//退款参数准备
RefundModel refundModel = new RefundModel()
.setTransaction_id(refundLog.getPaymentReceivableNo())
.setOut_refund_no(refundLog.getOutOrderNo())
.setReason(refundLog.getRefundReason())
.setAmount(amount)
.setNotify_url(refundNotifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
AmountReq amount = new AmountReq();
amount.setRefund(CurrencyUtil.getFenLong(refundLog.getTotalAmount()));
amount.setTotal(CurrencyUtil.getFenLong(orderService.getPaymentTotal(refundLog.getOrderSn())));
amount.setCurrency("CNY");
//获取微信设置
WechatPaymentSetting setting = wechatPaymentSetting();
log.info("微信退款参数 {}", JSONUtil.toJsonStr(refundModel));
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.DOMESTIC_REFUNDS.toString(),
setting.getMchId(),
setting.getSerialNumber(),
null,
setting.getApiclient_key(),
JSONUtil.toJsonStr(refundModel)
);
log.info("微信退款响应 {}", response);
//退款申请成功
if (response.getStatus() == 200) {
refundLogService.save(refundLog);
} else {
//退款申请失败
refundLog.setErrorMessage(response.getBody());
refundLogService.save(refundLog);
}
Config config =this.getConfig(setting);
// 构建service
RefundService refundService = new RefundService.Builder().config(config).build();
CreateRequest request = new CreateRequest();
request.setTransactionId(refundLog.getPaymentReceivableNo());
request.setAmount(amount);
request.setOutRefundNo(refundLog.getOutOrderNo());
request.setReason(refundLog.getRefundReason());
request.setNotifyUrl(refundNotifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
Refund refund=refundService.create(request);
log.info("微信退款响应 {}", refund);
refundLogService.save(refundLog);
} catch (Exception e) {
log.error("微信退款申请失败", e);
}
@ -664,10 +628,10 @@ public class WechatPlugin implements Payment {
String result = HttpKit.readData(request);
log.info("微信退款通知密文 {}", result);
JSONObject ciphertext = JSONUtil.parseObj(result);
WechatPaymentSetting setting = wechatPaymentSetting();
try { //校验服务器端响应¬
String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
wechatPaymentSetting().getApiKey3(), Objects.requireNonNull(getPlatformCert()));
wechatPaymentSetting().getApiKey3(), Objects.requireNonNull(setting.getPublicKey()));
log.info("微信退款通知明文 {}", plainText);
if (("REFUND.SUCCESS").equals(ciphertext.getStr("event_type"))) {
@ -721,78 +685,22 @@ public class WechatPlugin implements Payment {
}
/**
* 获取平台公钥
*
* @return 平台公钥
* 获取微信支付配置
* @param setting
* @return
*/
private X509Certificate getPlatformCert() {
//获取缓存中的平台公钥如果有则直接返回否则去微信请求
String publicCert = cache.getString(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix());
if (!StringUtils.isEmpty(publicCert)) {
return PayKit.getCertificate(publicCert);
}
//获取平台证书列表
try {
WechatPaymentSetting setting = wechatPaymentSetting();
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.GET,
WechatDomain.CHINA.toString(),
WechatApiEnum.GET_CERTIFICATES.toString(),
setting.getMchId(),
setting.getSerialNumber(),
null,
setting.getApiclient_key(),
""
);
String body = response.getBody();
log.info("获取微信平台证书body: {}", body);
if (response.getStatus() == 200) {
JSONObject jsonObject = JSONUtil.parseObj(body);
JSONArray dataArray = jsonObject.getJSONArray("data");
//默认认为只有一个平台证书
JSONObject encryptObject = dataArray.getJSONObject(0);
JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
String associatedData = encryptCertificate.getStr("associated_data");
String cipherText = encryptCertificate.getStr("ciphertext");
String nonce = encryptCertificate.getStr("nonce");
publicCert = getPlatformCertStr(associatedData, nonce, cipherText);
long second = (PayKit.getCertificate(publicCert).getNotAfter().getTime() - System.currentTimeMillis()) / 1000;
cache.put(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix(), publicCert, second);
} else {
log.error("证书获取失败:{}" + body);
throw new ServiceException(ResultCode.WECHAT_CERT_ERROR);
}
return PayKit.getCertificate(publicCert);
} catch (Exception e) {
log.error("证书获取失败", e);
}
return null;
private RSAPublicKeyConfig getConfig(WechatPaymentSetting setting){
return
new RSAPublicKeyConfig.Builder()
.merchantId(setting.getMchId())
.privateKey(setting.getApiclientKey())
.publicKey(setting.getPublicKey())
.publicKeyId(setting.getPublicId())
.merchantSerialNumber(setting.getSerialNumber())
.apiV3Key(setting.getApiKey3())
.build();
}
/**
* 获取平台证书缓存的字符串
* 下列各个密钥参数
*
* @param associatedData 密钥参数
* @param nonce 密钥参数
* @param cipherText 密钥参数
* @return platform key
* @throws GeneralSecurityException 密钥获取异常
*/
private String getPlatformCertStr(String associatedData, String nonce, String cipherText) throws GeneralSecurityException {
AesUtil aesUtil = new AesUtil(wechatPaymentSetting().getApiKey3().getBytes(StandardCharsets.UTF_8));
//平台证书密文解密
//encrypt_certificate 中的 associated_data nonce ciphertext
return aesUtil.decryptToString(
associatedData.getBytes(StandardCharsets.UTF_8),
nonce.getBytes(StandardCharsets.UTF_8),
cipherText
);
}
/**
* 修改订单支付单号

View File

@ -37,10 +37,22 @@ public class WechatPaymentSetting {
* 商户号
*/
private String mchId;
/**
* 商户证书序列号
*/
private String serialNumber;
/**
* 私钥
*/
private String apiclient_key;
private String apiclientKey;
/**
* 公钥ID
*/
private String publicId;
/**
* 公钥
*/
private String publicKey;
// /**
// * pem 证书
// */
@ -49,10 +61,7 @@ public class WechatPaymentSetting {
// * p12 证书
// */
// private String apiclient_cert_p12;
/**
* 商户证书序列号
*/
private String serialNumber;
/**
* apiv3私钥
*/