f
This commit is contained in:
338
app/main/api/internal/logic/pay/alipaycallbacklogic.go
Normal file
338
app/main/api/internal/logic/pay/alipaycallbacklogic.go
Normal file
@@ -0,0 +1,338 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
"qnc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/smartwalle/alipay/v3"
|
||||
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type AlipayCallbackLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAlipayCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AlipayCallbackLogic {
|
||||
return &AlipayCallbackLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AlipayCallbackLogic) AlipayCallback(w http.ResponseWriter, r *http.Request) error {
|
||||
notification, err := l.svcCtx.AlipayService.HandleAliPaymentNotification(r)
|
||||
if err != nil {
|
||||
logx.Errorf("支付宝支付回调,%v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 根据订单号前缀判断订单类型
|
||||
orderNo := notification.OutTradeNo
|
||||
if strings.HasPrefix(orderNo, "Q_") {
|
||||
// 查询订单处理
|
||||
return l.handleQueryOrderPayment(w, notification)
|
||||
} else if strings.HasPrefix(orderNo, "U_") {
|
||||
// 代理升级订单处理
|
||||
return l.handleAgentUpgradeOrderPayment(w, notification)
|
||||
} else if strings.HasPrefix(orderNo, "A_") {
|
||||
// 旧系统会员充值订单(已废弃,新系统使用升级功能)
|
||||
// return l.handleAgentVipOrderPayment(w, notification)
|
||||
// 直接返回成功,避免旧订单影响
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
} else {
|
||||
// 兼容旧订单,假设没有前缀的是查询订单
|
||||
return l.handleQueryOrderPayment(w, notification)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理查询订单支付
|
||||
func (l *AlipayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter, notification *alipay.Notification) error {
|
||||
order, findOrderErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, notification.OutTradeNo)
|
||||
if findOrderErr != nil {
|
||||
logx.Errorf("支付宝支付回调,查找订单失败: %+v", findOrderErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
if order.Status != "pending" {
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, order.UserId)
|
||||
if err != nil {
|
||||
logx.Errorf("支付宝支付回调,查找用户失败: %+v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
amount := lzUtils.ToAlipayAmount(order.Amount)
|
||||
if user.Inside != 1 {
|
||||
// 确保订单金额和状态正确,防止重复更新
|
||||
if amount != notification.TotalAmount {
|
||||
logx.Errorf("支付宝支付回调,金额不一致")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
switch notification.TradeStatus {
|
||||
case alipay.TradeStatusSuccess:
|
||||
order.Status = "paid"
|
||||
order.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
order.PlatformOrderId = lzUtils.StringToNullString(notification.TradeNo)
|
||||
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); updateErr != nil {
|
||||
logx.Errorf("支付宝支付回调,修改订单信息失败: %+v", updateErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
if order.Status == "paid" {
|
||||
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
|
||||
logx.Errorf("异步任务调度失败: %v", asyncErr)
|
||||
return asyncErr
|
||||
}
|
||||
}
|
||||
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理代理升级订单支付
|
||||
func (l *AlipayCallbackLogic) handleAgentUpgradeOrderPayment(w http.ResponseWriter, notification *alipay.Notification) error {
|
||||
orderNo := notification.OutTradeNo
|
||||
|
||||
// 1. 查找订单
|
||||
order, findOrderErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo)
|
||||
if findOrderErr != nil {
|
||||
logx.Errorf("支付宝支付回调,查找升级订单失败: %+v", findOrderErr)
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 2. 验证金额
|
||||
amount := lzUtils.ToAlipayAmount(order.Amount)
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, order.UserId)
|
||||
if err == nil && user.Inside != 1 {
|
||||
if amount != notification.TotalAmount {
|
||||
logx.Errorf("支付宝支付回调,升级订单金额不一致,订单号: %s", orderNo)
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 检查订单状态
|
||||
if order.Status != "pending" {
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 4. 查找升级记录
|
||||
upgradeRecords, findUpgradeErr := l.svcCtx.AgentUpgradeModel.FindAll(l.ctx, l.svcCtx.AgentUpgradeModel.SelectBuilder().
|
||||
Where("order_no = ?", orderNo).
|
||||
Limit(1), "")
|
||||
if findUpgradeErr != nil || len(upgradeRecords) == 0 {
|
||||
logx.Errorf("支付宝支付回调,查找升级记录失败,订单号: %s, 错误: %+v", orderNo, findUpgradeErr)
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
upgradeRecord := upgradeRecords[0]
|
||||
|
||||
// 5. 检查升级记录状态
|
||||
if upgradeRecord.Status != 1 {
|
||||
// 升级记录状态不是待支付,直接返回成功
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 6. 处理支付状态
|
||||
switch notification.TradeStatus {
|
||||
case alipay.TradeStatusSuccess:
|
||||
order.Status = "paid"
|
||||
order.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||
order.PlatformOrderId = lzUtils.StringToNullString(notification.TradeNo)
|
||||
case alipay.TradeStatusClosed:
|
||||
order.Status = "closed"
|
||||
order.CloseTime = lzUtils.TimeToNullTime(time.Now())
|
||||
default:
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 7. 更新订单状态
|
||||
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); updateErr != nil {
|
||||
logx.Errorf("支付宝支付回调,更新升级订单状态失败: %+v", updateErr)
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 8. 如果支付成功,执行升级操作
|
||||
if order.Status == "paid" {
|
||||
err := l.svcCtx.AgentWalletModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 8.1 执行升级操作
|
||||
if err := l.svcCtx.AgentService.ProcessUpgrade(transCtx, upgradeRecord.AgentId, upgradeRecord.ToLevel, upgradeRecord.UpgradeType, upgradeRecord.UpgradeFee, upgradeRecord.RebateAmount, orderNo, ""); err != nil {
|
||||
return errors.Wrapf(err, "执行升级操作失败")
|
||||
}
|
||||
|
||||
// 8.2 更新升级记录状态为已完成
|
||||
upgradeRecord.Status = 2 // 已完成(status: 1=待处理,2=已完成,3=已失败)
|
||||
upgradeRecord.Remark = lzUtils.StringToNullString("支付成功,升级完成")
|
||||
if updateErr := l.svcCtx.AgentUpgradeModel.UpdateWithVersion(transCtx, session, upgradeRecord); updateErr != nil {
|
||||
return errors.Wrapf(updateErr, "更新升级记录状态失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logx.Errorf("支付宝支付回调,处理升级订单失败,订单号: %s, 错误: %+v", orderNo, err)
|
||||
// 即使升级失败,也返回成功给支付宝,避免重复回调
|
||||
} else {
|
||||
logx.Infof("支付宝支付回调,代理升级成功,订单号: %s, 代理ID: %d, 从等级 %d 升级到等级 %d", orderNo, upgradeRecord.AgentId, upgradeRecord.FromLevel, upgradeRecord.ToLevel)
|
||||
}
|
||||
}
|
||||
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理代理会员订单支付(已废弃,新系统使用升级功能)
|
||||
/*
|
||||
func (l *AlipayCallbackLogic) handleAgentVipOrderPayment(w http.ResponseWriter, notification *alipay.Notification) error {
|
||||
agentOrder, findAgentOrderErr := l.svcCtx.AgentMembershipRechargeOrderModel.FindOneByOrderNo(l.ctx, notification.OutTradeNo)
|
||||
if findAgentOrderErr != nil {
|
||||
logx.Errorf("支付宝支付回调,查找代理会员订单失败: %+v", findAgentOrderErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
if agentOrder.Status != "pending" {
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, agentOrder.UserId)
|
||||
if err != nil {
|
||||
logx.Errorf("支付宝支付回调,查找用户失败: %+v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
amount := lzUtils.ToAlipayAmount(agentOrder.Amount)
|
||||
if user.Inside != 1 {
|
||||
// 确保订单金额和状态正确,防止重复更新
|
||||
if amount != notification.TotalAmount {
|
||||
logx.Errorf("支付宝支付回调,金额不一致")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
switch notification.TradeStatus {
|
||||
case alipay.TradeStatusSuccess:
|
||||
agentOrder.Status = "paid"
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if agentOrder.Status == "paid" {
|
||||
err = l.svcCtx.AgentModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOne(transCtx, agentOrder.AgentId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("查找代理信息失败: %+v", err)
|
||||
}
|
||||
agentOrder.PlatformOrderId = lzUtils.StringToNullString(notification.TradeNo)
|
||||
if updateErr := l.svcCtx.AgentMembershipRechargeOrderModel.UpdateWithVersion(l.ctx, nil, agentOrder); updateErr != nil {
|
||||
return fmt.Errorf("修改代理会员订单信息失败: %+v", updateErr)
|
||||
}
|
||||
|
||||
// 记录旧等级,用于判断是否为升级
|
||||
oldLevel := agentModel.LevelName
|
||||
|
||||
// 设置会员等级
|
||||
agentModel.LevelName = agentOrder.LevelName
|
||||
|
||||
// 延长会员时间
|
||||
// 检查是否是有效期内续费(不发放奖励)还是重新激活(发放奖励)
|
||||
isValidRenewal := oldLevel == agentOrder.LevelName && agentModel.MembershipExpiryTime.Valid && agentModel.MembershipExpiryTime.Time.After(time.Now())
|
||||
if isValidRenewal {
|
||||
logx.Infof("代理会员有效期内续费成功,会员ID:%d,等级:%s", agentModel.Id, agentModel.LevelName)
|
||||
} else {
|
||||
logx.Infof("代理会员新购、升级或重新激活成功,会员ID:%d,等级:%s", agentModel.Id, agentModel.LevelName)
|
||||
}
|
||||
agentModel.MembershipExpiryTime = lzUtils.RenewMembership(agentModel.MembershipExpiryTime)
|
||||
|
||||
if updateErr := l.svcCtx.AgentModel.UpdateWithVersion(l.ctx, nil, agentModel); updateErr != nil {
|
||||
return fmt.Errorf("修改代理信息失败: %+v", updateErr)
|
||||
}
|
||||
|
||||
// 如果不是有效期内续费,给上级代理发放升级奖励
|
||||
if !isValidRenewal && (agentOrder.LevelName == model.AgentLeveNameVIP || agentOrder.LevelName == model.AgentLeveNameSVIP) {
|
||||
// 验证升级路径的有效性
|
||||
if oldLevel != agentOrder.LevelName {
|
||||
upgradeRewardErr := l.svcCtx.AgentService.GiveUpgradeReward(transCtx, agentModel.Id, oldLevel, agentOrder.LevelName, session)
|
||||
if upgradeRewardErr != nil {
|
||||
logx.Errorf("发放升级奖励失败,代理ID:%d,旧等级:%s,新等级:%s,错误:%+v", agentModel.Id, oldLevel, agentOrder.LevelName, upgradeRewardErr)
|
||||
// 升级奖励失败不影响主流程,只记录日志
|
||||
} else {
|
||||
logx.Infof("发放升级奖励成功,代理ID:%d,旧等级:%s,新等级:%s", agentModel.Id, oldLevel, agentOrder.LevelName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
logx.Errorf("支付宝支付回调,处理代理会员订单失败: %+v", err)
|
||||
refundErr := l.handleRefund(agentOrder)
|
||||
if refundErr != nil {
|
||||
logx.Errorf("支付宝支付回调,退款失败: %+v", refundErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
} */
|
||||
|
||||
/* func (l *AlipayCallbackLogic) handleRefund(order *model.AgentMembershipRechargeOrder) error {
|
||||
ctx := context.Background()
|
||||
// 退款
|
||||
if order.PaymentMethod == "wechat" {
|
||||
refundErr := l.svcCtx.WechatPayService.WeChatRefund(ctx, order.OrderNo, order.Amount, order.Amount)
|
||||
if refundErr != nil {
|
||||
return refundErr
|
||||
}
|
||||
} else {
|
||||
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount)
|
||||
if refundErr != nil {
|
||||
return refundErr
|
||||
}
|
||||
if refund.IsSuccess() {
|
||||
logx.Errorf("支付宝退款成功, orderID: %d", order.Id)
|
||||
// 更新订单状态为退款
|
||||
order.Status = "refunded"
|
||||
updateOrderErr := l.svcCtx.AgentMembershipRechargeOrderModel.UpdateWithVersion(ctx, nil, order)
|
||||
if updateOrderErr != nil {
|
||||
logx.Errorf("更新订单状态失败,订单ID: %d, 错误: %v", order.Id, updateOrderErr)
|
||||
return fmt.Errorf("更新订单状态失败: %v", updateOrderErr)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
logx.Errorf("支付宝退款失败:%v", refundErr)
|
||||
return refundErr
|
||||
}
|
||||
// 直接成功
|
||||
}
|
||||
return nil
|
||||
} */
|
||||
83
app/main/api/internal/logic/pay/iapcallbacklogic.go
Normal file
83
app/main/api/internal/logic/pay/iapcallbacklogic.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/common/xerr"
|
||||
"qnc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type IapCallbackLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewIapCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *IapCallbackLogic {
|
||||
return &IapCallbackLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *IapCallbackLogic) IapCallback(req *types.IapCallbackReq) error {
|
||||
// Step 1: 查找订单
|
||||
order, findOrderErr := l.svcCtx.OrderModel.FindOne(l.ctx, fmt.Sprintf("%d", req.OrderID))
|
||||
if findOrderErr != nil {
|
||||
logx.Errorf("苹果内购支付回调,查找订单失败: %+v", findOrderErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Step 2: 验证订单状态
|
||||
if order.Status != "pending" {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "苹果内购支付回调, 订单状态异常: %+v", order)
|
||||
}
|
||||
|
||||
// Step 3: 调用 VerifyReceipt 验证苹果支付凭证
|
||||
//receipt := req.TransactionReceipt // 从请求中获取支付凭证
|
||||
//verifyResponse, verifyErr := l.svcCtx.ApplePayService.VerifyReceipt(l.ctx, receipt)
|
||||
//if verifyErr != nil {
|
||||
// return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "苹果内购支付回调, 验证订单异常: %+v", verifyErr)
|
||||
//}
|
||||
|
||||
// Step 4: 验证订单
|
||||
//product, findProductErr := l.svcCtx.ProductModel.FindOne(l.ctx, order.Id)
|
||||
//if findProductErr != nil {
|
||||
// return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "苹果内购支付回调, 获取订单相关商品失败: %+v", findProductErr)
|
||||
//}
|
||||
//isProductMatched := false
|
||||
//appleProductID := l.svcCtx.ApplePayService.GetIappayAppID(product.ProductEn)
|
||||
//for _, item := range verifyResponse.Receipt.InApp {
|
||||
// if item.ProductID == appleProductID {
|
||||
// isProductMatched = true
|
||||
// order.PlatformOrderId = lzUtils.StringToNullString(item.TransactionID) // 记录交易 ID
|
||||
// break
|
||||
// }
|
||||
//}
|
||||
//if !isProductMatched {
|
||||
// return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "苹果内购支付回调, 商品 ID 不匹配,订单 ID: %d, 回调苹果商品 ID: %s", order.Id, verifyResponse.Receipt.InApp[0].ProductID)
|
||||
//}
|
||||
|
||||
// Step 5: 更新订单状态 mm
|
||||
order.Status = "paid"
|
||||
order.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||
|
||||
// 更新订单到数据库
|
||||
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); updateErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "苹果内购支付回调, 修改订单信息失败: %+v", updateErr)
|
||||
}
|
||||
|
||||
// Step 6: 处理订单完成后的逻辑
|
||||
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "苹果内购支付回调,异步任务调度失败: %v", asyncErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
51
app/main/api/internal/logic/pay/paymentchecklogic.go
Normal file
51
app/main/api/internal/logic/pay/paymentchecklogic.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type PaymentCheckLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewPaymentCheckLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PaymentCheckLogic {
|
||||
return &PaymentCheckLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *PaymentCheckLogic) PaymentCheck(req *types.PaymentCheckReq) (resp *types.PaymentCheckResp, err error) {
|
||||
// 根据订单号前缀判断订单类型
|
||||
if strings.HasPrefix(req.OrderNo, "U_") {
|
||||
// 升级订单
|
||||
order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询升级订单失败: %v", err)
|
||||
}
|
||||
return &types.PaymentCheckResp{
|
||||
Type: "agent_upgrade",
|
||||
Status: order.Status,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 查询订单(包括代理订单)
|
||||
order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询订单失败: %v", err)
|
||||
}
|
||||
return &types.PaymentCheckResp{
|
||||
Type: "query",
|
||||
Status: order.Status,
|
||||
}, nil
|
||||
}
|
||||
475
app/main/api/internal/logic/pay/paymentlogic.go
Normal file
475
app/main/api/internal/logic/pay/paymentlogic.go
Normal file
@@ -0,0 +1,475 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/app/main/model"
|
||||
"qnc-server/common/ctxdata"
|
||||
"qnc-server/common/xerr"
|
||||
"qnc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type PaymentLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
type PaymentTypeResp struct {
|
||||
amount float64
|
||||
outTradeNo string
|
||||
description string
|
||||
orderID string // 订单ID,用于开发环境测试支付模式
|
||||
}
|
||||
|
||||
func NewPaymentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PaymentLogic {
|
||||
return &PaymentLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp, err error) {
|
||||
var paymentTypeResp *PaymentTypeResp
|
||||
var prepayData interface{}
|
||||
var orderID string
|
||||
|
||||
// 检查是否为开发环境的测试支付模式
|
||||
env := os.Getenv("ENV")
|
||||
isDevTestPayment := env == "development" && (req.PayMethod == "test" || req.PayMethod == "test_empty")
|
||||
isEmptyReportMode := env == "development" && req.PayMethod == "test_empty"
|
||||
|
||||
l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
switch req.PayType {
|
||||
case "agent_vip":
|
||||
paymentTypeResp, err = l.AgentVipOrderPayment(req, session)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case "query":
|
||||
paymentTypeResp, err = l.QueryOrderPayment(req, session)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case "agent_upgrade":
|
||||
paymentTypeResp, err = l.AgentUpgradeOrderPayment(req, session)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 开发环境测试支付模式:跳过实际支付流程
|
||||
// 注意:订单状态更新在事务外进行,避免在事务中查询不到订单的问题
|
||||
if isDevTestPayment {
|
||||
// 获取订单ID(从 QueryOrderPayment 返回的 orderID)
|
||||
if paymentTypeResp.orderID == "" {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "开发测试模式,订单ID无效")
|
||||
}
|
||||
orderID = paymentTypeResp.orderID
|
||||
|
||||
// 在事务中只记录订单ID,不更新订单状态
|
||||
// 订单状态的更新和后续流程在事务提交后处理
|
||||
logx.Infof("开发环境测试支付模式:订单 %s (ID: %s) 将在事务提交后更新状态", paymentTypeResp.outTradeNo, orderID)
|
||||
|
||||
// 返回测试支付标识
|
||||
prepayData = "test_payment_success"
|
||||
return nil
|
||||
}
|
||||
|
||||
// 正常支付流程
|
||||
var createOrderErr error
|
||||
if req.PayMethod == "wechat" {
|
||||
prepayData, createOrderErr = l.svcCtx.WechatPayService.CreateWechatOrder(l.ctx, paymentTypeResp.amount, paymentTypeResp.description, paymentTypeResp.outTradeNo)
|
||||
} else if req.PayMethod == "alipay" {
|
||||
prepayData, createOrderErr = l.svcCtx.AlipayService.CreateAlipayOrder(l.ctx, paymentTypeResp.amount, paymentTypeResp.description, paymentTypeResp.outTradeNo)
|
||||
} else if req.PayMethod == "appleiap" {
|
||||
prepayData = l.svcCtx.ApplePayService.GetIappayAppID(paymentTypeResp.outTradeNo)
|
||||
}
|
||||
if createOrderErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 创建支付订单失败: %+v", createOrderErr)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 开发环境测试支付模式:事务提交后处理订单状态更新和后续流程
|
||||
if isDevTestPayment && paymentTypeResp != nil && paymentTypeResp.orderID != "" {
|
||||
// 使用 goroutine 异步处理,确保事务已完全提交
|
||||
go func() {
|
||||
// 短暂延迟,确保事务已完全提交到数据库
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
finalOrderID := paymentTypeResp.orderID
|
||||
|
||||
// 查找订单并更新状态为已支付
|
||||
order, findOrderErr := l.svcCtx.OrderModel.FindOne(context.Background(), finalOrderID)
|
||||
if findOrderErr != nil {
|
||||
logx.Errorf("开发测试模式,查找订单失败,订单ID: %s, 错误: %v", finalOrderID, findOrderErr)
|
||||
return
|
||||
}
|
||||
|
||||
// 更新订单状态为已支付
|
||||
order.Status = "paid"
|
||||
now := time.Now()
|
||||
order.PayTime = sql.NullTime{Time: now, Valid: true}
|
||||
|
||||
// 空报告模式:在 PaymentPlatform 字段中标记,用于后续生成空报告
|
||||
if isEmptyReportMode {
|
||||
order.PaymentPlatform = "test_empty"
|
||||
logx.Infof("开发环境空报告模式:订单 %s (ID: %s) 已标记为空报告模式", paymentTypeResp.outTradeNo, finalOrderID)
|
||||
}
|
||||
|
||||
// 更新订单状态(在事务外执行)
|
||||
updateErr := l.svcCtx.OrderModel.UpdateWithVersion(context.Background(), nil, order)
|
||||
if updateErr != nil {
|
||||
logx.Errorf("开发测试模式,更新订单状态失败,订单ID: %s, 错误: %+v", finalOrderID, updateErr)
|
||||
return
|
||||
}
|
||||
|
||||
logx.Infof("开发环境测试支付模式:订单 %s (ID: %s) 已自动标记为已支付", paymentTypeResp.outTradeNo, finalOrderID)
|
||||
|
||||
// 再次短暂延迟,确保订单状态更新已提交
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// 根据订单类型处理后续流程
|
||||
if strings.HasPrefix(paymentTypeResp.outTradeNo, "U_") {
|
||||
// 升级订单:直接执行升级操作
|
||||
upgradeRecords, findUpgradeErr := l.svcCtx.AgentUpgradeModel.FindAll(context.Background(), l.svcCtx.AgentUpgradeModel.SelectBuilder().
|
||||
Where("order_no = ?", paymentTypeResp.outTradeNo).
|
||||
Limit(1), "")
|
||||
if findUpgradeErr != nil || len(upgradeRecords) == 0 {
|
||||
logx.Errorf("开发测试模式,查找升级记录失败,订单号: %s, 错误: %+v", paymentTypeResp.outTradeNo, findUpgradeErr)
|
||||
return
|
||||
}
|
||||
upgradeRecord := upgradeRecords[0]
|
||||
|
||||
// 执行升级操作
|
||||
err := l.svcCtx.AgentWalletModel.Trans(context.Background(), func(transCtx context.Context, session sqlx.Session) error {
|
||||
if err := l.svcCtx.AgentService.ProcessUpgrade(transCtx, upgradeRecord.AgentId, upgradeRecord.ToLevel, upgradeRecord.UpgradeType, upgradeRecord.UpgradeFee, upgradeRecord.RebateAmount, paymentTypeResp.outTradeNo, ""); err != nil {
|
||||
return errors.Wrapf(err, "执行升级操作失败")
|
||||
}
|
||||
|
||||
// 更新升级记录状态为已完成
|
||||
upgradeRecord.Status = 2 // 已完成(status: 1=待处理,2=已完成,3=已失败)
|
||||
upgradeRecord.Remark = lzUtils.StringToNullString("测试支付成功,升级完成")
|
||||
if updateErr := l.svcCtx.AgentUpgradeModel.UpdateWithVersion(transCtx, session, upgradeRecord); updateErr != nil {
|
||||
return errors.Wrapf(updateErr, "更新升级记录状态失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logx.Errorf("开发测试模式,处理升级订单失败,订单号: %s, 错误: %+v", paymentTypeResp.outTradeNo, err)
|
||||
} else {
|
||||
logx.Infof("开发测试模式,代理升级成功,订单号: %s, 代理ID: %s", paymentTypeResp.outTradeNo, upgradeRecord.AgentId)
|
||||
}
|
||||
} else {
|
||||
// 查询订单:发送支付成功通知任务,触发后续流程(生成报告和代理处理)
|
||||
if sendErr := l.svcCtx.AsynqService.SendQueryTask(finalOrderID); sendErr != nil {
|
||||
logx.Errorf("开发测试模式,发送支付成功通知任务失败,订单ID: %s, 错误: %+v", finalOrderID, sendErr)
|
||||
} else {
|
||||
logx.Infof("开发测试模式,已发送支付成功通知任务,订单ID: %s", finalOrderID)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
switch v := prepayData.(type) {
|
||||
case string:
|
||||
// 如果 prepayData 是字符串类型,直接返回
|
||||
return &types.PaymentResp{PrepayId: v, OrderNo: paymentTypeResp.outTradeNo}, nil
|
||||
default:
|
||||
return &types.PaymentResp{PrepayData: prepayData, OrderNo: paymentTypeResp.outTradeNo}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Session) (resp *PaymentTypeResp, err error) {
|
||||
userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if getUidErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取用户信息失败, %+v", getUidErr)
|
||||
}
|
||||
outTradeNo := req.Id
|
||||
redisKey := fmt.Sprintf(types.QueryCacheKey, userID, outTradeNo)
|
||||
cache, cacheErr := l.svcCtx.Redis.GetCtx(l.ctx, redisKey)
|
||||
if cacheErr != nil {
|
||||
if cacheErr == redis.Nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("订单已过期"), "生成订单, 缓存不存在, %+v", cacheErr)
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取缓存失败, %+v", cacheErr)
|
||||
}
|
||||
var data types.QueryCacheLoad
|
||||
err = json.Unmarshal([]byte(cache), &data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 解析缓存内容失败, %v", err)
|
||||
}
|
||||
|
||||
product, err := l.svcCtx.ProductModel.FindOneByProductEn(l.ctx, data.Product)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 查找产品错误: %v", err)
|
||||
}
|
||||
|
||||
var amount float64
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 获取用户信息失败: %v", err)
|
||||
}
|
||||
|
||||
var agentLinkModel *model.AgentLink
|
||||
if data.AgentIdentifier != "" {
|
||||
var findAgentLinkErr error
|
||||
agentLinkModel, findAgentLinkErr = l.svcCtx.AgentLinkModel.FindOneByLinkIdentifier(l.ctx, data.AgentIdentifier)
|
||||
if findAgentLinkErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 获取代理链接失败: %+v", findAgentLinkErr)
|
||||
}
|
||||
amount = agentLinkModel.SetPrice
|
||||
} else {
|
||||
amount = product.SellPrice
|
||||
}
|
||||
|
||||
if user.Inside == 1 {
|
||||
amount = 0.01
|
||||
}
|
||||
order := model.Order{
|
||||
Id: uuid.NewString(),
|
||||
OrderNo: outTradeNo,
|
||||
UserId: userID,
|
||||
ProductId: product.Id,
|
||||
PaymentPlatform: req.PayMethod,
|
||||
PaymentScene: "app",
|
||||
Amount: amount,
|
||||
Status: "pending",
|
||||
}
|
||||
_, insertOrderErr := l.svcCtx.OrderModel.Insert(l.ctx, session, &order)
|
||||
if insertOrderErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 保存订单失败: %+v", insertOrderErr)
|
||||
}
|
||||
orderID := order.Id
|
||||
|
||||
// 如果是代理推广订单,创建完整的代理订单记录
|
||||
if data.AgentIdentifier != "" && agentLinkModel != nil {
|
||||
// 获取代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOne(l.ctx, agentLinkModel.AgentId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 查询代理信息失败: %+v", err)
|
||||
}
|
||||
|
||||
// 获取产品配置(必须存在)
|
||||
productConfig, err := l.svcCtx.AgentProductConfigModel.FindOneByProductId(l.ctx, product.Id)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单失败,产品配置不存在, productId: %s,请先在后台配置产品价格参数", product.Id)
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 查询产品配置失败: %+v", err)
|
||||
}
|
||||
|
||||
// 获取等级加成(需要从系统配置读取)
|
||||
levelBonus, err := l.getLevelBonus(agent.Level)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 获取等级加成配置失败: %+v", err)
|
||||
}
|
||||
|
||||
// 使用产品配置的底价计算实际底价
|
||||
basePrice := productConfig.BasePrice
|
||||
actualBasePrice := basePrice + float64(levelBonus)
|
||||
|
||||
// 计算提价成本(使用产品配置)
|
||||
priceThreshold := 0.0
|
||||
priceFeeRate := 0.0
|
||||
if productConfig.PriceThreshold.Valid {
|
||||
priceThreshold = productConfig.PriceThreshold.Float64
|
||||
}
|
||||
if productConfig.PriceFeeRate.Valid {
|
||||
priceFeeRate = productConfig.PriceFeeRate.Float64
|
||||
}
|
||||
|
||||
priceCost := 0.0
|
||||
if agentLinkModel.SetPrice > priceThreshold {
|
||||
priceCost = (agentLinkModel.SetPrice - priceThreshold) * priceFeeRate
|
||||
}
|
||||
|
||||
// 计算代理收益
|
||||
agentProfit := agentLinkModel.SetPrice - actualBasePrice - priceCost
|
||||
|
||||
// 创建代理订单记录
|
||||
agentOrder := model.AgentOrder{
|
||||
Id: uuid.NewString(),
|
||||
AgentId: agentLinkModel.AgentId,
|
||||
OrderId: orderID,
|
||||
ProductId: product.Id,
|
||||
OrderAmount: amount,
|
||||
SetPrice: agentLinkModel.SetPrice,
|
||||
ActualBasePrice: actualBasePrice,
|
||||
PriceCost: priceCost,
|
||||
AgentProfit: agentProfit,
|
||||
ProcessStatus: 0, // 待处理
|
||||
}
|
||||
_, agentOrderInsert := l.svcCtx.AgentOrderModel.Insert(l.ctx, session, &agentOrder)
|
||||
if agentOrderInsert != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 保存代理订单失败: %+v", agentOrderInsert)
|
||||
}
|
||||
}
|
||||
return &PaymentTypeResp{amount: amount, outTradeNo: outTradeNo, description: product.ProductName, orderID: orderID}, nil
|
||||
}
|
||||
|
||||
// AgentVipOrderPayment 代理会员充值订单(已废弃,新系统使用升级功能替代)
|
||||
func (l *PaymentLogic) AgentVipOrderPayment(req *types.PaymentReq, session sqlx.Session) (resp *PaymentTypeResp, err error) {
|
||||
// 新代理系统已废弃会员充值功能,请使用升级功能
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("该功能已废弃,请使用代理升级功能"), "")
|
||||
}
|
||||
|
||||
// AgentUpgradeOrderPayment 代理升级订单支付
|
||||
func (l *PaymentLogic) AgentUpgradeOrderPayment(req *types.PaymentReq, session sqlx.Session) (resp *PaymentTypeResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 解析升级记录ID
|
||||
upgradeId := req.Id
|
||||
|
||||
// 2. 查找升级记录
|
||||
upgradeRecord, err := l.svcCtx.AgentUpgradeModel.FindOne(l.ctx, upgradeId)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("升级记录不存在"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询升级记录失败, %v", err)
|
||||
}
|
||||
|
||||
// 3. 验证升级记录状态(必须是待支付状态)
|
||||
if upgradeRecord.Status != 1 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("升级记录状态不正确,无法支付"), "")
|
||||
}
|
||||
|
||||
// 4. 验证代理ID是否匹配
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
if agent.Id != upgradeRecord.AgentId {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("无权支付此升级订单"), "")
|
||||
}
|
||||
|
||||
// 5. 生成订单号(升级订单前缀 U_,限制长度不超过32)
|
||||
base := l.svcCtx.AlipayService.GenerateOutTradeNo()
|
||||
outTradeNo := "U_" + base
|
||||
if len(outTradeNo) > 32 {
|
||||
outTradeNo = outTradeNo[:32]
|
||||
}
|
||||
|
||||
// 6. 获取用户信息(用于内部用户判断)
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取用户信息失败: %v", err)
|
||||
}
|
||||
|
||||
// 7. 计算支付金额
|
||||
amount := upgradeRecord.UpgradeFee
|
||||
if user.Inside == 1 {
|
||||
amount = 0.01 // 内部用户测试金额
|
||||
}
|
||||
|
||||
// 8. 创建订单记录
|
||||
order := model.Order{
|
||||
Id: uuid.NewString(),
|
||||
OrderNo: outTradeNo,
|
||||
UserId: userID,
|
||||
ProductId: "", // 升级订单没有产品ID
|
||||
PaymentPlatform: req.PayMethod,
|
||||
PaymentScene: "app",
|
||||
Amount: amount,
|
||||
Status: "pending",
|
||||
}
|
||||
orderInsertResult, insertOrderErr := l.svcCtx.OrderModel.Insert(l.ctx, session, &order)
|
||||
if insertOrderErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建订单失败: %+v", insertOrderErr)
|
||||
}
|
||||
_ = orderInsertResult
|
||||
orderID := order.Id
|
||||
|
||||
// 9. 更新升级记录的订单号
|
||||
upgradeRecord.OrderNo = lzUtils.StringToNullString(outTradeNo)
|
||||
if updateErr := l.svcCtx.AgentUpgradeModel.UpdateWithVersion(l.ctx, session, upgradeRecord); updateErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新升级记录订单号失败: %+v", updateErr)
|
||||
}
|
||||
|
||||
// 10. 生成描述信息
|
||||
levelNames := map[int64]string{
|
||||
1: "普通代理",
|
||||
2: "黄金代理",
|
||||
3: "钻石代理",
|
||||
}
|
||||
fromLevelName := levelNames[upgradeRecord.FromLevel]
|
||||
toLevelName := levelNames[upgradeRecord.ToLevel]
|
||||
description := fmt.Sprintf("代理升级:%s → %s", fromLevelName, toLevelName)
|
||||
|
||||
return &PaymentTypeResp{
|
||||
amount: amount,
|
||||
outTradeNo: outTradeNo,
|
||||
description: description,
|
||||
orderID: orderID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getLevelBonus 获取等级加成(从配置表读取)
|
||||
func (l *PaymentLogic) getLevelBonus(level int64) (int64, error) {
|
||||
var configKey string
|
||||
switch level {
|
||||
case 1: // 普通
|
||||
configKey = "level_1_bonus"
|
||||
case 2: // 黄金
|
||||
configKey = "level_2_bonus"
|
||||
case 3: // 钻石
|
||||
configKey = "level_3_bonus"
|
||||
default:
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
bonus, err := l.getConfigFloat(configKey)
|
||||
if err != nil {
|
||||
// 配置不存在时返回默认值
|
||||
l.Errorf("获取等级加成配置失败, level: %d, key: %s, err: %v,使用默认值", level, configKey, err)
|
||||
switch level {
|
||||
case 1:
|
||||
return 6, nil
|
||||
case 2:
|
||||
return 3, nil
|
||||
case 3:
|
||||
return 0, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
return int64(bonus), nil
|
||||
}
|
||||
|
||||
// getConfigFloat 获取配置值(浮点数)
|
||||
func (l *PaymentLogic) getConfigFloat(configKey string) (float64, error) {
|
||||
config, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, configKey)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取配置失败, key: %s, %v", configKey, err)
|
||||
}
|
||||
value, err := strconv.ParseFloat(config.ConfigValue, 64)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解析配置值失败, key: %s, value: %s, %v", configKey, config.ConfigValue, err)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
338
app/main/api/internal/logic/pay/wechatpaycallbacklogic.go
Normal file
338
app/main/api/internal/logic/pay/wechatpaycallbacklogic.go
Normal file
@@ -0,0 +1,338 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
"qnc-server/app/main/api/internal/service"
|
||||
"qnc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type WechatPayCallbackLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewWechatPayCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WechatPayCallbackLogic {
|
||||
return &WechatPayCallbackLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *WechatPayCallbackLogic) WechatPayCallback(w http.ResponseWriter, r *http.Request) error {
|
||||
notification, err := l.svcCtx.WechatPayService.HandleWechatPayNotification(l.ctx, r)
|
||||
if err != nil {
|
||||
logx.Errorf("微信支付回调,%v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 根据订单号前缀判断订单类型
|
||||
orderNo := *notification.OutTradeNo
|
||||
if strings.HasPrefix(orderNo, "Q_") {
|
||||
// 查询订单处理
|
||||
return l.handleQueryOrderPayment(w, notification)
|
||||
} else if strings.HasPrefix(orderNo, "U_") {
|
||||
// 代理升级订单处理
|
||||
return l.handleAgentUpgradeOrderPayment(w, notification)
|
||||
} else if strings.HasPrefix(orderNo, "A_") {
|
||||
// 旧系统会员充值订单(已废弃,新系统使用升级功能)
|
||||
// return l.handleAgentVipOrderPayment(w, notification)
|
||||
// 直接返回成功,避免旧订单影响
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
} else {
|
||||
// 兼容旧订单,假设没有前缀的是查询订单
|
||||
return l.handleQueryOrderPayment(w, notification)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理查询订单支付
|
||||
func (l *WechatPayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter, notification *payments.Transaction) error {
|
||||
order, findOrderErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, *notification.OutTradeNo)
|
||||
if findOrderErr != nil {
|
||||
logx.Errorf("微信支付回调,查找订单信息失败: %+v", findOrderErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
amount := lzUtils.ToWechatAmount(order.Amount)
|
||||
if amount != *notification.Amount.Total {
|
||||
logx.Errorf("微信支付回调,金额不一致")
|
||||
return nil
|
||||
}
|
||||
|
||||
if order.Status != "pending" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
switch *notification.TradeState {
|
||||
case service.TradeStateSuccess:
|
||||
order.Status = "paid"
|
||||
order.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||
case service.TradeStateClosed:
|
||||
order.Status = "closed"
|
||||
order.CloseTime = lzUtils.TimeToNullTime(time.Now())
|
||||
case service.TradeStateRevoked:
|
||||
order.Status = "failed"
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
order.PlatformOrderId = lzUtils.StringToNullString(*notification.TransactionId)
|
||||
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); updateErr != nil {
|
||||
logx.Errorf("微信支付回调,更新订单失败%+v", updateErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
if order.Status == "paid" {
|
||||
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
|
||||
logx.Errorf("异步任务调度失败: %v", asyncErr)
|
||||
return asyncErr
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理代理升级订单支付
|
||||
func (l *WechatPayCallbackLogic) handleAgentUpgradeOrderPayment(w http.ResponseWriter, notification *payments.Transaction) error {
|
||||
orderNo := *notification.OutTradeNo
|
||||
|
||||
// 1. 查找订单
|
||||
order, findOrderErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo)
|
||||
if findOrderErr != nil {
|
||||
logx.Errorf("微信支付回调,查找升级订单失败: %+v", findOrderErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 2. 验证金额
|
||||
amount := lzUtils.ToWechatAmount(order.Amount)
|
||||
if amount != *notification.Amount.Total {
|
||||
logx.Errorf("微信支付回调,升级订单金额不一致,订单号: %s", orderNo)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 3. 检查订单状态
|
||||
if order.Status != "pending" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 4. 查找升级记录
|
||||
upgradeRecords, findUpgradeErr := l.svcCtx.AgentUpgradeModel.FindAll(l.ctx, l.svcCtx.AgentUpgradeModel.SelectBuilder().
|
||||
Where("order_no = ?", orderNo).
|
||||
Limit(1), "")
|
||||
if findUpgradeErr != nil || len(upgradeRecords) == 0 {
|
||||
logx.Errorf("微信支付回调,查找升级记录失败,订单号: %s, 错误: %+v", orderNo, findUpgradeErr)
|
||||
return nil
|
||||
}
|
||||
upgradeRecord := upgradeRecords[0]
|
||||
|
||||
// 5. 检查升级记录状态
|
||||
if upgradeRecord.Status != 1 {
|
||||
// 升级记录状态不是待支付,直接返回成功
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 6. 处理支付状态
|
||||
switch *notification.TradeState {
|
||||
case service.TradeStateSuccess:
|
||||
order.Status = "paid"
|
||||
order.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||
order.PlatformOrderId = lzUtils.StringToNullString(*notification.TransactionId)
|
||||
case service.TradeStateClosed:
|
||||
order.Status = "closed"
|
||||
order.CloseTime = lzUtils.TimeToNullTime(time.Now())
|
||||
case service.TradeStateRevoked:
|
||||
order.Status = "failed"
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
// 7. 更新订单状态
|
||||
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); updateErr != nil {
|
||||
logx.Errorf("微信支付回调,更新升级订单状态失败: %+v", updateErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 8. 如果支付成功,执行升级操作
|
||||
if order.Status == "paid" {
|
||||
err := l.svcCtx.AgentWalletModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 8.1 执行升级操作
|
||||
if err := l.svcCtx.AgentService.ProcessUpgrade(transCtx, upgradeRecord.AgentId, upgradeRecord.ToLevel, upgradeRecord.UpgradeType, upgradeRecord.UpgradeFee, upgradeRecord.RebateAmount, orderNo, ""); err != nil {
|
||||
return errors.Wrapf(err, "执行升级操作失败")
|
||||
}
|
||||
|
||||
// 8.2 更新升级记录状态为已完成
|
||||
upgradeRecord.Status = 2 // 已完成(status: 1=待处理,2=已完成,3=已失败)
|
||||
upgradeRecord.Remark = lzUtils.StringToNullString("支付成功,升级完成")
|
||||
if updateErr := l.svcCtx.AgentUpgradeModel.UpdateWithVersion(transCtx, session, upgradeRecord); updateErr != nil {
|
||||
return errors.Wrapf(updateErr, "更新升级记录状态失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logx.Errorf("微信支付回调,处理升级订单失败,订单号: %s, 错误: %+v", orderNo, err)
|
||||
// 即使升级失败,也返回成功给微信,避免重复回调
|
||||
} else {
|
||||
logx.Infof("微信支付回调,代理升级成功,订单号: %s, 代理ID: %d, 从等级 %d 升级到等级 %d", orderNo, upgradeRecord.AgentId, upgradeRecord.FromLevel, upgradeRecord.ToLevel)
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理代理会员订单支付(已废弃,新系统使用升级功能)
|
||||
/*
|
||||
func (l *WechatPayCallbackLogic) handleAgentVipOrderPayment(w http.ResponseWriter, notification *payments.Transaction) error {
|
||||
agentOrder, findAgentOrderErr := l.svcCtx.AgentMembershipRechargeOrderModel.FindOneByOrderNo(l.ctx, *notification.OutTradeNo)
|
||||
if findAgentOrderErr != nil {
|
||||
logx.Errorf("微信支付回调,查找代理会员订单失败: %+v", findAgentOrderErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
if agentOrder.Status != "pending" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, agentOrder.UserId)
|
||||
if err != nil {
|
||||
logx.Errorf("微信支付回调,查找用户失败: %+v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
amount := lzUtils.ToWechatAmount(agentOrder.Amount)
|
||||
if user.Inside != 1 {
|
||||
if amount != *notification.Amount.Total {
|
||||
logx.Errorf("微信支付回调,金额不一致")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
switch *notification.TradeState {
|
||||
case service.TradeStateSuccess:
|
||||
agentOrder.Status = "paid"
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if agentOrder.Status == "paid" {
|
||||
err = l.svcCtx.AgentModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOne(transCtx, agentOrder.AgentId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("查找代理信息失败: %+v", err)
|
||||
}
|
||||
|
||||
agentOrder.PlatformOrderId = lzUtils.StringToNullString(*notification.TransactionId)
|
||||
if updateErr := l.svcCtx.AgentMembershipRechargeOrderModel.UpdateWithVersion(l.ctx, nil, agentOrder); updateErr != nil {
|
||||
return fmt.Errorf("修改代理会员订单信息失败: %+v", updateErr)
|
||||
}
|
||||
|
||||
// 记录旧等级,用于判断是否为升级
|
||||
oldLevel := agentModel.LevelName
|
||||
|
||||
// 设置会员等级
|
||||
agentModel.LevelName = agentOrder.LevelName
|
||||
|
||||
// 延长会员时间
|
||||
// 检查是否是有效期内续费(不发放奖励)还是重新激活(发放奖励)
|
||||
isValidRenewal := oldLevel == agentOrder.LevelName && agentModel.MembershipExpiryTime.Valid && agentModel.MembershipExpiryTime.Time.After(time.Now())
|
||||
if isValidRenewal {
|
||||
logx.Infof("代理会员有效期内续费成功,会员ID:%d,等级:%s", agentModel.Id, agentModel.LevelName)
|
||||
} else {
|
||||
logx.Infof("代理会员新购、升级或重新激活成功,会员ID:%d,等级:%s", agentModel.Id, agentModel.LevelName)
|
||||
}
|
||||
agentModel.MembershipExpiryTime = lzUtils.RenewMembership(agentModel.MembershipExpiryTime)
|
||||
|
||||
if updateErr := l.svcCtx.AgentModel.UpdateWithVersion(l.ctx, nil, agentModel); updateErr != nil {
|
||||
return fmt.Errorf("修改代理信息失败: %+v", updateErr)
|
||||
}
|
||||
|
||||
// 如果不是有效期内续费,给上级代理发放升级奖励
|
||||
if !isValidRenewal && (agentOrder.LevelName == model.AgentLeveNameVIP || agentOrder.LevelName == model.AgentLeveNameSVIP) {
|
||||
// 验证升级路径的有效性
|
||||
if oldLevel != agentOrder.LevelName {
|
||||
upgradeRewardErr := l.svcCtx.AgentService.GiveUpgradeReward(transCtx, agentModel.Id, oldLevel, agentOrder.LevelName, session)
|
||||
if upgradeRewardErr != nil {
|
||||
logx.Errorf("发放升级奖励失败,代理ID:%d,旧等级:%s,新等级:%s,错误:%+v", agentModel.Id, oldLevel, agentOrder.LevelName, upgradeRewardErr)
|
||||
// 升级奖励失败不影响主流程,只记录日志
|
||||
} else {
|
||||
logx.Infof("发放升级奖励成功,代理ID:%d,旧等级:%s,新等级:%s", agentModel.Id, oldLevel, agentOrder.LevelName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logx.Errorf("微信支付回调,处理代理会员订单失败: %+v", err)
|
||||
refundErr := l.handleRefund(agentOrder)
|
||||
if refundErr != nil {
|
||||
logx.Errorf("微信支付回调,退款失败: %+v", refundErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
func (l *WechatPayCallbackLogic) handleRefund(order *model.AgentMembershipRechargeOrder) error {
|
||||
ctx := context.Background()
|
||||
// 退款
|
||||
if order.PaymentMethod == "wechat" {
|
||||
refundErr := l.svcCtx.WechatPayService.WeChatRefund(ctx, order.OrderNo, order.Amount, order.Amount)
|
||||
if refundErr != nil {
|
||||
return refundErr
|
||||
}
|
||||
} else {
|
||||
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount)
|
||||
if refundErr != nil {
|
||||
return refundErr
|
||||
}
|
||||
if refund.IsSuccess() {
|
||||
logx.Errorf("支付宝退款成功, orderID: %d", order.Id)
|
||||
// 更新订单状态为退款
|
||||
order.Status = "refunded"
|
||||
updateOrderErr := l.svcCtx.AgentMembershipRechargeOrderModel.UpdateWithVersion(ctx, nil, order)
|
||||
if updateOrderErr != nil {
|
||||
logx.Errorf("更新订单状态失败,订单ID: %d, 错误: %v", order.Id, updateOrderErr)
|
||||
return fmt.Errorf("更新订单状态失败: %v", updateOrderErr)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
logx.Errorf("支付宝退款失败:%v", refundErr)
|
||||
return refundErr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
} */
|
||||
239
app/main/api/internal/logic/pay/wechatpayrefundcallbacklogic.go
Normal file
239
app/main/api/internal/logic/pay/wechatpayrefundcallbacklogic.go
Normal file
@@ -0,0 +1,239 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/model"
|
||||
"qnc-server/common/globalkey"
|
||||
|
||||
"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 {
|
||||
order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "查找查询订单信息失败: %s", orderNo)
|
||||
}
|
||||
|
||||
// 检查订单是否已经处理过退款
|
||||
if order.Status == model.OrderStatusRefunded {
|
||||
logx.Infof("订单已经是退款状态,无需重复处理: orderNo=%s", orderNo)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 只处理成功和失败状态
|
||||
var orderStatus, refundStatus string
|
||||
switch status {
|
||||
case refunddomestic.STATUS_SUCCESS:
|
||||
orderStatus = model.OrderStatusRefunded
|
||||
refundStatus = model.OrderRefundStatusSuccess
|
||||
case refunddomestic.STATUS_CLOSED:
|
||||
// 退款关闭,保持订单原状态,更新退款记录为失败
|
||||
refundStatus = model.OrderRefundStatusFailed
|
||||
case refunddomestic.STATUS_ABNORMAL:
|
||||
// 退款异常,保持订单原状态,更新退款记录为失败
|
||||
refundStatus = model.OrderRefundStatusFailed
|
||||
default:
|
||||
// 其他状态暂不处理
|
||||
return nil
|
||||
}
|
||||
|
||||
// 使用事务同时更新订单和退款记录
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 查找最新的pending状态的退款记录
|
||||
refund, err := l.findLatestPendingRefund(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)
|
||||
}
|
||||
|
||||
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)
|
||||
// 直接返回,避免旧订单影响
|
||||
processErr = nil
|
||||
default:
|
||||
// 兼容旧订单,假设没有前缀的是查询订单
|
||||
processErr = l.handleQueryOrderRefund(orderNo, status)
|
||||
}
|
||||
|
||||
// 5. 处理错误并响应
|
||||
if processErr != nil {
|
||||
logx.Errorf("处理退款订单失败: orderNo=%s, err=%v", orderNo, processErr)
|
||||
}
|
||||
|
||||
// 无论处理是否成功,都返回成功响应给微信
|
||||
l.sendSuccessResponse(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// findLatestPendingRefund 查找订单最新的pending状态退款记录
|
||||
func (l *WechatPayRefundCallbackLogic) findLatestPendingRefund(ctx context.Context, orderId string) (*model.OrderRefund, error) {
|
||||
// 使用SelectBuilder查询最新的pending状态退款记录
|
||||
builder := l.svcCtx.OrderRefundModel.SelectBuilder().
|
||||
Where("order_id = ? AND status = ? AND del_state = ?", orderId, model.OrderRefundStatusPending, globalkey.DelStateNo).
|
||||
OrderBy("id DESC").
|
||||
Limit(1)
|
||||
|
||||
refunds, err := l.svcCtx.OrderRefundModel.FindAll(ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(refunds) == 0 {
|
||||
return nil, model.ErrNotFound
|
||||
}
|
||||
|
||||
return refunds[0], nil
|
||||
}
|
||||
Reference in New Issue
Block a user