no message
This commit is contained in:
433
src/main/java/com/sczx/pay/service/WechatPayService.java
Normal file
433
src/main/java/com/sczx/pay/service/WechatPayService.java
Normal file
@ -0,0 +1,433 @@
|
||||
package com.sczx.pay.service;
|
||||
|
||||
import com.sczx.pay.config.DynamicWXPayConfig;
|
||||
import com.sczx.pay.dto.PaymentRequest;
|
||||
import com.sczx.pay.dto.PaymentResponse;
|
||||
import com.sczx.pay.dto.RefundRequest;
|
||||
import com.sczx.pay.entity.CompanyWechatConfig;
|
||||
import com.sczx.pay.entity.PaymentRecord;
|
||||
import com.sczx.pay.entity.RefundRecord;
|
||||
import com.sczx.pay.mapper.CompanyWechatConfigMapper;
|
||||
import com.sczx.pay.mapper.PaymentRecordMapper;
|
||||
import com.sczx.pay.mapper.RefundRecordMapper;
|
||||
import com.sczx.pay.sdk.WXPay;
|
||||
import com.sczx.pay.utils.WXPayUtil;
|
||||
import com.sczx.pay.utils.IPUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 微信支付服务类
|
||||
*/
|
||||
@Service
|
||||
public class WechatPayService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(WechatPayService.class);
|
||||
|
||||
@Autowired
|
||||
private CompanyWechatConfigMapper companyWechatConfigMapper;
|
||||
|
||||
@Autowired
|
||||
private PaymentRecordMapper paymentRecordMapper;
|
||||
|
||||
@Autowired
|
||||
private RefundRecordMapper refundRecordMapper;
|
||||
|
||||
@Value("${wechat.pay.app-id}")
|
||||
private String appId;
|
||||
|
||||
@Value("${wechat.pay.notify-url}")
|
||||
private String notifyUrl;
|
||||
|
||||
/**
|
||||
* 小程序统一下单
|
||||
*/
|
||||
@Transactional
|
||||
public PaymentResponse unifiedOrder(PaymentRequest request) {
|
||||
PaymentResponse response = new PaymentResponse();
|
||||
|
||||
try {
|
||||
// 根据companyId获取微信支付配置
|
||||
CompanyWechatConfig companyConfig = companyWechatConfigMapper.getWechatConfigByCompanyId(request.getCompanyId());
|
||||
if (companyConfig == null) {
|
||||
response.setCode("FAIL");
|
||||
response.setMessage("未找到公司对应的微信支付配置");
|
||||
return response;
|
||||
}
|
||||
|
||||
// 创建动态配置
|
||||
DynamicWXPayConfig wxPayConfig = new DynamicWXPayConfig(
|
||||
appId,
|
||||
companyConfig.getMchId(),
|
||||
companyConfig.getApikey(),
|
||||
notifyUrl
|
||||
);
|
||||
|
||||
WXPay wxPay = new WXPay(wxPayConfig);
|
||||
|
||||
// 构造请求参数
|
||||
Map<String, String> reqData = new HashMap<>();
|
||||
reqData.put("body", request.getBody());
|
||||
reqData.put("out_trade_no", request.getOutTradeNo());
|
||||
reqData.put("total_fee", String.valueOf(request.getTotalFee()));
|
||||
// 自动获取服务器公网IP
|
||||
reqData.put("spbill_create_ip", IPUtils.getServerPublicIP());
|
||||
reqData.put("notify_url", notifyUrl);
|
||||
reqData.put("trade_type", "JSAPI");
|
||||
reqData.put("openid", request.getOpenId());
|
||||
|
||||
if (request.getAttach() != null) {
|
||||
reqData.put("attach", request.getAttach());
|
||||
}
|
||||
|
||||
// 调用微信统一下单接口
|
||||
Map<String, String> result = wxPay.unifiedOrder(reqData);
|
||||
|
||||
logger.info("微信统一下单结果: {}", result);
|
||||
|
||||
// 处理返回结果
|
||||
if ("SUCCESS".equals(result.get("return_code"))) {
|
||||
if ("SUCCESS".equals(result.get("result_code"))) {
|
||||
// 构造小程序支付参数
|
||||
Map<String, String> payData = buildPayData(result.get("prepay_id"), wxPayConfig);
|
||||
response.setCode("SUCCESS");
|
||||
response.setMessage("下单成功");
|
||||
response.setPayData(payData);
|
||||
|
||||
// 记录支付信息到数据库
|
||||
recordPaymentInfo(request, companyConfig);
|
||||
} else {
|
||||
response.setCode("FAIL");
|
||||
response.setMessage(result.get("err_code_des"));
|
||||
}
|
||||
} else {
|
||||
response.setCode("FAIL");
|
||||
response.setMessage(result.get("return_msg"));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("微信统一下单异常", e);
|
||||
response.setCode("ERROR");
|
||||
response.setMessage("系统异常: " + e.getMessage());
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录支付信息到数据库
|
||||
*/
|
||||
private void recordPaymentInfo(PaymentRequest request, CompanyWechatConfig companyConfig) {
|
||||
try {
|
||||
PaymentRecord paymentRecord = new PaymentRecord();
|
||||
paymentRecord.setCompanyId(request.getCompanyId());
|
||||
paymentRecord.setOutTradeNo(request.getOutTradeNo());
|
||||
paymentRecord.setTotalFee(new BigDecimal(request.getTotalFee()).divide(new BigDecimal(100))); // 转换为元
|
||||
paymentRecord.setBody(request.getBody());
|
||||
paymentRecord.setOpenid(request.getOpenId());
|
||||
paymentRecord.setTradeState("NOTPAY"); // 未支付
|
||||
paymentRecord.setTradeStateDesc("未支付");
|
||||
paymentRecord.setCreateTime(new Date());
|
||||
paymentRecord.setUpdateTime(new Date());
|
||||
paymentRecord.setAttach(request.getAttach());
|
||||
paymentRecord.setPayChannel(PaymentRecord.PayChannel.WECHAT.name()); // 设置支付渠道为微信支付
|
||||
|
||||
paymentRecordMapper.insertPaymentRecord(paymentRecord);
|
||||
logger.info("支付记录已保存,订单号: {}", request.getOutTradeNo());
|
||||
} catch (Exception e) {
|
||||
logger.error("保存支付记录异常,订单号: {}", request.getOutTradeNo(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询订单
|
||||
*/
|
||||
public Map<String, String> orderQuery(Long companyId, String outTradeNo) throws Exception {
|
||||
// 根据companyId获取微信支付配置
|
||||
CompanyWechatConfig companyConfig = companyWechatConfigMapper.getWechatConfigByCompanyId(companyId);
|
||||
if (companyConfig == null) {
|
||||
Map<String, String> errorResult = new HashMap<>();
|
||||
errorResult.put("return_code", "FAIL");
|
||||
errorResult.put("return_msg", "未找到公司对应的微信支付配置");
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
// 创建动态配置
|
||||
DynamicWXPayConfig wxPayConfig = new DynamicWXPayConfig(
|
||||
appId,
|
||||
companyConfig.getMchId(),
|
||||
companyConfig.getApikey(),
|
||||
notifyUrl
|
||||
);
|
||||
|
||||
WXPay wxPay = new WXPay(wxPayConfig);
|
||||
|
||||
Map<String, String> reqData = new HashMap<>();
|
||||
reqData.put("out_trade_no", outTradeNo);
|
||||
|
||||
return wxPay.orderQuery(reqData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭订单
|
||||
*/
|
||||
public Map<String, String> closeOrder(Long companyId, String outTradeNo) throws Exception {
|
||||
// 根据companyId获取微信支付配置
|
||||
CompanyWechatConfig companyConfig = companyWechatConfigMapper.getWechatConfigByCompanyId(companyId);
|
||||
if (companyConfig == null) {
|
||||
Map<String, String> errorResult = new HashMap<>();
|
||||
errorResult.put("return_code", "FAIL");
|
||||
errorResult.put("return_msg", "未找到公司对应的微信支付配置");
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
// 创建动态配置
|
||||
DynamicWXPayConfig wxPayConfig = new DynamicWXPayConfig(
|
||||
appId,
|
||||
companyConfig.getMchId(),
|
||||
companyConfig.getApikey(),
|
||||
notifyUrl
|
||||
);
|
||||
|
||||
WXPay wxPay = new WXPay(wxPayConfig);
|
||||
|
||||
Map<String, String> reqData = new HashMap<>();
|
||||
reqData.put("out_trade_no", outTradeNo);
|
||||
|
||||
return wxPay.closeOrder(reqData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请退款
|
||||
*/
|
||||
@Transactional
|
||||
public Map<String, String> refund(RefundRequest request) throws Exception {
|
||||
// 根据companyId获取微信支付配置
|
||||
CompanyWechatConfig companyConfig = companyWechatConfigMapper.getWechatConfigByCompanyId(request.getCompanyId());
|
||||
if (companyConfig == null) {
|
||||
Map<String, String> errorResult = new HashMap<>();
|
||||
errorResult.put("return_code", "FAIL");
|
||||
errorResult.put("return_msg", "未找到公司对应的微信支付配置");
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
// 创建动态配置
|
||||
DynamicWXPayConfig wxPayConfig = new DynamicWXPayConfig(
|
||||
appId,
|
||||
companyConfig.getMchId(),
|
||||
companyConfig.getApikey(),
|
||||
notifyUrl
|
||||
);
|
||||
|
||||
WXPay wxPay = new WXPay(wxPayConfig);
|
||||
|
||||
Map<String, String> reqData = new HashMap<>();
|
||||
reqData.put("out_trade_no", request.getOutTradeNo());
|
||||
reqData.put("out_refund_no", request.getOutRefundNo());
|
||||
reqData.put("total_fee", String.valueOf(request.getTotalFee()));
|
||||
reqData.put("refund_fee", String.valueOf(request.getRefundFee()));
|
||||
|
||||
if (request.getRefundDesc() != null) {
|
||||
reqData.put("refund_desc", request.getRefundDesc());
|
||||
}
|
||||
|
||||
// 退款需要证书,这里调用带证书的接口
|
||||
Map<String, String> result = wxPay.refund(reqData);
|
||||
|
||||
// 记录退款信息到数据库
|
||||
if ("SUCCESS".equals(result.get("return_code"))) {
|
||||
recordRefundInfo(request, companyConfig, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录退款信息到数据库
|
||||
*/
|
||||
private void recordRefundInfo(RefundRequest request, CompanyWechatConfig companyConfig, Map<String, String> result) {
|
||||
try {
|
||||
RefundRecord refundRecord = new RefundRecord();
|
||||
refundRecord.setCompanyId(request.getCompanyId());
|
||||
refundRecord.setOutTradeNo(request.getOutTradeNo());
|
||||
refundRecord.setOutRefundNo(request.getOutRefundNo());
|
||||
refundRecord.setTotalFee(new BigDecimal(request.getTotalFee()).divide(new BigDecimal(100))); // 转换为元
|
||||
refundRecord.setRefundFee(new BigDecimal(request.getRefundFee()).divide(new BigDecimal(100))); // 转换为元
|
||||
refundRecord.setRefundDesc(request.getRefundDesc());
|
||||
refundRecord.setCreateTime(new Date());
|
||||
refundRecord.setUpdateTime(new Date());
|
||||
|
||||
if ("SUCCESS".equals(result.get("result_code"))) {
|
||||
refundRecord.setRefundStatus("PROCESSING"); // 退款处理中
|
||||
refundRecord.setRefundStatusDesc("退款处理中");
|
||||
refundRecord.setRefundId(result.get("refund_id"));
|
||||
} else {
|
||||
refundRecord.setRefundStatus("FAIL"); // 退款失败
|
||||
refundRecord.setRefundStatusDesc(result.get("err_code_des"));
|
||||
}
|
||||
|
||||
refundRecordMapper.insertRefundRecord(refundRecord);
|
||||
logger.info("退款记录已保存,退款单号: {}", request.getOutRefundNo());
|
||||
} catch (Exception e) {
|
||||
logger.error("保存退款记录异常,退款单号: {}", request.getOutRefundNo(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询退款
|
||||
*/
|
||||
public Map<String, String> refundQuery(Long companyId, String outTradeNo) throws Exception {
|
||||
// 根据companyId获取微信支付配置
|
||||
CompanyWechatConfig companyConfig = companyWechatConfigMapper.getWechatConfigByCompanyId(companyId);
|
||||
if (companyConfig == null) {
|
||||
Map<String, String> errorResult = new HashMap<>();
|
||||
errorResult.put("return_code", "FAIL");
|
||||
errorResult.put("return_msg", "未找到公司对应的微信支付配置");
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
// 创建动态配置
|
||||
DynamicWXPayConfig wxPayConfig = new DynamicWXPayConfig(
|
||||
appId,
|
||||
companyConfig.getMchId(),
|
||||
companyConfig.getApikey(),
|
||||
notifyUrl
|
||||
);
|
||||
|
||||
WXPay wxPay = new WXPay(wxPayConfig);
|
||||
|
||||
Map<String, String> reqData = new HashMap<>();
|
||||
reqData.put("out_trade_no", outTradeNo);
|
||||
|
||||
return wxPay.refundQuery(reqData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造小程序支付参数
|
||||
*/
|
||||
private Map<String, String> buildPayData(String prepayId, DynamicWXPayConfig wxPayConfig) throws Exception {
|
||||
Map<String, String> payData = new HashMap<>();
|
||||
payData.put("appId", wxPayConfig.getAppID());
|
||||
payData.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
|
||||
payData.put("nonceStr", WXPayUtil.generateNonceStr());
|
||||
payData.put("package", "prepay_id=" + prepayId);
|
||||
payData.put("signType", "MD5");
|
||||
|
||||
// 生成签名
|
||||
String paySign = WXPayUtil.generateSignature(payData, wxPayConfig.getKey());
|
||||
payData.put("paySign", paySign);
|
||||
|
||||
return payData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证微信支付通知签名
|
||||
*/
|
||||
public boolean verifyNotifySign(Long companyId, Map<String, String> notifyData) {
|
||||
try {
|
||||
// 根据companyId获取微信支付配置
|
||||
CompanyWechatConfig companyConfig = companyWechatConfigMapper.getWechatConfigByCompanyId(companyId);
|
||||
if (companyConfig == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return WXPayUtil.isSignatureValid(notifyData, companyConfig.getApikey());
|
||||
} catch (Exception e) {
|
||||
logger.error("验证微信支付通知签名异常", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理支付成功通知并更新订单状态
|
||||
*/
|
||||
@Transactional
|
||||
public boolean processPaySuccessNotify(Long companyId, Map<String, String> notifyData) {
|
||||
try {
|
||||
String outTradeNo = notifyData.get("out_trade_no");
|
||||
String transactionId = notifyData.get("transaction_id");
|
||||
String totalFee = notifyData.get("total_fee");
|
||||
|
||||
// 更新支付记录状态
|
||||
int updated = paymentRecordMapper.updateToSuccess(
|
||||
outTradeNo,
|
||||
transactionId,
|
||||
new Date(), // 支付时间
|
||||
new Date() // 更新时间
|
||||
);
|
||||
|
||||
if (updated > 0) {
|
||||
logger.info("微信支付记录状态已更新,订单号: {}, 微信交易号: {}", outTradeNo, transactionId);
|
||||
// TODO: 在这里调用其他业务服务更新实际订单状态
|
||||
return true;
|
||||
} else {
|
||||
logger.warn("未找到对应的微信支付记录,订单号: {}", outTradeNo);
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("处理微信支付成功通知异常,订单号: {}", notifyData.get("out_trade_no"), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理退款成功通知并更新退款状态
|
||||
*/
|
||||
@Transactional
|
||||
public boolean processRefundSuccessNotify(Long companyId, Map<String, String> notifyData) {
|
||||
try {
|
||||
String outRefundNo = notifyData.get("out_refund_no");
|
||||
String refundId = notifyData.get("refund_id");
|
||||
String refundStatus = notifyData.get("refund_status");
|
||||
|
||||
// 根据退款状态更新退款记录
|
||||
String statusDesc = "";
|
||||
switch (refundStatus) {
|
||||
case "SUCCESS":
|
||||
statusDesc = "退款成功";
|
||||
break;
|
||||
case "REFUNDCLOSE":
|
||||
statusDesc = "退款关闭";
|
||||
break;
|
||||
case "PROCESSING":
|
||||
statusDesc = "退款处理中";
|
||||
break;
|
||||
case "CHANGE":
|
||||
statusDesc = "退款异常";
|
||||
break;
|
||||
default:
|
||||
statusDesc = "未知状态";
|
||||
}
|
||||
|
||||
int updated = refundRecordMapper.updateRefundStatus(
|
||||
outRefundNo,
|
||||
refundStatus,
|
||||
statusDesc,
|
||||
refundId,
|
||||
"SUCCESS".equals(refundStatus) ? new Date() : null, // 退款成功时间
|
||||
new Date() // 更新时间
|
||||
);
|
||||
|
||||
if (updated > 0) {
|
||||
logger.info("退款记录状态已更新,退款单号: {}, 微信退款单号: {}, 状态: {}", outRefundNo, refundId, refundStatus);
|
||||
// TODO: 在这里调用其他业务服务更新实际订单退款状态
|
||||
return true;
|
||||
} else {
|
||||
logger.warn("未找到对应的退款记录,退款单号: {}", outRefundNo);
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("处理退款成功通知异常,退款单号: {}", notifyData.get("out_refund_no"), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user