新增微信支付以及支付宝工具类实现

This commit is contained in:
xiongf
2025-01-10 16:51:42 +08:00
parent 3b0d9f5fc5
commit 5ac8ab7e0c
8 changed files with 585 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
package cn.xf.basedemo.common.model.pay;
import lombok.Data;
@Data
public class AlipayTransRsqVo {
private boolean success = false;
private String code;
private String subCode;
private String msg;
private String orderId;
private String status;
private String payFundOrderID;
private String outBizNo;
private String transDate;
private String settleSerialNo;
private String amount;
}

View File

@@ -0,0 +1,14 @@
package cn.xf.basedemo.common.model.pay;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.Data;
@Data
public class RefundInfoRsqDTO {
private boolean isSuccess;
private String msg;
private Refund refund;
}

View File

@@ -0,0 +1,12 @@
package cn.xf.basedemo.common.model.pay;
import lombok.Data;
@Data
public class TransferQueryVO {
private boolean isSuccess;
private String resCode;
private String resCodeDes;
private WechatCashQueryVo cashQueryVo;
}

View File

@@ -0,0 +1,24 @@
package cn.xf.basedemo.common.model.pay;
import lombok.Data;
import java.time.OffsetDateTime;
@Data
public class WechatCashQueryVo {
private String appid;
private String batch_id;
private String detail_id;
private String detail_status;
private OffsetDateTime initiate_time;
private String mchid;
private String openid;
private String out_batch_no;
private String out_detail_no;
private long transfer_amount;
private String transfer_remark;
private OffsetDateTime update_time;
private String user_name;
}

View File

@@ -0,0 +1,11 @@
package cn.xf.basedemo.common.model.pay;
import lombok.Data;
@Data
public class WxPayTransRsqVo {
private boolean success = false;
private String code;
private String msg;
}

View File

@@ -0,0 +1,210 @@
package cn.xf.basedemo.common.utils.pay;
import cn.xf.basedemo.common.model.pay.AlipayTransRsqVo;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayFundTransUniTransferModel;
import com.alipay.api.domain.Participant;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
@Slf4j
public class AliPayUtil {
//支付路径
private static String payPath;
private static PayInfo payInfo;
private static String gatewayUrl = "https://openapi.alipay.com/gateway.do";//"https://openapi.alipaydev.com/gateway.do";
private static String format = "json";
private static String charset = "utf-8";
private static String signType = "RSA2";
@Data
static class PayInfo {
String appId;
String appCertPath;
String alipayCertPath;
String rootCertPath;
String rsaPrivateKey;
}
public AliPayUtil(String path) {
payPath = path;
String path1 = "d:/pay/";
path1 = "";
if (path.equals("a")) {
payInfo = new PayInfo();
payInfo.setAppId("");
payInfo.setAppCertPath(path1 + "15080.crt");
payInfo.setAlipayCertPath(path1 + "alipayCertPublicKey_RSA2.crt");
payInfo.setRootCertPath(path1 + "alipayRootCert.crt");
payInfo.setRsaPrivateKey(readFileContent(path1 + "private.key"));
}
}
@SneakyThrows
public AlipayTransRsqVo transfer(String trans_no, String account, BigDecimal money, int type, String trueName, String mark) {
AlipayTransRsqVo resVo = new AlipayTransRsqVo();
// 创建API客户端实例
CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
certAlipayRequest.setServerUrl(gatewayUrl);
certAlipayRequest.setAppId(payInfo.appId);
certAlipayRequest.setPrivateKey(payInfo.rsaPrivateKey);
certAlipayRequest.setFormat(format);
certAlipayRequest.setCharset(charset);
certAlipayRequest.setSignType(signType);
//设置应用公钥证书路径
certAlipayRequest.setCertPath(payInfo.getAppCertPath());
//设置支付宝公钥证书路径
certAlipayRequest.setAlipayPublicCertPath(payInfo.alipayCertPath);
//certAlipayRequest.setAlipayPublicCertContent(AlipaySignature.getAlipayPublicKey(payInfo.alipayCertPath));
//设置支付宝根证书路径
certAlipayRequest.setRootCertPath(payInfo.getRootCertPath());
AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
/** 实例化具体API对应的request类类名称和接口名称对应,当前调用接口名称alipay.fund.trans.uni.transfer(单笔转账接口) **/
AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
/******必传参数******/
// 商户端的唯一订单号,对于同一笔转账请求,商户需保证该订单号唯一
model.setOutBizNo(trans_no);
// 转账金额TRANS_ACCOUNT_NO_PWD产品取值最低0.1
model.setTransAmount(String.valueOf(money.setScale(2, RoundingMode.HALF_UP)));
// 销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD。
model.setProductCode("TRANS_ACCOUNT_NO_PWD");
// 业务场景。单笔无密转账固定为 DIRECT_TRANSFER
model.setBizScene("DIRECT_TRANSFER");
// 转账业务的标题,用于在支付宝用户的账单里显示。
model.setOrderTitle("订单标题");
// 收款方信息
Participant payeeInfo = new Participant();
// 参与方的标识类型,设置ALIPAY_USER_ID或者ALIPAY_LOGON_ID
// ALIPAY_USER_ID支付宝会员的用户 ID可通过 获取会员信息 获取https://opendocs.alipay.com/open/284/106000
// ALIPAY_LOGON_ID支付宝登录号支持邮箱和手机号格式。
if (type == 1) {
payeeInfo.setIdentityType("ALIPAY_USER_ID");
}
if (type == 2) {
payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
}
// 参与方的标识 ID根据identity_type类型选择对应信息
// 当 identity_type=ALIPAY_USER_ID 时,填写支付宝用户 UID。示例值2088123412341234。
// 当 identity_type=ALIPAY_LOGON_ID 时填写支付宝登录号。示例值186xxxxxxxx。
payeeInfo.setIdentity(account);
// 参与方真实姓名。如果非空,将校验收款支付宝账号姓名一致性。
// 当 identity_type=ALIPAY_LOGON_ID 时,本字段必填。
payeeInfo.setName(trueName);
model.setPayeeInfo(payeeInfo);
/******可选参数******/
// 业务备注
model.setRemark(mark);
// 转账业务请求的扩展参数
// payer_show_name_use_alias是否展示付款方别名可选收款方在支付宝账单中可见。枚举支持
// * true展示别名将展示商家支付宝在商家中心 商户信息 > 商户基本信息 页面配置的 商户别名。
// * false不展示别名。默认为 false。
// model.setBusinessParams("{\"payer_show_name_use_alias\":\"true\"}");
request.setBizModel(model);
try {
// 发送请求并获取响应
// AlipayFundTransToaccountTransferResponse response = alipayClient.certificateExecute(request);
AlipayFundTransUniTransferResponse response = alipayClient.certificateExecute(request);
// 处理响应通常需要检查out_biz_notrade_no和resultCode
if (response.isSuccess()) {
resVo.setSuccess(true);
resVo.setMsg(response.getSubMsg());
//resVo.setBody(JSON.parseObject(response.getBody(), AliPayBodyVo.class));
resVo.setOrderId(response.getOrderId());
resVo.setStatus(response.getStatus());
resVo.setPayFundOrderID(response.getPayFundOrderId());
resVo.setTransDate(response.getTransDate());
resVo.setOutBizNo(response.getOutBizNo());
resVo.setSettleSerialNo(response.getSettleSerialNo());
resVo.setAmount(response.getAmount());
log.info("转账成功: " + response.getBody());
} else {
resVo.setSuccess(false);
resVo.setMsg(response.getSubMsg());
resVo.setCode(response.getCode());
resVo.setSubCode(response.getSubCode());
log.info("转账失败: " + response.getSubCode() + " - " + response.getSubMsg());
}
} catch (AlipayApiException e) {
//e.printStackTrace();
resVo.setSuccess(false);
resVo.setMsg(e.getMessage());
}
return resVo;
}
public static PrivateKey getPrivateKeyFromPath(String path) {
try {
FileInputStream fis = new FileInputStream(path);
byte[] keyBytes = new byte[fis.available()];
fis.read(keyBytes);
fis.close();
String privateKeyPEM = new String(keyBytes);
// 移除PEM头部和尾部
privateKeyPEM = privateKeyPEM.replace("-----BEGIN PRIVATE KEY-----", "");
privateKeyPEM = privateKeyPEM.replace("-----END PRIVATE KEY-----", "");
// 对于Windows平台请使用"\r\n"作为分隔符对于Linux/Mac平台应使用"\n"
privateKeyPEM = privateKeyPEM.replaceAll("\\r\\n|\\n|\\r", "");
byte[] decoded = Base64.decodeBase64(privateKeyPEM);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(keySpec);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String readFileContent(String filePath) {
StringBuilder content = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
content.append(line).append("\n");
}
} catch (IOException e) {
e.printStackTrace();
}
return content.toString();
}
}

View File

@@ -0,0 +1,270 @@
package cn.xf.basedemo.common.utils.pay;
import cn.xf.basedemo.common.model.pay.RefundInfoRsqDTO;
import cn.xf.basedemo.common.model.pay.TransferQueryVO;
import cn.xf.basedemo.common.model.pay.WechatCashQueryVo;
import cn.xf.basedemo.common.model.pay.WxPayTransRsqVo;
import com.alibaba.fastjson2.JSON;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
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.QueryByOutRefundNoRequest;
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.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class WxPayUtil {
private static String payPath;
private static String appId;
private static String wxMerchantId;
private static String wxApiV3Key;
private static String wxApiSerialNo;
private static String wxMerchantApiCertificate;
private static String wxMerchantApiPrivateKey;
private static String plantSerialNo;
private static String notifyUrl; //通知URL
private static String refundUrl; //退款充值URL
public WxPayUtil(String path, int type) {
payPath = path;
type = type;
String path1 = "d:/pay";
path1 = "";
refundUrl = "" + payPath;
switch (payPath) {
case "a"://提现打款用-文撩
appId = "";
wxMerchantId = "";
wxApiV3Key = "";
wxApiSerialNo = "";
wxMerchantApiCertificate = path1 + "9DE56.pem";
wxMerchantApiPrivateKey = path1 + "apiclient_key.pem";
plantSerialNo = "1A1D8C3A474A29DE56";
break;
}
}
@SneakyThrows
public WxPayTransRsqVo transfer(String trans_no, String account, BigDecimal money, String trueName, String mark) {
WxPayTransRsqVo rsqVo = new WxPayTransRsqVo();
rsqVo.setSuccess(false);
log.info("开始退款,{}-{}-{}-{}-{}", wxMerchantId, wxMerchantApiPrivateKey, wxApiSerialNo, wxApiV3Key, money);
Config config = new RSAAutoCertificateConfig.Builder()
.merchantId(wxMerchantId)
.privateKeyFromPath(wxMerchantApiPrivateKey)
.merchantSerialNumber(wxApiSerialNo)
.apiV3Key(wxApiV3Key)
.build();
TransferBatchService service = new TransferBatchService.Builder().config(config).build();
//数据封装
InitiateBatchTransferRequest initiateBatchTransferRequest = new InitiateBatchTransferRequest();
initiateBatchTransferRequest.setAppid(appId);
initiateBatchTransferRequest.setOutBatchNo(trans_no);
initiateBatchTransferRequest.setBatchName(mark);
initiateBatchTransferRequest.setBatchRemark(mark);
initiateBatchTransferRequest.setTotalAmount(money.multiply(BigDecimal.valueOf(100)).longValue());
initiateBatchTransferRequest.setTotalNum(1);
//initiateBatchTransferRequest.setTransferSceneId("1001");
{
List<TransferDetailInput> transferDetailListList = new ArrayList<>();
{
TransferDetailInput transferDetailInput = new TransferDetailInput();
transferDetailInput.setTransferAmount(money.multiply(BigDecimal.valueOf(100)).longValue());//金额为分 需要乘以100
transferDetailInput.setOutDetailNo(trans_no);
transferDetailInput.setOpenid(account);
transferDetailInput.setUserName(trueName);
transferDetailInput.setTransferRemark(mark);
transferDetailListList.add(transferDetailInput);
}
initiateBatchTransferRequest.setTransferDetailList(
transferDetailListList);
}
//发起商家转账
InitiateBatchTransferResponse response;
try {
response = service.initiateBatchTransfer(initiateBatchTransferRequest);
log.info("转账:", response.toString());
// if (response.getBatchStatus().equals("ACCEPTED")) {
// log.info("initiateBatchTransfer:", response.getBatchStatus());
// }
// log.error("initiateBatchTransfer:", response.getBatchStatus());
rsqVo.setSuccess(true);
} catch (ServiceException e) {
log.info("出错了:", e.getErrorMessage());
// e.printStackTrace();
rsqVo.setCode(e.getErrorCode());
rsqVo.setMsg(e.getErrorMessage());
}
return rsqVo;
}
@SneakyThrows
public TransferQueryVO transferQuery(String outBatchNo, String outDetailNo) {
TransferQueryVO transferQueryVO = new TransferQueryVO();
//商家转账批次单号
//商家转账明细单号
transferQueryVO.setSuccess(false);
Config config = new RSAAutoCertificateConfig.Builder()
.merchantId(wxMerchantId)
.privateKeyFromPath(wxMerchantApiPrivateKey)
.merchantSerialNumber(wxApiSerialNo)
.apiV3Key(wxApiV3Key)
.build();
try {
TransferBatchService transferBatchService = new TransferBatchService.Builder().config(config).build();
GetTransferDetailByOutNoRequest request = new GetTransferDetailByOutNoRequest();
request.setOutBatchNo(outBatchNo);
request.setOutDetailNo(outDetailNo);
TransferDetailEntity response = transferBatchService.getTransferDetailByOutNo(request);
log.info("转账返回信息:{}", JSON.toJSONString(response));
transferQueryVO.setCashQueryVo(JSON.parseObject(JSON.toJSONString(response), WechatCashQueryVo.class));
transferQueryVO.setResCode(response.getDetailStatus());
transferQueryVO.setResCodeDes(response.getDetailStatus());
if ("FAIL".equals(response.getDetailStatus())) {
// transferQueryVO.setResCodeDes(getErrorMsg(String.valueOf(response.getFailReason())));
transferQueryVO.setSuccess(false);
} else {
transferQueryVO.setSuccess(true);
}
log.info("转账返回信息II{}", JSON.toJSONString(transferQueryVO));
} catch (HttpException e) { // 发送HTTP请求失败
// 调用e.getHttpRequest()获取请求打印日志或上报监控更多方法见HttpException定义
log.error(e.getMessage());
transferQueryVO.setResCodeDes(e.getMessage());
// throw new RuntimeException("微信转账失败");
} catch (ServiceException e) { // 服务返回状态小于200或大于等于300例如500
// 调用e.getResponseBody()获取返回体打印日志或上报监控更多方法见ServiceException定义
log.error(e.getMessage());
transferQueryVO.setResCodeDes(e.getMessage());
//throw new RuntimeException("微信转账失败");
} catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
// 调用e.getMessage()获取信息打印日志或上报监控更多方法见MalformedMessageException定义
log.error(e.getMessage());
transferQueryVO.setResCodeDes(e.getMessage());
//throw new RuntimeException("微信转账失败");
} catch (Exception e) {
log.error(e.getMessage());
// e.printStackTrace();
transferQueryVO.setResCodeDes(e.getMessage());
//throw new RuntimeException("微信转账失败");
}
return transferQueryVO;
}
@SneakyThrows
public RefundInfoRsqDTO refundUser(String outTradeNo, String outRefundNo, long totalFee, BigDecimal refundFee) {
RefundInfoRsqDTO refundInfoRsqDTO = new RefundInfoRsqDTO();
refundInfoRsqDTO.setSuccess(false);
Config config = new RSAAutoCertificateConfig.Builder()
.merchantId(wxMerchantId)
.privateKeyFromPath(wxMerchantApiPrivateKey)
.merchantSerialNumber(wxApiSerialNo)
.apiV3Key(wxApiV3Key)
.build();
RefundService service = new RefundService.Builder().config(config).build();
CreateRequest request = new CreateRequest();
request.setOutTradeNo(outTradeNo);
request.setOutRefundNo(outRefundNo);
request.setNotifyUrl(refundUrl);
AmountReq amount = new AmountReq();
amount.setRefund(refundFee.longValue());
amount.setTotal(totalFee);
amount.setCurrency("CNY");
request.setAmount(amount);
try {
Refund refund = service.create(request);
log.info("refund:{}", refund.toString());
if (refund.getStatus().toString().equals("PROCESSING")) {
refundInfoRsqDTO.setSuccess(true);
}
refundInfoRsqDTO.setRefund(refund);
} catch (ServiceException e) {
log.error("退款失败:{}------{}-----{}", e.getErrorMessage(), e.getErrorCode(), e.getResponseBody());
e.printStackTrace();
refundInfoRsqDTO.setMsg(e.getErrorMessage());
}
return refundInfoRsqDTO;
}
public static RefundInfoRsqDTO queryRefund(String outRefundNo) {
RefundInfoRsqDTO refundInfoRsqDTO = new RefundInfoRsqDTO();
refundInfoRsqDTO.setSuccess(false);
Config config = new RSAAutoCertificateConfig.Builder()
.merchantId(wxMerchantId)
.privateKeyFromPath(wxMerchantApiPrivateKey)
.merchantSerialNumber(wxApiSerialNo)
.apiV3Key(wxApiV3Key)
.build();
RefundService service = new RefundService.Builder().config(config).build();
QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
request.setOutRefundNo(outRefundNo);
try {
Refund refund = service.queryByOutRefundNo(request);
log.info("refund:{}", refund.toString());
if (refund.getStatus().toString().equals("SUCCESS")) {
refundInfoRsqDTO.setSuccess(true);
}
refundInfoRsqDTO.setRefund(refund);
} catch (ServiceException e) {
log.error("查询失败:{}------{}-----{}", e.getErrorMessage(), e.getErrorCode(), e.getResponseBody());
e.printStackTrace();
refundInfoRsqDTO.setMsg(e.getErrorMessage());
}
return refundInfoRsqDTO;
}
// private String getErrorMsg(String errCode) {
// return switch (errCode) {
// case "ACCOUNT_FROZEN" -> "账户冻结";
// case "REAL_NAME_CHECK_FAIL" -> "用户未实名";
// case "NAME_NOT_CORRECT" -> "用户姓名校验失败";
// case "OPENID_INVALID" -> "Openid校验失败";
// case "TRANSFER_QUOTA_EXCEED" -> "超过用户单笔收款额度";
// case "DAY_RECEIVED_QUOTA_EXCEED" -> "超过用户单日收款额度";
// case "MONTH_RECEIVED_QUOTA_EXCEED" -> "超过用户单月收款额度";
// case "DAY_RECEIVED_COUNT_EXCEED" -> "超过用户单日收款次数";
// case "PRODUCT_AUTH_CHECK_FAIL" -> "产品权限校验失败";
// case "OVERDUE_CLOSE" -> "转账关闭";
// case "ID_CARD_NOT_CORRECT" -> "用户身份证校验失败";
// case "ACCOUNT_NOT_EXIST" -> "用户账户不存在";
// case "TRANSFER_RISK" -> "转账存在风险";
// case "REALNAME_ACCOUNT_RECEIVED_QUOTA_EXCEED" -> "用户账户收款受限,请引导用户在微信支付查看详情";
// case "RECEIVE_ACCOUNT_NOT_PERMMIT" -> "未配置该用户为转账收款人";
// case "PAYER_ACCOUNT_ABNORMAL" -> "商户账户付款受限,可前往商户平台-违约记录获取解除功能限制指引";
// case "PAYEE_ACCOUNT_ABNORMAL" -> "用户账户收款异常,请引导用户完善其在微信支付的身份信息以继续收款";
// case "TRANSFER_REMARK_SET_FAIL" -> "转账备注设置失败,请调整对应文案后重新再试";
// default -> "内部错误,请联系管理人员";
// };
// }
}