first commit
This commit is contained in:
238
app/main/api/internal/logic/pay/alipaycallbacklogic.go
Normal file
238
app/main/api/internal/logic/pay/alipaycallbacklogic.go
Normal file
@@ -0,0 +1,238 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
"sim-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"github.com/smartwalle/alipay/v3"
|
||||
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
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, "A_") {
|
||||
// 旧系统会员充值订单(已废弃,新系统使用升级功能)
|
||||
// 直接返回成功,避免旧订单影响
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
} else if strings.HasPrefix(orderNo, "U_") {
|
||||
// 系统简化:移除升级功能,直接返回成功
|
||||
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) 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
|
||||
} */
|
||||
122
app/main/api/internal/logic/pay/easypaycallbacklogic.go
Normal file
122
app/main/api/internal/logic/pay/easypaycallbacklogic.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sim-server/app/main/api/internal/service"
|
||||
"sim-server/pkg/lzkit/lzUtils"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type EasyPayCallbackLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewEasyPayCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *EasyPayCallbackLogic {
|
||||
return &EasyPayCallbackLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *EasyPayCallbackLogic) EasyPayCallback(w http.ResponseWriter, r *http.Request) error {
|
||||
// 检查易支付服务是否启用
|
||||
if l.svcCtx.EasyPayService == nil {
|
||||
logx.Errorf("易支付服务未启用")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return nil
|
||||
}
|
||||
|
||||
notification, err := l.svcCtx.EasyPayService.HandleEasyPayNotification(r)
|
||||
if err != nil {
|
||||
logx.Errorf("易支付回调处理失败: %v", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 根据订单号前缀判断订单类型
|
||||
orderNo := notification.OutTradeNo
|
||||
if strings.HasPrefix(orderNo, "Q_") {
|
||||
// 查询订单处理
|
||||
return l.handleQueryOrderPayment(w, notification)
|
||||
} else if strings.HasPrefix(orderNo, "A_") {
|
||||
// 旧系统会员充值订单(已废弃,新系统使用升级功能)
|
||||
// 直接返回成功,避免旧订单影响
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
} else if strings.HasPrefix(orderNo, "U_") {
|
||||
// 系统简化:移除升级功能,直接返回成功
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
} else {
|
||||
// 兼容旧订单,假设没有前缀的是查询订单
|
||||
return l.handleQueryOrderPayment(w, notification)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理查询订单支付
|
||||
func (l *EasyPayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter, notification *service.EasyPayNotification) error {
|
||||
order, findOrderErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, notification.OutTradeNo)
|
||||
if findOrderErr != nil {
|
||||
logx.Errorf("易支付回调,查找订单失败: %+v", findOrderErr)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
if order.Status != "pending" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 验证支付状态
|
||||
if !l.svcCtx.EasyPayService.IsPaymentSuccess(notification) {
|
||||
logx.Infof("易支付回调,订单未支付成功,订单号: %s, 状态: %s", notification.OutTradeNo, notification.TradeStatus)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 验证金额(转换为字符串比较)
|
||||
amountStr := lzUtils.ToAlipayAmount(order.Amount)
|
||||
if amountStr != notification.Money {
|
||||
logx.Errorf("易支付回调,金额不一致,订单号: %s, 订单金额: %s, 回调金额: %s", notification.OutTradeNo, amountStr, notification.Money)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 更新订单状态
|
||||
order.Status = "paid"
|
||||
order.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||
order.PlatformOrderId = lzUtils.StringToNullString(notification.TradeNo)
|
||||
|
||||
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); updateErr != nil {
|
||||
logx.Errorf("易支付回调,修改订单信息失败: %+v", updateErr)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 发送异步任务处理后续流程
|
||||
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
|
||||
logx.Errorf("异步任务调度失败: %v", asyncErr)
|
||||
// 不返回错误,因为订单已经更新成功
|
||||
}
|
||||
|
||||
// 返回success,易支付要求返回纯字符串success
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
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"
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/api/internal/types"
|
||||
"sim-server/common/xerr"
|
||||
"sim-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
|
||||
}
|
||||
208
app/main/api/internal/logic/pay/paymentchecklogic.go
Normal file
208
app/main/api/internal/logic/pay/paymentchecklogic.go
Normal file
@@ -0,0 +1,208 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/api/internal/types"
|
||||
"sim-server/common/xerr"
|
||||
"sim-server/pkg/lzkit/lzUtils"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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)
|
||||
}
|
||||
|
||||
// 如果订单状态是 pending 且支付平台是易支付,主动查询易支付订单状态
|
||||
if order.Status == "pending" && order.PaymentPlatform == "easypay_alipay" {
|
||||
// 检查易支付服务是否启用
|
||||
if l.svcCtx.EasyPayService != nil {
|
||||
// 主动查询易支付订单状态
|
||||
queryResp, queryErr := l.svcCtx.EasyPayService.QueryOrderStatus(l.ctx, req.OrderNo)
|
||||
if queryErr != nil {
|
||||
logx.Errorf("主动查询易支付订单状态失败,订单号: %s, 错误: %v", req.OrderNo, queryErr)
|
||||
// 查询失败不影响返回,继续返回当前订单状态
|
||||
} else {
|
||||
// 如果易支付返回订单已支付(status == 1),更新本地订单状态
|
||||
if queryResp.GetStatusInt() == 1 {
|
||||
logx.Infof("主动查询发现易支付订单已支付,订单号: %s,开始更新订单状态", req.OrderNo)
|
||||
|
||||
// 重新查询订单(获取最新版本号)
|
||||
order, err = l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
|
||||
if err != nil {
|
||||
logx.Errorf("更新订单状态前重新查询订单失败: %v", err)
|
||||
} else if order.Status == "pending" {
|
||||
// 更新订单状态
|
||||
order.Status = "paid"
|
||||
order.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||
if queryResp.TradeNo != "" {
|
||||
order.PlatformOrderId = lzUtils.StringToNullString(queryResp.TradeNo)
|
||||
}
|
||||
|
||||
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); updateErr != nil {
|
||||
logx.Errorf("主动查询后更新订单状态失败: %v", updateErr)
|
||||
} else {
|
||||
logx.Infof("主动查询后成功更新订单状态为已支付,订单号: %s", req.OrderNo)
|
||||
|
||||
// 发送异步任务处理后续流程
|
||||
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
|
||||
logx.Errorf("主动查询后发送异步任务失败: %v", asyncErr)
|
||||
}
|
||||
|
||||
// 返回更新后的状态
|
||||
return &types.PaymentCheckResp{
|
||||
Type: "query",
|
||||
Status: "paid",
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果订单状态是 pending 且支付平台是云印签支付,主动查询云印签订单状态
|
||||
if order.Status == "pending" && (order.PaymentPlatform == "yunyinSignPay" || order.PaymentPlatform == "yunyinSignPay_wechat" || order.PaymentPlatform == "yunyinSignPay_alipay") {
|
||||
// 检查云印签支付服务是否启用
|
||||
if l.svcCtx.YunYinSignPayService != nil {
|
||||
// 主动查询云印签订单状态
|
||||
queryResp, queryErr := l.svcCtx.YunYinSignPayService.QueryPayeeBill(l.ctx, req.OrderNo)
|
||||
if queryErr != nil {
|
||||
logx.Errorf("主动查询云印签订单状态失败,订单号: %s, 错误: %v", req.OrderNo, queryErr)
|
||||
// 查询失败不影响返回,继续返回当前订单状态
|
||||
} else {
|
||||
// 根据云印签返回的支付状态更新本地订单状态
|
||||
// 云印签 payStatus: 0-订单生成, 1-支付中, 2-支付成功, 3-支付失败, 4-已退款
|
||||
// 我们的订单状态: pending, paid, failed, refunded
|
||||
var newOrderStatus string
|
||||
var shouldUpdate bool
|
||||
|
||||
switch queryResp.PayStatus {
|
||||
case 2: // 支付成功
|
||||
newOrderStatus = "paid"
|
||||
shouldUpdate = true
|
||||
case 3: // 支付失败
|
||||
newOrderStatus = "failed"
|
||||
shouldUpdate = true
|
||||
case 4: // 已退款
|
||||
newOrderStatus = "refunded"
|
||||
shouldUpdate = true
|
||||
case 0, 1: // 订单生成或支付中,保持 pending
|
||||
// 不更新,继续返回 pending
|
||||
}
|
||||
|
||||
if shouldUpdate {
|
||||
logx.Infof("主动查询发现云印签订单状态已变更,订单号: %s, 支付状态: %d, 新订单状态: %s", req.OrderNo, queryResp.PayStatus, newOrderStatus)
|
||||
|
||||
// 重新查询订单(获取最新版本号)
|
||||
order, err = l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
|
||||
if err != nil {
|
||||
logx.Errorf("更新订单状态前重新查询订单失败: %v", err)
|
||||
} else {
|
||||
// 更新云印签订单表的支付状态(无论订单状态如何,都要同步云印签的状态)
|
||||
yunyinOrder, findYunyinErr := l.svcCtx.YunyinSignPayOrderModel.FindOneByOrderId(l.ctx, order.Id)
|
||||
if findYunyinErr == nil && yunyinOrder != nil {
|
||||
// 更新支付状态
|
||||
// 云印签 payStatus: 0-订单生成, 1-支付中, 2-支付成功, 3-支付失败, 4-已退款
|
||||
// 我们的 payStatus: 0-待支付, 1-已支付, 2-已退款
|
||||
var newPayStatus int64
|
||||
if queryResp.PayStatus == 2 {
|
||||
newPayStatus = 1 // 已支付
|
||||
} else if queryResp.PayStatus == 4 {
|
||||
newPayStatus = 2 // 已退款
|
||||
} else if queryResp.PayStatus == 3 {
|
||||
// 支付失败,保持待支付状态
|
||||
newPayStatus = 0
|
||||
}
|
||||
|
||||
if newPayStatus != yunyinOrder.PayStatus {
|
||||
yunyinOrder.PayStatus = newPayStatus
|
||||
if _, updateYunyinErr := l.svcCtx.YunyinSignPayOrderModel.Update(l.ctx, nil, yunyinOrder); updateYunyinErr != nil {
|
||||
logx.Errorf("更新云印签订单支付状态失败: %v", updateYunyinErr)
|
||||
} else {
|
||||
logx.Infof("成功更新云印签订单支付状态,订单ID: %s, 新支付状态: %d", order.Id, newPayStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 只有在订单状态是 pending 时才更新订单状态
|
||||
if order.Status == "pending" {
|
||||
// 更新订单状态
|
||||
order.Status = newOrderStatus
|
||||
if newOrderStatus == "paid" {
|
||||
order.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||
if queryResp.ChannelOrderNo != "" {
|
||||
order.PlatformOrderId = lzUtils.StringToNullString(queryResp.ChannelOrderNo)
|
||||
}
|
||||
} else if newOrderStatus == "refunded" {
|
||||
order.RefundTime = lzUtils.TimeToNullTime(time.Now())
|
||||
}
|
||||
|
||||
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); updateErr != nil {
|
||||
logx.Errorf("主动查询后更新订单状态失败: %v", updateErr)
|
||||
} else {
|
||||
logx.Infof("主动查询后成功更新订单状态,订单号: %s, 新状态: %s", req.OrderNo, newOrderStatus)
|
||||
|
||||
// 如果订单已支付,发送异步任务处理后续流程
|
||||
if newOrderStatus == "paid" {
|
||||
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
|
||||
logx.Errorf("主动查询后发送异步任务失败: %v", asyncErr)
|
||||
}
|
||||
}
|
||||
|
||||
// 返回更新后的状态
|
||||
return &types.PaymentCheckResp{
|
||||
Type: "query",
|
||||
Status: newOrderStatus,
|
||||
}, nil
|
||||
}
|
||||
} else {
|
||||
// 订单状态已经不是 pending,说明可能已经被其他流程处理
|
||||
// 但仍然返回当前状态
|
||||
logx.Infof("订单状态已不是 pending,当前状态: %s,跳过更新", order.Status)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &types.PaymentCheckResp{
|
||||
Type: "query",
|
||||
Status: order.Status,
|
||||
}, nil
|
||||
}
|
||||
512
app/main/api/internal/logic/pay/paymentlogic.go
Normal file
512
app/main/api/internal/logic/pay/paymentlogic.go
Normal file
@@ -0,0 +1,512 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sim-server/app/main/api/internal/service"
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/api/internal/types"
|
||||
"sim-server/app/main/model"
|
||||
"sim-server/common/ctxdata"
|
||||
"sim-server/common/xerr"
|
||||
"sim-server/pkg/lzkit/crypto"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"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,用于开发环境测试支付模式
|
||||
userName string // 用户姓名(从查询缓存中获取)
|
||||
userMobile string // 用户手机号(从查询缓存中获取)
|
||||
}
|
||||
|
||||
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":
|
||||
// 系统简化:已废弃会员充值功能
|
||||
return errors.Wrapf(xerr.NewErrMsg("该功能已废弃"), "")
|
||||
|
||||
case "query":
|
||||
paymentTypeResp, err = l.QueryOrderPayment(req, session)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case "agent_upgrade":
|
||||
// 系统简化:已废弃升级功能
|
||||
return errors.Wrapf(xerr.NewErrMsg("该功能已废弃"), "")
|
||||
}
|
||||
|
||||
// 开发环境测试支付模式:跳过实际支付流程
|
||||
// 注意:订单状态更新在事务外进行,避免在事务中查询不到订单的问题
|
||||
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 == "easypay_alipay" {
|
||||
if l.svcCtx.EasyPayService == nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "易支付服务未启用")
|
||||
}
|
||||
// 获取用户ID用于次数轮询模式
|
||||
userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if getUidErr != nil {
|
||||
// 如果获取用户ID失败,使用空字符串(会回退到天数轮询)
|
||||
userID = ""
|
||||
}
|
||||
easyPayResult, createOrderErr := l.svcCtx.EasyPayService.CreateEasyPayOrder(l.ctx, paymentTypeResp.amount, paymentTypeResp.description, paymentTypeResp.outTradeNo, userID)
|
||||
if createOrderErr == nil && easyPayResult != nil {
|
||||
prepayData = easyPayResult.PayURL
|
||||
// 将渠道ID存储到context中,后续创建订单时使用
|
||||
ctx = context.WithValue(ctx, "easypay_cid", easyPayResult.CID)
|
||||
l.ctx = ctx
|
||||
}
|
||||
} else if req.PayMethod == "appleiap" {
|
||||
prepayData = l.svcCtx.ApplePayService.GetIappayAppID(paymentTypeResp.outTradeNo)
|
||||
} else if req.PayMethod == "yunyinSignPay" || req.PayMethod == "yunyinSignPay_wechat" || req.PayMethod == "yunyinSignPay_alipay" {
|
||||
logx.Infof("[云印签支付] 开始处理支付请求,支付方式: %s, 订单号: %s", req.PayMethod, paymentTypeResp.outTradeNo)
|
||||
|
||||
if l.svcCtx.YunYinSignPayService == nil {
|
||||
logx.Errorf("[云印签支付] 支付服务未启用")
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "云印签支付服务未启用")
|
||||
}
|
||||
|
||||
// 从查询缓存中获取用户姓名和手机号
|
||||
if paymentTypeResp.userMobile == "" {
|
||||
logx.Errorf("[云印签支付] 查询缓存中未找到用户手机号,订单号: %s", paymentTypeResp.outTradeNo)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 查询缓存中未找到用户手机号,无法使用云印签支付")
|
||||
}
|
||||
|
||||
// 获取用户ID
|
||||
userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if getUidErr != nil {
|
||||
logx.Errorf("[云印签支付] 获取用户ID失败,订单号: %s, 错误: %+v", paymentTypeResp.outTradeNo, getUidErr)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取用户ID失败: %+v", getUidErr)
|
||||
}
|
||||
|
||||
// 根据支付方式确定支付类型
|
||||
payType := 0 // 默认微信支付
|
||||
if req.PayMethod == "yunyinSignPay_alipay" {
|
||||
payType = 1 // 支付宝支付
|
||||
}
|
||||
logx.Infof("[云印签支付] 支付参数,订单号: %s, 用户ID: %s, 用户手机: %s, 用户姓名: %s, 金额: %.2f, 支付类型: %d(0=微信,1=支付宝)",
|
||||
paymentTypeResp.outTradeNo, userID, paymentTypeResp.userMobile, paymentTypeResp.userName, paymentTypeResp.amount, payType)
|
||||
|
||||
// 查询用户是否有未完成的签署(待签署且待支付)
|
||||
var yunYinSignPayResult *service.CreateYunYinSignPayOrderResult
|
||||
// 使用 SelectBuilder 查询未完成的签署订单(del_state 由系统自动处理)
|
||||
logx.Infof("[云印签支付] 查询用户未完成的签署订单,用户ID: %s", userID)
|
||||
unfinishedBuilder := l.svcCtx.YunyinSignPayOrderModel.SelectBuilder().
|
||||
Where(squirrel.Eq{
|
||||
"user_id": userID,
|
||||
"sign_status": 0, // 待签署
|
||||
"pay_status": 0, // 待支付
|
||||
}).
|
||||
OrderBy("create_time DESC").
|
||||
Limit(1)
|
||||
unfinishedOrders, findUnfinishedErr := l.svcCtx.YunyinSignPayOrderModel.FindAll(l.ctx, unfinishedBuilder, "")
|
||||
var unfinishedOrder *model.YunyinSignPayOrder
|
||||
if findUnfinishedErr == nil && len(unfinishedOrders) > 0 {
|
||||
unfinishedOrder = unfinishedOrders[0]
|
||||
logx.Infof("[云印签支付] 找到未完成的签署订单,订单号: %s, 任务ID: %s, 参与者ID: %s, 金额: %.2f",
|
||||
paymentTypeResp.outTradeNo, unfinishedOrder.TaskId, unfinishedOrder.ParticipantId, unfinishedOrder.Amount)
|
||||
} else {
|
||||
if findUnfinishedErr != nil {
|
||||
logx.Infof("[云印签支付] 查询未完成签署订单失败,用户ID: %s, 错误: %v", userID, findUnfinishedErr)
|
||||
} else {
|
||||
logx.Infof("[云印签支付] 未找到未完成的签署订单,用户ID: %s, 将创建新签署流程", userID)
|
||||
}
|
||||
}
|
||||
|
||||
if unfinishedOrder != nil {
|
||||
// 复用未完成的签署,只获取新的支付链接
|
||||
logx.Infof("[云印签支付] 复用未完成的签署,订单号: %s, 任务ID: %s, 参与者ID: %s",
|
||||
paymentTypeResp.outTradeNo, unfinishedOrder.TaskId, unfinishedOrder.ParticipantId)
|
||||
|
||||
// 获取token和操作ID(带缓存)
|
||||
logx.Infof("[云印签支付] 开始获取AccessToken,订单号: %s", paymentTypeResp.outTradeNo)
|
||||
accessToken, tokenErr := l.svcCtx.YunYinSignPayService.GetAccessToken(l.ctx)
|
||||
if tokenErr != nil {
|
||||
logx.Errorf("[云印签支付] 获取AccessToken失败,订单号: %s, 错误: %+v", paymentTypeResp.outTradeNo, tokenErr)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取云印签token失败: %+v", tokenErr)
|
||||
}
|
||||
logx.Infof("[云印签支付] 获取AccessToken成功,订单号: %s", paymentTypeResp.outTradeNo)
|
||||
|
||||
logx.Infof("[云印签支付] 开始获取操作ID,订单号: %s", paymentTypeResp.outTradeNo)
|
||||
operationUserId, userIdErr := l.svcCtx.YunYinSignPayService.GetUserId(l.ctx, accessToken)
|
||||
if userIdErr != nil {
|
||||
logx.Errorf("[云印签支付] 获取操作ID失败,订单号: %s, 错误: %+v", paymentTypeResp.outTradeNo, userIdErr)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取云印签操作ID失败: %+v", userIdErr)
|
||||
}
|
||||
logx.Infof("[云印签支付] 获取操作ID成功,订单号: %s, 操作ID: %s", paymentTypeResp.outTradeNo, operationUserId)
|
||||
|
||||
// 获取新的支付链接
|
||||
logx.Infof("[云印签支付] 开始获取支付链接,订单号: %s, 参与者ID: %s, 支付类型: %d",
|
||||
paymentTypeResp.outTradeNo, unfinishedOrder.ParticipantId, payType)
|
||||
payURL, payURLErr := l.svcCtx.YunYinSignPayService.GetPaymentURL(l.ctx, accessToken, operationUserId, unfinishedOrder.ParticipantId, payType)
|
||||
if payURLErr != nil {
|
||||
logx.Errorf("[云印签支付] 获取支付链接失败,订单号: %s, 参与者ID: %s, 错误: %+v",
|
||||
paymentTypeResp.outTradeNo, unfinishedOrder.ParticipantId, payURLErr)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取云印签支付链接失败: %+v", payURLErr)
|
||||
}
|
||||
logx.Infof("[云印签支付] 获取支付链接成功,订单号: %s, 支付链接: %s", paymentTypeResp.outTradeNo, payURL)
|
||||
|
||||
yunYinSignPayResult = &service.CreateYunYinSignPayOrderResult{
|
||||
PayURL: payURL,
|
||||
ParticipantID: unfinishedOrder.ParticipantId,
|
||||
TaskID: unfinishedOrder.TaskId,
|
||||
}
|
||||
logx.Infof("[云印签支付] 复用签署流程完成,订单号: %s, 任务ID: %s, 参与者ID: %s, 支付链接: %s",
|
||||
paymentTypeResp.outTradeNo, yunYinSignPayResult.TaskID, yunYinSignPayResult.ParticipantID, yunYinSignPayResult.PayURL)
|
||||
} else {
|
||||
// 没有未完成的签署,创建新的签署流程
|
||||
logx.Infof("[云印签支付] 开始创建新签署流程,订单号: %s, 用户手机: %s, 用户姓名: %s, 金额: %.2f, 支付类型: %d",
|
||||
paymentTypeResp.outTradeNo, paymentTypeResp.userMobile, paymentTypeResp.userName, paymentTypeResp.amount, payType)
|
||||
var createOrderErr error
|
||||
yunYinSignPayResult, createOrderErr = l.svcCtx.YunYinSignPayService.CreateYunYinSignPayOrder(
|
||||
l.ctx,
|
||||
paymentTypeResp.userMobile,
|
||||
paymentTypeResp.userName,
|
||||
paymentTypeResp.amount,
|
||||
paymentTypeResp.outTradeNo,
|
||||
payType,
|
||||
)
|
||||
if createOrderErr != nil {
|
||||
logx.Errorf("[云印签支付] 创建新签署流程失败,订单号: %s, 错误: %+v", paymentTypeResp.outTradeNo, createOrderErr)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 创建云印签支付订单失败: %+v", createOrderErr)
|
||||
}
|
||||
logx.Infof("[云印签支付] 创建新签署流程成功,订单号: %s, 任务ID: %s, 参与者ID: %s, 支付链接: %s",
|
||||
paymentTypeResp.outTradeNo, yunYinSignPayResult.TaskID, yunYinSignPayResult.ParticipantID, yunYinSignPayResult.PayURL)
|
||||
}
|
||||
|
||||
prepayData = yunYinSignPayResult.PayURL
|
||||
logx.Infof("[云印签支付] 支付链接获取成功,订单号: %s, 支付链接: %s", paymentTypeResp.outTradeNo, yunYinSignPayResult.PayURL)
|
||||
|
||||
// 将云印签信息存储到context中,后续创建订单和云印签订单记录时使用
|
||||
ctx = context.WithValue(ctx, "yunyin_sign_pay_result", yunYinSignPayResult)
|
||||
ctx = context.WithValue(ctx, "yunyin_sign_pay_user_id", userID)
|
||||
ctx = context.WithValue(ctx, "yunyin_sign_pay_user_mobile", paymentTypeResp.userMobile)
|
||||
ctx = context.WithValue(ctx, "yunyin_sign_pay_user_name", paymentTypeResp.userName)
|
||||
ctx = context.WithValue(ctx, "yunyin_sign_pay_amount", paymentTypeResp.amount)
|
||||
ctx = context.WithValue(ctx, "yunyin_sign_pay_pay_type", payType)
|
||||
l.ctx = ctx
|
||||
logx.Infof("[云印签支付] 云印签信息已存储到context,订单号: %s, 任务ID: %s, 参与者ID: %s",
|
||||
paymentTypeResp.outTradeNo, yunYinSignPayResult.TaskID, yunYinSignPayResult.ParticipantID)
|
||||
}
|
||||
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_") {
|
||||
// 系统简化:升级订单已废弃,跳过处理
|
||||
logx.Infof("开发测试模式,升级订单已废弃,订单号: %s", paymentTypeResp.outTradeNo)
|
||||
} 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)
|
||||
}
|
||||
|
||||
// 解析查询参数,获取用户姓名和手机号(用于云印签支付)
|
||||
var userName, userMobile string
|
||||
if data.Params != "" {
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
key, decodeErr := hex.DecodeString(secretKey)
|
||||
if decodeErr == nil {
|
||||
decryptedData, decryptErr := crypto.AesDecrypt(data.Params, key)
|
||||
if decryptErr == nil {
|
||||
var params map[string]interface{}
|
||||
if unmarshalErr := json.Unmarshal(decryptedData, ¶ms); unmarshalErr == nil {
|
||||
if name, ok := params["name"].(string); ok {
|
||||
userName = name
|
||||
}
|
||||
if mobile, ok := params["mobile"].(string); ok {
|
||||
userMobile = mobile
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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",
|
||||
}
|
||||
// 如果是易支付,记录使用的渠道ID到备注字段
|
||||
if req.PayMethod == "easypay_alipay" {
|
||||
if cid, ok := l.ctx.Value("easypay_cid").(string); ok && cid != "" {
|
||||
order.Remark = sql.NullString{String: fmt.Sprintf("易支付渠道号: %s", cid), Valid: true}
|
||||
}
|
||||
}
|
||||
_, 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 req.PayMethod == "yunyinSignPay" || req.PayMethod == "yunyinSignPay_wechat" || req.PayMethod == "yunyinSignPay_alipay" {
|
||||
logx.Infof("[云印签支付] 开始创建云印签订单记录,订单号: %s, 订单ID: %s", outTradeNo, orderID)
|
||||
yunYinSignPayResult, ok := l.ctx.Value("yunyin_sign_pay_result").(*service.CreateYunYinSignPayOrderResult)
|
||||
if ok && yunYinSignPayResult != nil {
|
||||
userID, _ := l.ctx.Value("yunyin_sign_pay_user_id").(string)
|
||||
userMobile, _ := l.ctx.Value("yunyin_sign_pay_user_mobile").(string)
|
||||
userName, _ := l.ctx.Value("yunyin_sign_pay_user_name").(string)
|
||||
amount, _ := l.ctx.Value("yunyin_sign_pay_amount").(float64)
|
||||
payType, _ := l.ctx.Value("yunyin_sign_pay_pay_type").(int)
|
||||
|
||||
logx.Infof("[云印签支付] 云印签订单数据,订单号: %s, 订单ID: %s, 用户ID: %s, 任务ID: %s, 参与者ID: %s, 金额: %.2f, 支付类型: %d",
|
||||
outTradeNo, orderID, userID, yunYinSignPayResult.TaskID, yunYinSignPayResult.ParticipantID, amount, payType)
|
||||
|
||||
yunyinSignPayOrder := model.YunyinSignPayOrder{
|
||||
Id: uuid.NewString(),
|
||||
OrderId: orderID,
|
||||
UserId: userID,
|
||||
TaskId: yunYinSignPayResult.TaskID,
|
||||
ParticipantId: yunYinSignPayResult.ParticipantID,
|
||||
Amount: amount,
|
||||
PayType: int64(payType),
|
||||
SignStatus: 0, // 待签署
|
||||
PayStatus: 0, // 待支付
|
||||
SourceOrderCode: outTradeNo,
|
||||
UserMobile: sql.NullString{String: userMobile, Valid: userMobile != ""},
|
||||
UserName: sql.NullString{String: userName, Valid: userName != ""},
|
||||
DelState: 0,
|
||||
Version: 0,
|
||||
}
|
||||
|
||||
_, insertYunYinErr := l.svcCtx.YunyinSignPayOrderModel.Insert(l.ctx, session, &yunyinSignPayOrder)
|
||||
if insertYunYinErr != nil {
|
||||
logx.Errorf("[云印签支付] 保存云印签订单失败,订单号: %s, 订单ID: %s, 错误: %+v", outTradeNo, orderID, insertYunYinErr)
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 保存云印签订单失败: %+v", insertYunYinErr)
|
||||
}
|
||||
logx.Infof("[云印签支付] 云印签订单记录创建成功,订单号: %s, 订单ID: %s, 云印签订单ID: %s, 任务ID: %s, 参与者ID: %s",
|
||||
outTradeNo, orderID, yunyinSignPayOrder.Id, yunyinSignPayOrder.TaskId, yunyinSignPayOrder.ParticipantId)
|
||||
} else {
|
||||
logx.Errorf("[云印签支付] 未找到云印签支付结果,订单号: %s, 订单ID: %s", outTradeNo, orderID)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是代理推广订单,创建完整的代理订单记录
|
||||
if data.AgentIdentifier != "" && agentLinkModel != nil {
|
||||
// 获取产品配置(必须存在)
|
||||
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)
|
||||
}
|
||||
|
||||
// 系统简化:移除等级加成,直接使用底价
|
||||
actualBasePrice := productConfig.BasePrice
|
||||
|
||||
// 计算提价成本(使用产品配置)
|
||||
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,
|
||||
userName: userName,
|
||||
userMobile: userMobile,
|
||||
}, nil
|
||||
}
|
||||
242
app/main/api/internal/logic/pay/wechatpaycallbacklogic.go
Normal file
242
app/main/api/internal/logic/pay/wechatpaycallbacklogic.go
Normal file
@@ -0,0 +1,242 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
"sim-server/app/main/api/internal/service"
|
||||
"sim-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
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, "A_") {
|
||||
// 旧系统会员充值订单(已废弃,新系统使用升级功能)
|
||||
// 直接返回成功,避免旧订单影响
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
} else if strings.HasPrefix(orderNo, "U_") {
|
||||
// 系统简化:移除升级功能,直接返回成功
|
||||
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) 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"
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/model"
|
||||
"sim-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
|
||||
}
|
||||
250
app/main/api/internal/logic/pay/yunyinsignpaycallbacklogic.go
Normal file
250
app/main/api/internal/logic/pay/yunyinsignpaycallbacklogic.go
Normal file
@@ -0,0 +1,250 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/pkg/lzkit/lzUtils"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type YunYinSignPayCallbackLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewYunYinSignPayCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *YunYinSignPayCallbackLogic {
|
||||
return &YunYinSignPayCallbackLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// YunYinSignPayCallback 云印签支付回调处理
|
||||
func (l *YunYinSignPayCallbackLogic) YunYinSignPayCallback(w http.ResponseWriter, r *http.Request) error {
|
||||
logx.Infof("[云印签回调] 收到回调请求,Method: %s, URL: %s, RemoteAddr: %s", r.Method, r.URL.String(), r.RemoteAddr)
|
||||
logx.Infof("[云印签回调] 请求Header,Content-Type: %s, User-Agent: %s", r.Header.Get("Content-Type"), r.Header.Get("User-Agent"))
|
||||
|
||||
// 检查云印签支付服务是否启用
|
||||
if l.svcCtx.YunYinSignPayService == nil {
|
||||
logx.Errorf("[云印签回调] 云印签支付服务未启用")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 解析表单数据
|
||||
logx.Infof("[云印签回调] 开始解析表单数据")
|
||||
if err := r.ParseForm(); err != nil {
|
||||
logx.Errorf("[云印签回调] 解析表单数据失败: %v", err)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
logx.Infof("[云印签回调] 表单数据解析成功,Form参数数量: %d", len(r.Form))
|
||||
|
||||
// 获取回调参数
|
||||
tradeState := r.FormValue("tradeState")
|
||||
orderType := r.FormValue("orderType")
|
||||
payOrderNo := r.FormValue("payOrderNo")
|
||||
payAmount := r.FormValue("payAmount")
|
||||
flowId := r.FormValue("flowId")
|
||||
sourceOrderCode := r.FormValue("sourceOrderCode")
|
||||
channelOrderNo := r.FormValue("channelOrderNo")
|
||||
paySuccessTime := r.FormValue("paySuccessTime")
|
||||
|
||||
// 记录所有回调参数
|
||||
logx.Infof("[云印签回调] 回调参数详情:")
|
||||
logx.Infof("[云印签回调] - tradeState: %s", tradeState)
|
||||
logx.Infof("[云印签回调] - orderType: %s", orderType)
|
||||
logx.Infof("[云印签回调] - payOrderNo: %s", payOrderNo)
|
||||
logx.Infof("[云印签回调] - payAmount: %s", payAmount)
|
||||
logx.Infof("[云印签回调] - flowId: %s", flowId)
|
||||
logx.Infof("[云印签回调] - sourceOrderCode: %s", sourceOrderCode)
|
||||
logx.Infof("[云印签回调] - channelOrderNo: %s", channelOrderNo)
|
||||
logx.Infof("[云印签回调] - paySuccessTime: %s", paySuccessTime)
|
||||
|
||||
// 记录回调日志
|
||||
logx.Infof("[云印签回调] 回调通知摘要,订单号: %s, 支付状态: %s, 订单类型: %s, 支付单号: %s, 金额: %s, 流程ID: %s",
|
||||
sourceOrderCode, tradeState, orderType, payOrderNo, payAmount, flowId)
|
||||
|
||||
// 只处理付款通知(orderType == "pay")
|
||||
logx.Infof("[云印签回调] 检查订单类型,orderType: %s", orderType)
|
||||
if orderType != "pay" {
|
||||
logx.Infof("[云印签回调] 非付款通知,跳过处理,订单类型: %s, 订单号: %s", orderType, sourceOrderCode)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
logx.Infof("[云印签回调] 确认为付款通知,继续处理")
|
||||
|
||||
// 如果没有 sourceOrderCode,无法关联订单,直接返回成功
|
||||
if sourceOrderCode == "" {
|
||||
logx.Errorf("[云印签回调] 缺少 sourceOrderCode 参数,无法关联订单")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
logx.Infof("[云印签回调] sourceOrderCode 存在,订单号: %s", sourceOrderCode)
|
||||
|
||||
// 根据 sourceOrderCode 查找订单
|
||||
logx.Infof("[云印签回调] 开始查找订单,订单号: %s", sourceOrderCode)
|
||||
order, findOrderErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, sourceOrderCode)
|
||||
if findOrderErr != nil {
|
||||
logx.Errorf("[云印签回调] 查找订单失败,订单号: %s, 错误: %v", sourceOrderCode, findOrderErr)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
logx.Infof("[云印签回调] 订单查找成功,订单号: %s, 订单ID: %s, 订单状态: %s, 订单金额: %.2f, 支付平台: %s",
|
||||
sourceOrderCode, order.Id, order.Status, order.Amount, order.PaymentPlatform)
|
||||
|
||||
// 幂等性检查:如果订单已经不是 pending 状态,说明已经处理过,直接返回成功
|
||||
logx.Infof("[云印签回调] 幂等性检查,订单状态: %s", order.Status)
|
||||
if order.Status != "pending" {
|
||||
logx.Infof("[云印签回调] 订单已处理,跳过重复处理,订单号: %s, 当前状态: %s", sourceOrderCode, order.Status)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
logx.Infof("[云印签回调] 订单状态为pending,继续处理")
|
||||
|
||||
// 验证支付状态:tradeState == "00000" 或 "success" 表示支付成功
|
||||
logx.Infof("[云印签回调] 验证支付状态,tradeState: %s", tradeState)
|
||||
isPaymentSuccess := tradeState == "00000" || tradeState == "success"
|
||||
if !isPaymentSuccess {
|
||||
logx.Infof("[云印签回调] 订单未支付成功,跳过处理,订单号: %s, 支付状态: %s", sourceOrderCode, tradeState)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
logx.Infof("[云印签回调] 支付状态验证通过,tradeState: %s 表示支付成功", tradeState)
|
||||
|
||||
// 验证金额
|
||||
logx.Infof("[云印签回调] 开始验证金额,回调金额字符串: %s, 订单金额: %.2f", payAmount, order.Amount)
|
||||
payAmountFloat, parseAmountErr := strconv.ParseFloat(payAmount, 64)
|
||||
if parseAmountErr != nil {
|
||||
logx.Errorf("[云印签回调] 金额解析失败,订单号: %s, 金额字符串: %s, 错误: %v", sourceOrderCode, payAmount, parseAmountErr)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
logx.Infof("[云印签回调] 金额解析成功,回调金额: %.2f, 订单金额: %.2f", payAmountFloat, order.Amount)
|
||||
|
||||
// 金额比较(允许小数点误差)
|
||||
amountDiff := payAmountFloat - order.Amount
|
||||
if amountDiff < 0 {
|
||||
amountDiff = -amountDiff
|
||||
}
|
||||
logx.Infof("[云印签回调] 金额差异: %.2f", amountDiff)
|
||||
if amountDiff > 0.01 { // 允许1分钱的误差
|
||||
logx.Errorf("[云印签回调] 金额不一致,订单号: %s, 订单金额: %.2f, 回调金额: %.2f, 差异: %.2f",
|
||||
sourceOrderCode, order.Amount, payAmountFloat, amountDiff)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
logx.Infof("[云印签回调] 金额验证通过,差异在允许范围内")
|
||||
|
||||
// 解析支付成功时间
|
||||
logx.Infof("[云印签回调] 开始解析支付成功时间,时间字符串: %s", paySuccessTime)
|
||||
var payTime time.Time
|
||||
if paySuccessTime != "" {
|
||||
// 尝试多种时间格式
|
||||
timeFormats := []string{
|
||||
"2006-01-02 15:04:05",
|
||||
"2006-01-02T15:04:05",
|
||||
"2006-01-02 15:04:05.000",
|
||||
"2006-01-02T15:04:05.000Z",
|
||||
}
|
||||
parsed := false
|
||||
for i, format := range timeFormats {
|
||||
if t, err := time.Parse(format, paySuccessTime); err == nil {
|
||||
payTime = t
|
||||
parsed = true
|
||||
logx.Infof("[云印签回调] 支付时间解析成功,使用格式[%d]: %s, 解析结果: %s", i, format, payTime.Format("2006-01-02 15:04:05"))
|
||||
break
|
||||
}
|
||||
}
|
||||
if !parsed {
|
||||
// 如果解析失败,使用当前时间
|
||||
payTime = time.Now()
|
||||
logx.Infof("[云印签回调] 支付时间解析失败,使用当前时间,订单号: %s, 时间字符串: %s, 当前时间: %s",
|
||||
sourceOrderCode, paySuccessTime, payTime.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
} else {
|
||||
payTime = time.Now()
|
||||
logx.Infof("[云印签回调] 支付时间字符串为空,使用当前时间: %s", payTime.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
// 更新订单状态
|
||||
logx.Infof("[云印签回调] 开始更新订单状态,订单号: %s, 订单ID: %s, 原状态: %s, 新状态: paid, 支付时间: %s, 渠道单号: %s",
|
||||
sourceOrderCode, order.Id, order.Status, payTime.Format("2006-01-02 15:04:05"), channelOrderNo)
|
||||
order.Status = "paid"
|
||||
order.PayTime = lzUtils.TimeToNullTime(payTime)
|
||||
if channelOrderNo != "" {
|
||||
order.PlatformOrderId = lzUtils.StringToNullString(channelOrderNo)
|
||||
logx.Infof("[云印签回调] 设置渠道单号,订单号: %s, 渠道单号: %s", sourceOrderCode, channelOrderNo)
|
||||
}
|
||||
|
||||
logx.Infof("[云印签回调] 调用UpdateWithVersion更新订单,订单号: %s, 订单ID: %s, Version: %d",
|
||||
sourceOrderCode, order.Id, order.Version)
|
||||
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); updateErr != nil {
|
||||
logx.Errorf("[云印签回调] 更新订单状态失败,订单号: %s, 订单ID: %s, 错误: %v", sourceOrderCode, order.Id, updateErr)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
logx.Infof("[云印签回调] 订单状态更新成功,订单号: %s, 订单ID: %s", sourceOrderCode, order.Id)
|
||||
|
||||
// 更新云印签订单表的支付状态
|
||||
logx.Infof("[云印签回调] 开始查找云印签订单记录,订单ID: %s, 流程ID: %s", order.Id, flowId)
|
||||
yunyinOrder, findYunyinErr := l.svcCtx.YunyinSignPayOrderModel.FindOneByOrderId(l.ctx, order.Id)
|
||||
if findYunyinErr == nil && yunyinOrder != nil {
|
||||
logx.Infof("[云印签回调] 找到云印签订单记录,订单ID: %s, 云印签订单ID: %s, 任务ID: %s, 参与者ID: %s, 当前支付状态: %d",
|
||||
order.Id, yunyinOrder.Id, yunyinOrder.TaskId, yunyinOrder.ParticipantId, yunyinOrder.PayStatus)
|
||||
// 更新支付状态为已支付(1)
|
||||
if yunyinOrder.PayStatus != 1 {
|
||||
logx.Infof("[云印签回调] 需要更新云印签订单支付状态,订单ID: %s, 原支付状态: %d, 新支付状态: 1", order.Id, yunyinOrder.PayStatus)
|
||||
yunyinOrder.PayStatus = 1
|
||||
if _, updateYunyinErr := l.svcCtx.YunyinSignPayOrderModel.Update(l.ctx, nil, yunyinOrder); updateYunyinErr != nil {
|
||||
logx.Errorf("[云印签回调] 更新云印签订单支付状态失败,订单ID: %s, 云印签订单ID: %s, 错误: %v",
|
||||
order.Id, yunyinOrder.Id, updateYunyinErr)
|
||||
} else {
|
||||
logx.Infof("[云印签回调] 成功更新云印签订单支付状态,订单ID: %s, 云印签订单ID: %s, 支付状态: 1",
|
||||
order.Id, yunyinOrder.Id)
|
||||
}
|
||||
} else {
|
||||
logx.Infof("[云印签回调] 云印签订单支付状态已是已支付,无需更新,订单ID: %s, 支付状态: %d", order.Id, yunyinOrder.PayStatus)
|
||||
}
|
||||
} else {
|
||||
if findYunyinErr != nil {
|
||||
logx.Infof("[云印签回调] 查找云印签订单记录失败,订单ID: %s, 流程ID: %s, 错误: %v", order.Id, flowId, findYunyinErr)
|
||||
} else {
|
||||
logx.Infof("[云印签回调] 未找到对应的云印签订单记录,订单ID: %s, 流程ID: %s", order.Id, flowId)
|
||||
}
|
||||
}
|
||||
|
||||
// 发送异步任务处理后续流程
|
||||
logx.Infof("[云印签回调] 开始发送异步任务,订单ID: %s", order.Id)
|
||||
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
|
||||
logx.Errorf("[云印签回调] 异步任务调度失败,订单ID: %s, 错误: %v", order.Id, asyncErr)
|
||||
// 不返回错误,因为订单已经更新成功
|
||||
} else {
|
||||
logx.Infof("[云印签回调] 异步任务发送成功,订单ID: %s", order.Id)
|
||||
}
|
||||
|
||||
logx.Infof("[云印签回调] 回调处理完成,订单号: %s, 订单ID: %s, 支付单号: %s, 渠道单号: %s, 支付金额: %.2f",
|
||||
sourceOrderCode, order.Id, payOrderNo, channelOrderNo, payAmountFloat)
|
||||
|
||||
// 返回 success,云印签要求返回纯字符串 success
|
||||
logx.Infof("[云印签回调] 返回success响应给云印签")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
153
app/main/api/internal/logic/pay/yunyinsignpayrefundlogic.go
Normal file
153
app/main/api/internal/logic/pay/yunyinsignpayrefundlogic.go
Normal file
@@ -0,0 +1,153 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/api/internal/types"
|
||||
"sim-server/app/main/model"
|
||||
"sim-server/common/xerr"
|
||||
"sim-server/pkg/lzkit/lzUtils"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type YunYinSignPayRefundLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewYunYinSignPayRefundLogic(ctx context.Context, svcCtx *svc.ServiceContext) *YunYinSignPayRefundLogic {
|
||||
return &YunYinSignPayRefundLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// YunYinSignPayRefund 云印签退款
|
||||
func (l *YunYinSignPayRefundLogic) YunYinSignPayRefund(req *types.YunYinSignPayRefundReq) (resp *types.YunYinSignPayRefundResp, err error) {
|
||||
// 检查云印签支付服务是否启用
|
||||
if l.svcCtx.YunYinSignPayService == nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "云印签支付服务未启用")
|
||||
}
|
||||
|
||||
// 验证参数:order_no 和 participate_id 至少提供一个
|
||||
if req.OrderNo == "" && req.ParticipateId == 0 {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "订单号和参与方ID至少需要提供一个")
|
||||
}
|
||||
|
||||
// 查找订单(如果提供了订单号)
|
||||
var order *model.Order
|
||||
if req.OrderNo != "" {
|
||||
order, err = l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查找订单失败,订单号: %s, 错误: %v", req.OrderNo, err)
|
||||
}
|
||||
|
||||
// 验证订单支付平台
|
||||
if order.PaymentPlatform != "yunyinSignPay" && order.PaymentPlatform != "yunyinSignPay_wechat" && order.PaymentPlatform != "yunyinSignPay_alipay" {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "订单不是云印签支付订单,订单号: %s, 支付平台: %s", req.OrderNo, order.PaymentPlatform)
|
||||
}
|
||||
|
||||
// 验证订单状态
|
||||
if order.Status != "paid" {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "订单状态不允许退款,订单号: %s, 状态: %s", req.OrderNo, order.Status)
|
||||
}
|
||||
|
||||
// 验证退款金额
|
||||
if req.RefundAmount > order.Amount {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "退款金额不能超过订单金额,订单号: %s, 订单金额: %.2f, 退款金额: %.2f", req.OrderNo, order.Amount, req.RefundAmount)
|
||||
}
|
||||
}
|
||||
|
||||
// 调用云印签退款接口
|
||||
// RefundPayeeBill 方法会自动处理:如果只提供了 sourceOrderCode,会查询收款单获取 participateId
|
||||
refundErr := l.svcCtx.YunYinSignPayService.RefundPayeeBill(
|
||||
l.ctx,
|
||||
req.OrderNo,
|
||||
req.ParticipateId,
|
||||
req.RefundAmount,
|
||||
req.RefundReason,
|
||||
)
|
||||
if refundErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "云印签退款失败: %v", refundErr)
|
||||
}
|
||||
|
||||
// 如果提供了订单号,更新订单状态和创建退款记录
|
||||
if req.OrderNo != "" && order != nil {
|
||||
// 检查是否全额退款
|
||||
isFullRefund := req.RefundAmount >= order.Amount-0.01 // 允许1分钱的误差
|
||||
|
||||
// 创建退款记录并更新订单状态
|
||||
err = l.createRefundRecordAndUpdateOrder(order, req, isFullRefund)
|
||||
if err != nil {
|
||||
logx.Errorf("创建退款记录或更新订单状态失败: %v", err)
|
||||
// 不返回错误,因为退款已经成功
|
||||
}
|
||||
|
||||
// 更新云印签订单表的支付状态
|
||||
yunyinOrder, findYunyinErr := l.svcCtx.YunyinSignPayOrderModel.FindOneByOrderId(l.ctx, order.Id)
|
||||
if findYunyinErr == nil && yunyinOrder != nil {
|
||||
if isFullRefund {
|
||||
// 全额退款,更新为已退款状态(2)
|
||||
yunyinOrder.PayStatus = 2
|
||||
}
|
||||
// 部分退款保持已支付状态(1),但可以通过其他字段记录退款金额
|
||||
if _, updateYunyinErr := l.svcCtx.YunyinSignPayOrderModel.Update(l.ctx, nil, yunyinOrder); updateYunyinErr != nil {
|
||||
logx.Errorf("更新云印签订单状态失败: %v", updateYunyinErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logx.Infof("云印签退款成功,订单号: %s, 参与方ID: %d, 退款金额: %.2f", req.OrderNo, req.ParticipateId, req.RefundAmount)
|
||||
|
||||
return &types.YunYinSignPayRefundResp{
|
||||
Success: true,
|
||||
Message: "退款申请已提交",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// createRefundRecordAndUpdateOrder 创建退款记录并更新订单状态
|
||||
func (l *YunYinSignPayRefundLogic) createRefundRecordAndUpdateOrder(order *model.Order, req *types.YunYinSignPayRefundReq, isFullRefund bool) error {
|
||||
return l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 生成退款单号
|
||||
refundNo := fmt.Sprintf("refund-%s", order.OrderNo)
|
||||
|
||||
// 创建退款记录
|
||||
refund := &model.OrderRefund{
|
||||
Id: uuid.NewString(),
|
||||
RefundNo: refundNo,
|
||||
PlatformRefundId: sql.NullString{String: "", Valid: false}, // 云印签退款没有平台退款单号
|
||||
OrderId: order.Id,
|
||||
UserId: order.UserId,
|
||||
ProductId: order.ProductId,
|
||||
RefundAmount: req.RefundAmount,
|
||||
RefundReason: lzUtils.StringToNullString(req.RefundReason),
|
||||
Status: "success", // 云印签退款是异步的,这里标记为成功
|
||||
RefundTime: sql.NullTime{Time: time.Now(), Valid: true},
|
||||
}
|
||||
|
||||
if _, err := l.svcCtx.OrderRefundModel.Insert(ctx, session, refund); err != nil {
|
||||
return fmt.Errorf("创建退款记录失败: %v", err)
|
||||
}
|
||||
|
||||
// 更新订单状态
|
||||
if isFullRefund {
|
||||
order.Status = "refunded"
|
||||
}
|
||||
// 部分退款保持 paid 状态
|
||||
|
||||
if _, err := l.svcCtx.OrderModel.Update(ctx, session, order); err != nil {
|
||||
return fmt.Errorf("更新订单状态失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user