qnc-server-tob/app/main/api/internal/logic/pay/wechatpayrefundcallbacklogic.go
2025-06-19 02:58:09 +08:00

251 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package pay
import (
"context"
"database/sql"
"net/http"
"qnc-server/app/main/api/internal/svc"
"qnc-server/app/main/model"
"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 {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewWechatPayRefundCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WechatPayRefundCallbackLogic {
return &WechatPayRefundCallbackLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
// handleQueryOrderRefund 处理查询订单退款
func (l *WechatPayRefundCallbackLogic) handleQueryOrderRefund(orderNo string, status refunddomestic.Status) error {
logx.Infof("【退款回调】开始处理查询订单退款orderNo: %s, status: %v", orderNo, status)
order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo)
if err != nil {
logx.Errorf("【退款回调】查找查询订单信息失败: orderNo=%s, err=%v", orderNo, err)
return errors.Wrapf(err, "查找查询订单信息失败: %s", orderNo)
}
logx.Infof("【退款回调】查找到订单信息: orderId=%d, currentStatus=%s", order.Id, order.Status)
// 只处理成功和失败状态
var orderStatus, refundStatus string
switch status {
case refunddomestic.STATUS_SUCCESS:
orderStatus = model.OrderStatusRefunded
refundStatus = model.OrderRefundStatusSuccess
logx.Infof("【退款回调】退款成功,将更新订单状态为: %s, 退款记录状态为: %s", orderStatus, refundStatus)
case refunddomestic.STATUS_CLOSED:
// 退款关闭,保持订单原状态,更新退款记录为失败
refundStatus = model.OrderRefundStatusFailed
logx.Infof("【退款回调】退款关闭,退款记录状态将更新为: %s", refundStatus)
case refunddomestic.STATUS_ABNORMAL:
// 退款异常,保持订单原状态,更新退款记录为失败
refundStatus = model.OrderRefundStatusFailed
logx.Infof("【退款回调】退款异常,退款记录状态将更新为: %s", refundStatus)
default:
// 其他状态暂不处理
logx.Infof("【退款回调】状态 %v 暂不处理,直接返回", status)
return nil
}
logx.Infof("【退款回调】开始执行数据库事务更新")
// 使用事务同时更新订单和退款记录
err = l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
logx.Infof("【退款回调】进入事务处理")
// 更新订单状态(仅在退款成功时更新)
if status == refunddomestic.STATUS_SUCCESS {
logx.Infof("【退款回调】准备更新订单状态orderId: %d", order.Id)
order.Status = orderStatus
order.RefundTime = sql.NullTime{
Time: time.Now(),
Valid: true,
}
if err := l.svcCtx.OrderModel.UpdateWithVersion(ctx, session, order); err != nil {
logx.Errorf("【退款回调】更新订单状态失败: orderId=%d, err=%v", order.Id, err)
return errors.Wrapf(err, "更新查询订单状态失败: %s", orderNo)
}
logx.Infof("【退款回调】订单状态更新成功orderId: %d", order.Id)
}
// 更新退款记录状态
logx.Infof("【退款回调】准备查找退款记录orderId: %d", order.Id)
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 // 没有退款记录时不报错,只记录警告
}
logx.Errorf("【退款回调】查找退款记录失败: orderNo=%s, orderId=%d, err=%v", orderNo, order.Id, err)
return errors.Wrapf(err, "查找退款记录失败: orderNo=%s", orderNo)
}
logx.Infof("【退款回调】找到退款记录: refundId=%d, currentStatus=%s", refund.Id, refund.Status)
refund.Status = refundStatus
if status == refunddomestic.STATUS_SUCCESS {
refund.RefundTime = sql.NullTime{
Time: time.Now(),
Valid: true,
}
logx.Infof("【退款回调】设置退款成功时间")
} else if status == refunddomestic.STATUS_CLOSED {
refund.CloseTime = sql.NullTime{
Time: time.Now(),
Valid: true,
}
logx.Infof("【退款回调】设置退款关闭时间")
}
logx.Infof("【退款回调】准备更新退款记录状态refundId: %d", refund.Id)
if _, err := l.svcCtx.OrderRefundModel.Update(ctx, session, refund); err != nil {
logx.Errorf("【退款回调】更新退款记录状态失败: refundId=%d, err=%v", refund.Id, err)
return errors.Wrapf(err, "更新退款记录状态失败: orderNo=%s", orderNo)
}
logx.Infof("【退款回调】退款记录状态更新成功refundId: %d", refund.Id)
logx.Infof("【退款回调】事务处理完成")
return nil
})
if err != nil {
logx.Errorf("【退款回调】事务执行失败: orderNo=%s, err=%v", orderNo, err)
return errors.Wrapf(err, "更新订单和退款记录失败: %s", orderNo)
}
logx.Infof("【退款回调】查询订单退款处理完成orderNo: %s", orderNo)
return nil
}
// handleAgentOrderRefund 处理代理会员订单退款
func (l *WechatPayRefundCallbackLogic) handleAgentOrderRefund(orderNo string, status refunddomestic.Status) error {
logx.Infof("【退款回调】开始处理代理会员订单退款orderNo: %s, status: %v", orderNo, status)
order, err := l.svcCtx.AgentMembershipRechargeOrderModel.FindOneByOrderNo(l.ctx, orderNo)
if err != nil {
logx.Errorf("【退款回调】查找代理会员订单信息失败: orderNo=%s, err=%v", orderNo, err)
return errors.Wrapf(err, "查找代理会员订单信息失败: %s", orderNo)
}
logx.Infof("【退款回调】找到代理会员订单: orderId=%d, currentStatus=%s", order.Id, order.Status)
if status == refunddomestic.STATUS_SUCCESS {
logx.Infof("【退款回调】代理会员订单退款成功,将更新状态为: refunded")
order.Status = "refunded"
} else if status == refunddomestic.STATUS_ABNORMAL {
logx.Infof("【退款回调】代理会员订单退款异常状态,直接返回")
return nil // 异常状态直接返回
} else {
logx.Infof("【退款回调】代理会员订单其他状态 %v直接返回", status)
return nil // 其他状态直接返回
}
logx.Infof("【退款回调】准备更新代理会员订单状态orderId: %d", order.Id)
if err := l.svcCtx.AgentMembershipRechargeOrderModel.UpdateWithVersion(l.ctx, nil, order); err != nil {
logx.Errorf("【退款回调】更新代理会员订单状态失败: orderId=%d, err=%v", order.Id, err)
return errors.Wrapf(err, "更新代理会员订单状态失败: %s", orderNo)
}
logx.Infof("【退款回调】代理会员订单退款处理完成orderNo: %s", orderNo)
return nil
}
// sendSuccessResponse 发送成功响应
func (l *WechatPayRefundCallbackLogic) sendSuccessResponse(w http.ResponseWriter) {
logx.Infof("【退款回调】发送成功响应给微信")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("success"))
}
func (l *WechatPayRefundCallbackLogic) WechatPayRefundCallback(w http.ResponseWriter, r *http.Request) error {
logx.Infof("【退款回调】收到微信退款回调请求")
// 1. 处理微信退款通知
logx.Infof("【退款回调】开始解析微信退款通知")
notification, err := l.svcCtx.WechatPayService.HandleRefundNotification(l.ctx, r)
if err != nil {
logx.Errorf("【退款回调】微信退款回调处理失败: %v", err)
l.sendSuccessResponse(w)
return nil
}
logx.Infof("【退款回调】微信退款通知解析成功")
logx.Infof("【退款回调】notification详细信息: %+v", notification)
// 2. 检查关键字段是否为空
if notification.OutTradeNo == nil {
logx.Errorf("【退款回调】OutTradeNo字段为空")
l.sendSuccessResponse(w)
return nil
}
orderNo := *notification.OutTradeNo
logx.Infof("【退款回调】提取到订单号: %s", orderNo)
// 3. 判断退款状态优先使用Status如果Status为nil则使用SuccessTime判断
var status refunddomestic.Status
var statusDetermined bool = false
if notification.Status != nil {
status = *notification.Status
statusDetermined = true
logx.Infof("【退款回调】从Status字段获取状态: %v", status)
} else if notification.SuccessTime != nil && !notification.SuccessTime.IsZero() {
// 如果Status为空但SuccessTime有值说明退款成功
status = refunddomestic.STATUS_SUCCESS
statusDetermined = true
logx.Infof("【退款回调】Status为空但SuccessTime有值(%v),判断为退款成功", notification.SuccessTime)
} else {
logx.Errorf("【退款回调】Status和SuccessTime都为空无法确定退款状态")
l.sendSuccessResponse(w)
return nil
}
if !statusDetermined {
logx.Errorf("【退款回调】无法确定退款状态")
l.sendSuccessResponse(w)
return nil
}
logx.Infof("【退款回调】最终确定状态: %v", status)
var processErr error
// 4. 根据订单号前缀处理不同类型的订单
logx.Infof("【退款回调】开始根据订单号前缀分发处理")
switch {
case strings.HasPrefix(orderNo, "Q_"):
logx.Infof("【退款回调】识别为查询订单orderNo: %s", orderNo)
processErr = l.handleQueryOrderRefund(orderNo, status)
case strings.HasPrefix(orderNo, "A_"):
logx.Infof("【退款回调】识别为代理会员订单orderNo: %s", orderNo)
processErr = l.handleAgentOrderRefund(orderNo, status)
default:
// 兼容旧订单,假设没有前缀的是查询订单
logx.Infof("【退款回调】无前缀订单按查询订单处理orderNo: %s", orderNo)
processErr = l.handleQueryOrderRefund(orderNo, status)
}
// 5. 处理错误并响应
if processErr != nil {
logx.Errorf("【退款回调】处理退款订单失败: orderNo=%s, err=%v", orderNo, processErr)
} else {
logx.Infof("【退款回调】处理退款订单成功: orderNo=%s", orderNo)
}
// 无论处理是否成功,都返回成功响应给微信
l.sendSuccessResponse(w)
logx.Infof("【退款回调】退款回调处理完成")
return nil
}