no message
This commit is contained in:
@ -3,6 +3,7 @@ package com.sczx.pay.controller;
|
|||||||
import com.sczx.pay.dto.PaymentRequest;
|
import com.sczx.pay.dto.PaymentRequest;
|
||||||
import com.sczx.pay.dto.PaymentResponse;
|
import com.sczx.pay.dto.PaymentResponse;
|
||||||
import com.sczx.pay.dto.RefundRequest;
|
import com.sczx.pay.dto.RefundRequest;
|
||||||
|
import com.sczx.pay.mapper.CompanyWechatConfigMapper;
|
||||||
import com.sczx.pay.service.WechatPayService;
|
import com.sczx.pay.service.WechatPayService;
|
||||||
import com.sczx.pay.utils.WXPayUtil;
|
import com.sczx.pay.utils.WXPayUtil;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -27,6 +28,9 @@ public class PaymentController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private WechatPayService wechatPayService;
|
private WechatPayService wechatPayService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CompanyWechatConfigMapper companyWechatConfigMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 小程序统一下单接口
|
* 小程序统一下单接口
|
||||||
*/
|
*/
|
||||||
@ -165,7 +169,59 @@ public class PaymentController {
|
|||||||
return buildResponse("FAIL", "处理异常");
|
return buildResponse("FAIL", "处理异常");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@PostMapping("/refundNotify")
|
||||||
|
public String refundNotify(@PathVariable HttpServletRequest request) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 读取微信退款回调数据
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
BufferedReader reader = request.getReader();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
sb.append(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
String xmlData = sb.toString();
|
||||||
|
logger.info("收到微信退款通知, 数据: {}", xmlData);
|
||||||
|
|
||||||
|
// 解析XML数据
|
||||||
|
Map<String, String> notifyMap = WXPayUtil.xmlToMap(xmlData);
|
||||||
|
|
||||||
|
Long companyId = companyWechatConfigMapper.getCompanyIdByMchId(notifyMap.get("mch_id"));
|
||||||
|
|
||||||
|
// 验证签名
|
||||||
|
if (!wechatPayService.verifyNotifySign(companyId, notifyMap)) {
|
||||||
|
logger.warn("微信退款通知签名验证失败,公司ID: {}", companyId);
|
||||||
|
return buildResponse("FAIL", "签名失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
String returnCode = notifyMap.get("return_code");
|
||||||
|
if (!"SUCCESS".equals(returnCode)) {
|
||||||
|
logger.warn("微信退款通知返回失败,公司ID: {}: {}", companyId, notifyMap.get("return_msg"));
|
||||||
|
return buildResponse("FAIL", "返回失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理退款通知的业务逻辑
|
||||||
|
String outRefundNo = notifyMap.get("out_refund_no");
|
||||||
|
String refundId = notifyMap.get("refund_id");
|
||||||
|
String refundStatus = notifyMap.get("refund_status");
|
||||||
|
|
||||||
|
// 更新数据库中的退款状态
|
||||||
|
boolean success = wechatPayService.processRefundNotify(companyId, notifyMap);
|
||||||
|
if (success) {
|
||||||
|
logger.info("退款处理完成,公司ID: {}, 退款单号: {}, 微信退款单号: {}, 状态: {}",
|
||||||
|
companyId, outRefundNo, refundId, refundStatus);
|
||||||
|
return buildResponse("SUCCESS", "OK");
|
||||||
|
} else {
|
||||||
|
logger.error("更新退款状态失败,公司ID: {}, 退款单号: {}", companyId, outRefundNo);
|
||||||
|
return buildResponse("FAIL", "更新退款状态失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("处理微信退款通知异常", e);
|
||||||
|
return buildResponse("FAIL", "处理异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
private String buildResponse(String returnCode, String returnMsg) {
|
private String buildResponse(String returnCode, String returnMsg) {
|
||||||
Map<String, String> response = new HashMap<>();
|
Map<String, String> response = new HashMap<>();
|
||||||
response.put("return_code", returnCode);
|
response.put("return_code", returnCode);
|
||||||
|
|||||||
28
src/main/java/com/sczx/pay/entity/OrderMain.java
Normal file
28
src/main/java/com/sczx/pay/entity/OrderMain.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package com.sczx.pay.entity;
|
||||||
|
|
||||||
|
public class OrderMain {
|
||||||
|
private Long orderId;
|
||||||
|
private String orderNo;
|
||||||
|
private String orderStatus;
|
||||||
|
|
||||||
|
public Long getOrderId() {
|
||||||
|
return orderId;
|
||||||
|
}
|
||||||
|
public void setOrderId(Long orderId) {
|
||||||
|
this.orderId = orderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrderNo() {
|
||||||
|
return orderNo;
|
||||||
|
}
|
||||||
|
public void setOrderNo(String orderNo) {
|
||||||
|
this.orderNo = orderNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrderStatus() {
|
||||||
|
return orderStatus;
|
||||||
|
}
|
||||||
|
public void setOrderStatus(String orderStatus) {
|
||||||
|
this.orderStatus = orderStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
113
src/main/java/com/sczx/pay/entity/PayStatus.java
Normal file
113
src/main/java/com/sczx/pay/entity/PayStatus.java
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
package com.sczx.pay.entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付状态枚举
|
||||||
|
*/
|
||||||
|
public enum PayStatus {
|
||||||
|
/**
|
||||||
|
* 未支付
|
||||||
|
*/
|
||||||
|
NOTPAY(1, "NOTPAY", "未支付"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付成功
|
||||||
|
*/
|
||||||
|
SUCCESS(2, "SUCCESS", "支付成功"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转入退款
|
||||||
|
*/
|
||||||
|
REFUND(3, "REFUND", "转入退款"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已关闭
|
||||||
|
*/
|
||||||
|
CLOSED(4, "CLOSED", "已关闭"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已撤销
|
||||||
|
*/
|
||||||
|
REVOKED(5, "REVOKED", "已撤销"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付失败
|
||||||
|
*/
|
||||||
|
PAYERROR(6, "PAYERROR", "支付失败");
|
||||||
|
|
||||||
|
private final int id;
|
||||||
|
private final String code;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
PayStatus(int id, String code, String description) {
|
||||||
|
this.id = id;
|
||||||
|
this.code = code;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取支付状态
|
||||||
|
*/
|
||||||
|
public static PayStatus fromId(int id) {
|
||||||
|
for (PayStatus status : PayStatus.values()) {
|
||||||
|
if (status.getId() == id) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("未知的支付状态ID: " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据编码获取支付状态
|
||||||
|
*/
|
||||||
|
public static PayStatus fromCode(String code) {
|
||||||
|
for (PayStatus status : PayStatus.values()) {
|
||||||
|
if (status.getCode().equals(code)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("未知的支付状态编码: " + code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断支付是否成功
|
||||||
|
*/
|
||||||
|
public boolean isPaySuccess() {
|
||||||
|
return this == SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为退款状态
|
||||||
|
*/
|
||||||
|
public boolean isRefund() {
|
||||||
|
return this == REFUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断交易是否完成(成功或关闭)
|
||||||
|
*/
|
||||||
|
public boolean isTradeFinished() {
|
||||||
|
return this == SUCCESS || this == CLOSED || this == REVOKED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter方法
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "PayStatus{" +
|
||||||
|
"id=" + id +
|
||||||
|
", code='" + code + '\'' +
|
||||||
|
", description='" + description + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
169
src/main/java/com/sczx/pay/entity/PaymentNotifyRecord.java
Normal file
169
src/main/java/com/sczx/pay/entity/PaymentNotifyRecord.java
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
package com.sczx.pay.entity;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付通知记录实体类
|
||||||
|
*/
|
||||||
|
public class PaymentNotifyRecord {
|
||||||
|
private Long id;
|
||||||
|
private Long companyId;
|
||||||
|
private String outTradeNo; // 商户订单号
|
||||||
|
private String outRefundNo; // 商户退款单号
|
||||||
|
private String notifyData; // 通知原始数据
|
||||||
|
private String notifyType; // 通知类型:PAY-支付通知,REFUND-退款通知
|
||||||
|
private String tradeStatus; // 交易状态
|
||||||
|
private String refundStatus; // 退款状态
|
||||||
|
private String payChannel; // 支付渠道:WECHAT-微信支付,ALIPAY-支付宝
|
||||||
|
private Integer processStatus; // 处理状态:0-未处理,1-已处理,2-处理失败
|
||||||
|
private String processResult; // 处理结果
|
||||||
|
private Date createTime; // 创建时间
|
||||||
|
private Date updateTime; // 更新时间
|
||||||
|
|
||||||
|
// 通知类型枚举
|
||||||
|
public enum NotifyType {
|
||||||
|
PAY("支付通知"),
|
||||||
|
REFUND("退款通知");
|
||||||
|
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
NotifyType(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理状态枚举
|
||||||
|
public enum ProcessStatus {
|
||||||
|
UNPROCESSED(0, "未处理"),
|
||||||
|
PROCESSED(1, "已处理"),
|
||||||
|
FAILED(2, "处理失败");
|
||||||
|
|
||||||
|
private final int code;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
ProcessStatus(int code, String description) {
|
||||||
|
this.code = code;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造函数
|
||||||
|
public PaymentNotifyRecord() {}
|
||||||
|
|
||||||
|
// Getter和Setter方法
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getCompanyId() {
|
||||||
|
return companyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCompanyId(Long companyId) {
|
||||||
|
this.companyId = companyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOutTradeNo() {
|
||||||
|
return outTradeNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutTradeNo(String outTradeNo) {
|
||||||
|
this.outTradeNo = outTradeNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOutRefundNo() {
|
||||||
|
return outRefundNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutRefundNo(String outRefundNo) {
|
||||||
|
this.outRefundNo = outRefundNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNotifyData() {
|
||||||
|
return notifyData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNotifyData(String notifyData) {
|
||||||
|
this.notifyData = notifyData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNotifyType() {
|
||||||
|
return notifyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNotifyType(String notifyType) {
|
||||||
|
this.notifyType = notifyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTradeStatus() {
|
||||||
|
return tradeStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTradeStatus(String tradeStatus) {
|
||||||
|
this.tradeStatus = tradeStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRefundStatus() {
|
||||||
|
return refundStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRefundStatus(String refundStatus) {
|
||||||
|
this.refundStatus = refundStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPayChannel() {
|
||||||
|
return payChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayChannel(String payChannel) {
|
||||||
|
this.payChannel = payChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getProcessStatus() {
|
||||||
|
return processStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProcessStatus(Integer processStatus) {
|
||||||
|
this.processStatus = processStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProcessResult() {
|
||||||
|
return processResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProcessResult(String processResult) {
|
||||||
|
this.processResult = processResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getCreateTime() {
|
||||||
|
return createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreateTime(Date createTime) {
|
||||||
|
this.createTime = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getUpdateTime() {
|
||||||
|
return updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpdateTime(Date updateTime) {
|
||||||
|
this.updateTime = updateTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
110
src/main/java/com/sczx/pay/entity/RefundStatus.java
Normal file
110
src/main/java/com/sczx/pay/entity/RefundStatus.java
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package com.sczx.pay.entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款状态枚举
|
||||||
|
*/
|
||||||
|
public enum RefundStatus {
|
||||||
|
/**
|
||||||
|
* 退款处理中
|
||||||
|
*/
|
||||||
|
PROCESSING(1, "PROCESSING", "退款处理中"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款成功
|
||||||
|
*/
|
||||||
|
SUCCESS(2, "SUCCESS", "退款成功"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款关闭
|
||||||
|
*/
|
||||||
|
REFUNDCLOSE(3, "REFUNDCLOSE", "退款关闭"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款异常
|
||||||
|
*/
|
||||||
|
CHANGE(4, "CHANGE", "退款异常");
|
||||||
|
|
||||||
|
private final int id;
|
||||||
|
private final String code;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
RefundStatus(int id, String code, String description) {
|
||||||
|
this.id = id;
|
||||||
|
this.code = code;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取退款状态
|
||||||
|
*/
|
||||||
|
public static RefundStatus fromId(int id) {
|
||||||
|
for (RefundStatus status : RefundStatus.values()) {
|
||||||
|
if (status.getId() == id) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("未知的退款状态ID: " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据编码获取退款状态
|
||||||
|
*/
|
||||||
|
public static RefundStatus fromCode(String code) {
|
||||||
|
for (RefundStatus status : RefundStatus.values()) {
|
||||||
|
if (status.getCode().equals(code)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("未知的退款状态编码: " + code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断退款是否成功
|
||||||
|
*/
|
||||||
|
public boolean isRefundSuccess() {
|
||||||
|
return this == SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断退款是否完成(成功或关闭)
|
||||||
|
*/
|
||||||
|
public boolean isRefundFinished() {
|
||||||
|
return this == SUCCESS || this == REFUNDCLOSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断退款是否异常
|
||||||
|
*/
|
||||||
|
public boolean isRefundAbnormal() {
|
||||||
|
return this == CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断退款是否处理中
|
||||||
|
*/
|
||||||
|
public boolean isProcessing() {
|
||||||
|
return this == PROCESSING;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter方法
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "RefundStatus{" +
|
||||||
|
"id=" + id +
|
||||||
|
", code='" + code + '\'' +
|
||||||
|
", description='" + description + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,4 +15,8 @@ public interface CompanyWechatConfigMapper {
|
|||||||
*/
|
*/
|
||||||
@Select("SELECT id, wechat_receiving_account AS mchId, wechat_key AS apikey FROM zc_company WHERE id = #{companyId}")
|
@Select("SELECT id, wechat_receiving_account AS mchId, wechat_key AS apikey FROM zc_company WHERE id = #{companyId}")
|
||||||
CompanyWechatConfig getWechatConfigByCompanyId(@Param("companyId") Long companyId);
|
CompanyWechatConfig getWechatConfigByCompanyId(@Param("companyId") Long companyId);
|
||||||
|
|
||||||
|
|
||||||
|
@Select("SELECT id FROM zc_company WHERE wechat_receiving_account = #{mchId}")
|
||||||
|
Long getCompanyIdByMchId(@Param("mchId") String mchId);
|
||||||
}
|
}
|
||||||
|
|||||||
51
src/main/java/com/sczx/pay/mapper/OrderPayMapper.java
Normal file
51
src/main/java/com/sczx/pay/mapper/OrderPayMapper.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package com.sczx.pay.mapper;
|
||||||
|
|
||||||
|
import com.sczx.pay.entity.OrderMain;
|
||||||
|
import com.sczx.pay.entity.PayStatus;
|
||||||
|
import com.sczx.pay.entity.PaymentRecord;
|
||||||
|
import org.apache.ibatis.annotations.*;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface OrderPayMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据商户订单号更新支付状态
|
||||||
|
*/
|
||||||
|
@Update("UPDATE zc_order_main SET pay_type = #{payType}, pay_status = #{payStatus}, " +
|
||||||
|
"payment_no = #{transactionId}" +
|
||||||
|
"WHERE order_no = #{outTradeNo}")
|
||||||
|
int updatePaymentStatus(@Param("outTradeNo") String outTradeNo,
|
||||||
|
@Param("payType") String payType,
|
||||||
|
@Param("payStatus") String payStatus,
|
||||||
|
@Param("transactionId") String transactionId);
|
||||||
|
|
||||||
|
@Update("update zc_order_main as om,zc_order_sub as os set os.transaction_id = #{transactionId},os.pay_status = #{payStatus}," +
|
||||||
|
"os.payment_method = #{payType}" +
|
||||||
|
" where om.order_id = os.order_id and om.order_no = #{outTradeNo}")
|
||||||
|
int updateSubOrderPaymentStatus(@Param("outTradeNo") String outTradeNo,
|
||||||
|
@Param("payType") String payType,
|
||||||
|
@Param("payStatus") String payStatus,
|
||||||
|
@Param("transactionId") String transactionId);
|
||||||
|
|
||||||
|
@Update("UPDATE zc_order_main SET order_status = #{orderStatus}" +
|
||||||
|
"WHERE order_no = #{outTradeNo}")
|
||||||
|
int updateOrderStatus(@Param("outTradeNo") String outTradeNo,
|
||||||
|
@Param("orderStatus") String orderStatus);
|
||||||
|
|
||||||
|
@Update("update zc_order_main as om,zc_order_sub as os set os.transaction_id = #{paymentId},os.pay_status = #{payStatus}" +
|
||||||
|
" where om.order_id = os.order_id and om.order_no = #{outTradeNo} and suborder_type = 'DEPOSIT'")
|
||||||
|
int updateRefundOrderStatus(@Param("outTradeNo") String outTradeNo,
|
||||||
|
@Param("orderStatus") String orderStatus,
|
||||||
|
@Param("transactionId") String transactionId);
|
||||||
|
|
||||||
|
|
||||||
|
@Select("select order_id,order_no,order_status from zc_order_main " +
|
||||||
|
"where order_id in (select order_id from zc_order_sub " +
|
||||||
|
"where payment_id = #{paymentId} and del_flag = '0')")
|
||||||
|
OrderMain getOrderStatusByOrderNo(@Param("paymentId") String paymentId);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.sczx.pay.mapper;
|
||||||
|
|
||||||
|
import com.sczx.pay.entity.PaymentNotifyRecord;
|
||||||
|
import org.apache.ibatis.annotations.*;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface PaymentNotifyRecordMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插入支付通知记录
|
||||||
|
*/
|
||||||
|
@Insert("INSERT INTO payment_notify_record (company_id, out_trade_no, out_refund_no, notify_data, notify_type, " +
|
||||||
|
"trade_status, refund_status, pay_channel, process_status, process_result) " +
|
||||||
|
"VALUES (#{companyId}, #{outTradeNo}, #{outRefundNo}, #{notifyData}, #{notifyType}, " +
|
||||||
|
"#{tradeStatus}, #{refundStatus}, #{payChannel}, #{processStatus}, #{processResult})")
|
||||||
|
@Options(useGeneratedKeys = true, keyProperty = "id")
|
||||||
|
int insertPaymentNotifyRecord(PaymentNotifyRecord paymentNotifyRecord);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新支付通知记录处理状态
|
||||||
|
*/
|
||||||
|
@Update("UPDATE payment_notify_record SET process_status = #{processStatus}, process_result = #{processResult}, " +
|
||||||
|
"update_time = #{updateTime} WHERE id = #{id}")
|
||||||
|
int updateProcessStatus(@Param("id") Long id,
|
||||||
|
@Param("processStatus") Integer processStatus,
|
||||||
|
@Param("processResult") String processResult,
|
||||||
|
@Param("updateTime") java.util.Date updateTime);
|
||||||
|
}
|
||||||
@ -4,10 +4,9 @@ import com.sczx.pay.config.DynamicWXPayConfig;
|
|||||||
import com.sczx.pay.dto.PaymentRequest;
|
import com.sczx.pay.dto.PaymentRequest;
|
||||||
import com.sczx.pay.dto.PaymentResponse;
|
import com.sczx.pay.dto.PaymentResponse;
|
||||||
import com.sczx.pay.dto.RefundRequest;
|
import com.sczx.pay.dto.RefundRequest;
|
||||||
import com.sczx.pay.entity.CompanyWechatConfig;
|
import com.sczx.pay.entity.*;
|
||||||
import com.sczx.pay.entity.PaymentRecord;
|
|
||||||
import com.sczx.pay.entity.RefundRecord;
|
|
||||||
import com.sczx.pay.mapper.CompanyWechatConfigMapper;
|
import com.sczx.pay.mapper.CompanyWechatConfigMapper;
|
||||||
|
import com.sczx.pay.mapper.OrderPayMapper;
|
||||||
import com.sczx.pay.mapper.PaymentRecordMapper;
|
import com.sczx.pay.mapper.PaymentRecordMapper;
|
||||||
import com.sczx.pay.mapper.RefundRecordMapper;
|
import com.sczx.pay.mapper.RefundRecordMapper;
|
||||||
import com.sczx.pay.sdk.WXPay;
|
import com.sczx.pay.sdk.WXPay;
|
||||||
@ -20,6 +19,7 @@ import org.springframework.beans.factory.annotation.Value;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -33,6 +33,9 @@ public class WechatPayService {
|
|||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(WechatPayService.class);
|
private static final Logger logger = LoggerFactory.getLogger(WechatPayService.class);
|
||||||
|
|
||||||
|
// 全局公网IP变量
|
||||||
|
private static String SERVER_PUBLIC_IP = "127.0.0.1";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private CompanyWechatConfigMapper companyWechatConfigMapper;
|
private CompanyWechatConfigMapper companyWechatConfigMapper;
|
||||||
|
|
||||||
@ -42,12 +45,39 @@ public class WechatPayService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private RefundRecordMapper refundRecordMapper;
|
private RefundRecordMapper refundRecordMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrderPayMapper orderPayMapper;
|
||||||
|
|
||||||
@Value("${wechat.pay.app-id}")
|
@Value("${wechat.pay.app-id}")
|
||||||
private String appId;
|
private String appId;
|
||||||
|
|
||||||
@Value("${wechat.pay.notify-url}")
|
@Value("${wechat.pay.notify-url}")
|
||||||
private String notifyUrl;
|
private String notifyUrl;
|
||||||
|
|
||||||
|
@Value("${wechat.pay.refund-notify-url}")
|
||||||
|
private String refundNotifyUrl;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目初始化时获取服务器公网IP
|
||||||
|
*/
|
||||||
|
@PostConstruct
|
||||||
|
public void initServerPublicIP() {
|
||||||
|
try {
|
||||||
|
SERVER_PUBLIC_IP = IPUtils.getServerPublicIP();
|
||||||
|
logger.info("服务器公网IP初始化完成: {}", SERVER_PUBLIC_IP);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("初始化服务器公网IP失败,使用默认IP: {}", SERVER_PUBLIC_IP, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务器公网IP(静态方法)
|
||||||
|
*/
|
||||||
|
public static String getServerPublicIP() {
|
||||||
|
return SERVER_PUBLIC_IP;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 小程序统一下单
|
* 小程序统一下单
|
||||||
*/
|
*/
|
||||||
@ -80,13 +110,12 @@ public class WechatPayService {
|
|||||||
reqData.put("out_trade_no", request.getOutTradeNo());
|
reqData.put("out_trade_no", request.getOutTradeNo());
|
||||||
reqData.put("total_fee", String.valueOf(request.getTotalFee()));
|
reqData.put("total_fee", String.valueOf(request.getTotalFee()));
|
||||||
// 自动获取服务器公网IP
|
// 自动获取服务器公网IP
|
||||||
reqData.put("spbill_create_ip", IPUtils.getServerPublicIP());
|
reqData.put("spbill_create_ip", getServerPublicIP());
|
||||||
reqData.put("notify_url", notifyUrl);
|
reqData.put("notify_url", notifyUrl);
|
||||||
reqData.put("trade_type", "JSAPI");
|
reqData.put("trade_type", "JSAPI");
|
||||||
reqData.put("openid", request.getOpenId());
|
reqData.put("openid", request.getOpenId());
|
||||||
reqData.put("attach", request.getCompanyId().toString());
|
reqData.put("attach", request.getCompanyId().toString());
|
||||||
|
|
||||||
|
|
||||||
// 调用微信统一下单接口
|
// 调用微信统一下单接口
|
||||||
Map<String, String> result = wxPay.unifiedOrder(reqData);
|
Map<String, String> result = wxPay.unifiedOrder(reqData);
|
||||||
|
|
||||||
@ -233,6 +262,8 @@ public class WechatPayService {
|
|||||||
reqData.put("out_refund_no", request.getOutRefundNo());
|
reqData.put("out_refund_no", request.getOutRefundNo());
|
||||||
reqData.put("total_fee", String.valueOf(request.getTotalFee()));
|
reqData.put("total_fee", String.valueOf(request.getTotalFee()));
|
||||||
reqData.put("refund_fee", String.valueOf(request.getRefundFee()));
|
reqData.put("refund_fee", String.valueOf(request.getRefundFee()));
|
||||||
|
reqData.put("notify_url", refundNotifyUrl);
|
||||||
|
|
||||||
|
|
||||||
if (request.getRefundDesc() != null) {
|
if (request.getRefundDesc() != null) {
|
||||||
reqData.put("refund_desc", request.getRefundDesc());
|
reqData.put("refund_desc", request.getRefundDesc());
|
||||||
@ -365,7 +396,16 @@ public class WechatPayService {
|
|||||||
|
|
||||||
if (updated > 0) {
|
if (updated > 0) {
|
||||||
logger.info("微信支付记录状态已更新,订单号: {}, 微信交易号: {}", outTradeNo, transactionId);
|
logger.info("微信支付记录状态已更新,订单号: {}, 微信交易号: {}", outTradeNo, transactionId);
|
||||||
// TODO: 在这里调用其他业务服务更新实际订单状态
|
//更新主订单状态
|
||||||
|
OrderMain orderMain = orderPayMapper.getOrderStatusByOrderNo(outTradeNo);
|
||||||
|
String OrderStatus = orderMain.getOrderStatus();
|
||||||
|
|
||||||
|
if(OrderStatus.equals("WAIT_PAY")){
|
||||||
|
orderPayMapper.updateOrderStatus(outTradeNo,"WAIT_PICK");
|
||||||
|
}else if (OrderStatus.equals("RERENT_WAIT_PAY")){
|
||||||
|
orderPayMapper.updateOrderStatus(outTradeNo,"RENT_ING");
|
||||||
|
}
|
||||||
|
orderPayMapper.updateSubOrderPaymentStatus(outTradeNo,"WX_PAY",PayStatus.SUCCESS.getCode(),transactionId);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
logger.warn("未找到对应的微信支付记录,订单号: {}", outTradeNo);
|
logger.warn("未找到对应的微信支付记录,订单号: {}", outTradeNo);
|
||||||
@ -428,4 +468,58 @@ public class WechatPayService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理退款通知并更新退款状态
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public boolean processRefundNotify(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 outTradeNo = notifyData.get("out_trade_no");
|
||||||
|
|
||||||
|
// 根据退款状态更新退款记录
|
||||||
|
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);
|
||||||
|
orderPayMapper.updateRefundOrderStatus(outTradeNo,"REFUND_SUCCESS",outRefundNo);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.warn("未找到对应的微信退款记录,退款单号: {}", outRefundNo);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("处理微信退款通知异常,退款单号: {}", notifyData.get("out_refund_no"), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
|
|
||||||
server:
|
server:
|
||||||
port: 8019
|
port: 8019
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: sczx-singlepay # 微服务名称
|
name: sczx_singlepay # 微服务名称
|
||||||
http:
|
http:
|
||||||
encoding:
|
encoding:
|
||||||
charset: UTF-8
|
charset: UTF-8
|
||||||
@ -18,7 +19,7 @@ spring:
|
|||||||
cloud:
|
cloud:
|
||||||
nacos:
|
nacos:
|
||||||
discovery:
|
discovery:
|
||||||
server-addr: 115.190.8.52:8848 # Nacos 地址
|
server-addr: 127.0.0.1:8848 # Nacos 地址
|
||||||
group: DEFAULT_GROUP
|
group: DEFAULT_GROUP
|
||||||
metadata:
|
metadata:
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
@ -60,3 +61,4 @@ wechat:
|
|||||||
mch-id: your_mch_id
|
mch-id: your_mch_id
|
||||||
key: your_api_key
|
key: your_api_key
|
||||||
notify-url: http://115.190.8.52:8019/api/payment/notify
|
notify-url: http://115.190.8.52:8019/api/payment/notify
|
||||||
|
refund-notify-url: http://115.190.8.52:8019/api/payment/refundNotify
|
||||||
|
|||||||
Reference in New Issue
Block a user