From 36a614d62b8336d0a5a8d0c877cabfc4069aa906 Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Sat, 18 Oct 2025 13:21:50 +0800 Subject: [PATCH] fix --- .../logic/pay/wechatpayrefundcallbacklogic.go | 192 ++++++++++++++++-- 1 file changed, 176 insertions(+), 16 deletions(-) diff --git a/app/main/api/internal/logic/pay/wechatpayrefundcallbacklogic.go b/app/main/api/internal/logic/pay/wechatpayrefundcallbacklogic.go index bfd0c01..df1876b 100644 --- a/app/main/api/internal/logic/pay/wechatpayrefundcallbacklogic.go +++ b/app/main/api/internal/logic/pay/wechatpayrefundcallbacklogic.go @@ -2,11 +2,17 @@ package pay import ( "context" + "database/sql" "hm-server/app/main/api/internal/svc" + "hm-server/app/main/model" "net/http" + "strings" + "time" + "github.com/pkg/errors" "github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic" "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/core/stores/sqlx" ) type WechatPayRefundCallbackLogic struct { @@ -23,33 +29,187 @@ func NewWechatPayRefundCallbackLogic(ctx context.Context, svcCtx *svc.ServiceCon } } -func (l *WechatPayRefundCallbackLogic) WechatPayRefundCallback(w http.ResponseWriter, r *http.Request) error { - notification, err := l.svcCtx.WechatPayService.HandleRefundNotification(l.ctx, r) +// handleQueryOrderRefund 处理查询订单退款 +func (l *WechatPayRefundCallbackLogic) handleQueryOrderRefund(orderNo string, status refunddomestic.Status) error { + order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo) if err != nil { - logx.Errorf("微信退款回调,%v", err) - return nil + return errors.Wrapf(err, "查找查询订单信息失败: %s", orderNo) } - order, findOrderErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, *notification.OutTradeNo) - if findOrderErr != nil { - logx.Errorf("微信退款回调,查找订单信息失败: %+v", findOrderErr) + + // 检查订单是否已经处理过退款 + if order.Status == model.OrderStatusRefunded { + logx.Infof("订单已经是退款状态,无需重复处理: orderNo=%s", orderNo) return nil } - switch *notification.Status { + // 只处理成功和失败状态 + var orderStatus, refundStatus string + switch status { case refunddomestic.STATUS_SUCCESS: - order.Status = "refunded" + orderStatus = model.OrderStatusRefunded + refundStatus = model.OrderRefundStatusSuccess + case refunddomestic.STATUS_CLOSED: + // 退款关闭,保持订单原状态,更新退款记录为失败 + refundStatus = model.OrderRefundStatusFailed case refunddomestic.STATUS_ABNORMAL: - // 异常 - return nil + // 退款异常,保持订单原状态,更新退款记录为失败 + refundStatus = model.OrderRefundStatusFailed default: + // 其他状态暂不处理 return nil } - if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); updateErr != nil { - logx.Errorf("微信退款回调,更新订单失败%+v", updateErr) + + // 使用事务同时更新订单和退款记录 + err = l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error { + // 更新订单状态(仅在退款成功时更新) + if status == refunddomestic.STATUS_SUCCESS { + order.Status = orderStatus + order.RefundTime = sql.NullTime{ + Time: time.Now(), + Valid: true, + } + if err := l.svcCtx.OrderModel.UpdateWithVersion(ctx, session, order); err != nil { + return errors.Wrapf(err, "更新查询订单状态失败: %s", orderNo) + } + } + + // 更新退款记录状态 + refund, err := l.svcCtx.OrderRefundModel.FindOneByOrderId(ctx, order.Id) + if err != nil { + if err == model.ErrNotFound { + logx.Errorf("未找到订单对应的退款记录: orderNo=%s, orderId=%d", orderNo, order.Id) + return nil // 没有退款记录时不报错,只记录警告 + } + return errors.Wrapf(err, "查找退款记录失败: orderNo=%s", orderNo) + } + + // 检查退款记录是否已经处理过 + if refund.Status == model.OrderRefundStatusSuccess { + logx.Infof("退款记录已经是成功状态,无需重复处理: orderNo=%s, refundId=%d", orderNo, refund.Id) + return nil + } + + refund.Status = refundStatus + if status == refunddomestic.STATUS_SUCCESS { + refund.RefundTime = sql.NullTime{ + Time: time.Now(), + Valid: true, + } + } else if status == refunddomestic.STATUS_CLOSED { + refund.CloseTime = sql.NullTime{ + Time: time.Now(), + Valid: true, + } + } + + if _, err := l.svcCtx.OrderRefundModel.Update(ctx, session, refund); err != nil { + return errors.Wrapf(err, "更新退款记录状态失败: orderNo=%s", orderNo) + } + return nil + }) + + if err != nil { + return errors.Wrapf(err, "更新订单和退款记录失败: %s", orderNo) } - // 响应微信回调成功 - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("success")) // 确保只写入一次响应 + + return nil +} + +// handleAgentOrderRefund 处理代理会员订单退款 +func (l *WechatPayRefundCallbackLogic) handleAgentOrderRefund(orderNo string, status refunddomestic.Status) error { + order, err := l.svcCtx.AgentMembershipRechargeOrderModel.FindOneByOrderNo(l.ctx, orderNo) + if err != nil { + return errors.Wrapf(err, "查找代理会员订单信息失败: %s", orderNo) + } + + // 检查订单是否已经处理过退款 + if order.Status == "refunded" { + logx.Infof("代理会员订单已经是退款状态,无需重复处理: orderNo=%s", orderNo) + return nil + } + + if status == refunddomestic.STATUS_SUCCESS { + order.Status = "refunded" + } else if status == refunddomestic.STATUS_ABNORMAL { + return nil // 异常状态直接返回 + } else { + return nil // 其他状态直接返回 + } + + if err := l.svcCtx.AgentMembershipRechargeOrderModel.UpdateWithVersion(l.ctx, nil, order); err != nil { + return errors.Wrapf(err, "更新代理会员订单状态失败: %s", orderNo) + } + + return nil +} + +// sendSuccessResponse 发送成功响应 +func (l *WechatPayRefundCallbackLogic) sendSuccessResponse(w http.ResponseWriter) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("success")) +} + +func (l *WechatPayRefundCallbackLogic) WechatPayRefundCallback(w http.ResponseWriter, r *http.Request) error { + // 1. 处理微信退款通知 + notification, err := l.svcCtx.WechatPayService.HandleRefundNotification(l.ctx, r) + if err != nil { + logx.Errorf("微信退款回调处理失败: %v", err) + l.sendSuccessResponse(w) + return nil + } + + // 2. 检查关键字段是否为空 + if notification.OutTradeNo == nil { + logx.Errorf("微信退款回调OutTradeNo字段为空") + l.sendSuccessResponse(w) + return nil + } + + orderNo := *notification.OutTradeNo + + // 3. 判断退款状态,优先使用Status,如果Status为nil则使用SuccessTime判断 + var status refunddomestic.Status + var statusDetermined bool = false + + if notification.Status != nil { + status = *notification.Status + statusDetermined = true + } else if notification.SuccessTime != nil && !notification.SuccessTime.IsZero() { + // 如果Status为空但SuccessTime有值,说明退款成功 + status = refunddomestic.STATUS_SUCCESS + statusDetermined = true + } else { + logx.Errorf("微信退款回调Status和SuccessTime都为空,无法确定退款状态: orderNo=%s", orderNo) + l.sendSuccessResponse(w) + return nil + } + + if !statusDetermined { + logx.Errorf("微信退款回调无法确定退款状态: orderNo=%s", orderNo) + l.sendSuccessResponse(w) + return nil + } + + var processErr error + + // 4. 根据订单号前缀处理不同类型的订单 + switch { + case strings.HasPrefix(orderNo, "Q_"): + processErr = l.handleQueryOrderRefund(orderNo, status) + case strings.HasPrefix(orderNo, "A_"): + processErr = l.handleAgentOrderRefund(orderNo, status) + default: + // 兼容旧订单,假设没有前缀的是查询订单 + processErr = l.handleQueryOrderRefund(orderNo, status) + } + + // 5. 处理错误并响应 + if processErr != nil { + logx.Errorf("处理退款订单失败: orderNo=%s, err=%v", orderNo, processErr) + } + + // 无论处理是否成功,都返回成功响应给微信 + l.sendSuccessResponse(w) return nil }