fix
This commit is contained in:
@@ -45,6 +45,9 @@ func (l *AlipayCallbackLogic) AlipayCallback(w http.ResponseWriter, r *http.Requ
|
||||
} else if strings.HasPrefix(orderNo, "U_") {
|
||||
// 代理升级订单处理
|
||||
return l.handleAgentUpgradeOrderPayment(w, notification)
|
||||
} else if strings.HasPrefix(orderNo, "W_") {
|
||||
// 白名单下架订单处理
|
||||
return l.handleWhitelistOrderPayment(w, notification)
|
||||
} else if strings.HasPrefix(orderNo, "A_") {
|
||||
// 旧系统会员充值订单(已废弃,新系统使用升级功能)
|
||||
// return l.handleAgentVipOrderPayment(w, notification)
|
||||
@@ -110,6 +113,96 @@ func (l *AlipayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter, not
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理白名单下架订单支付
|
||||
func (l *AlipayCallbackLogic) handleWhitelistOrderPayment(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. 查找白名单订单
|
||||
whitelistOrder, findWhitelistErr := l.svcCtx.WhitelistOrderModel.FindOneByOrderNo(l.ctx, orderNo)
|
||||
if findWhitelistErr != nil {
|
||||
logx.Errorf("支付宝白名单支付回调,查找白名单订单失败,订单号: %s, 错误: %+v", orderNo, findWhitelistErr)
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
if whitelistOrder.Status != 1 {
|
||||
// 白名单订单状态不是待支付,直接返回成功
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 5. 处理支付状态
|
||||
switch notification.TradeStatus {
|
||||
case alipay.TradeStatusSuccess:
|
||||
order.Status = "paid"
|
||||
order.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||
order.PlatformOrderId = lzUtils.StringToNullString(notification.TradeNo)
|
||||
whitelistOrder.Status = 2 // 已支付
|
||||
whitelistOrder.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||
case alipay.TradeStatusClosed:
|
||||
order.Status = "closed"
|
||||
order.CloseTime = lzUtils.TimeToNullTime(time.Now())
|
||||
whitelistOrder.Status = 3 // 已取消
|
||||
default:
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 6. 更新订单和白名单订单 + 创建白名单记录并删除报告数据
|
||||
err = l.svcCtx.WhitelistOrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 6.1 更新订单状态
|
||||
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(ctx, session, order); updateErr != nil {
|
||||
return errors.Wrapf(updateErr, "支付宝白名单支付回调,更新订单状态失败")
|
||||
}
|
||||
|
||||
// 6.2 更新白名单订单状态
|
||||
if updateErr := l.svcCtx.WhitelistOrderModel.UpdateWithVersion(ctx, session, whitelistOrder); updateErr != nil {
|
||||
return errors.Wrapf(updateErr, "支付宝白名单支付回调,更新白名单订单状态失败")
|
||||
}
|
||||
|
||||
// 6.3 如果支付成功,调用 WhitelistService 处理白名单和报告数据
|
||||
if whitelistOrder.Status == 2 {
|
||||
if processErr := l.svcCtx.WhitelistService.ProcessPaidWhitelistOrder(ctx, session, order, whitelistOrder); processErr != nil {
|
||||
return errors.Wrapf(processErr, "支付宝白名单支付回调,处理白名单订单失败")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
logx.Errorf("支付宝白名单支付回调,事务处理失败: %+v", err)
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理代理升级订单支付
|
||||
func (l *AlipayCallbackLogic) handleAgentUpgradeOrderPayment(w http.ResponseWriter, notification *alipay.Notification) error {
|
||||
orderNo := notification.OutTradeNo
|
||||
|
||||
240
app/main/api/internal/logic/pay/alipayfromlogic.go
Normal file
240
app/main/api/internal/logic/pay/alipayfromlogic.go
Normal file
@@ -0,0 +1,240 @@
|
||||
package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"ycc-server/app/main/api/internal/svc"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/smartwalle/alipay/v3"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AlipayFromLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAlipayFromLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AlipayFromLogic {
|
||||
return &AlipayFromLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// TradeComplainContent 交易投诉通知的 biz_content 内容
|
||||
type TradeComplainContent struct {
|
||||
ComplainEventID string `json:"complain_event_id"` // 支付宝侧投诉单号
|
||||
ComplainNotifyAppID string `json:"complain_notify_app_id"` // 投诉消息通知的应用ID
|
||||
|
||||
Status string `json:"status"` // 投诉单状态,可能的枚举值如下(用Go语法注释):
|
||||
/*
|
||||
MERCHANT_PROCESSING // 待处理
|
||||
MERCHANT_FEEDBACKED // 已处理
|
||||
FINISHED // 投诉完结
|
||||
CANCELLED // 投诉关闭
|
||||
PLATFORM_PROCESSING // 客服处理中
|
||||
PLATFORM_FINISH // 客服处理完结
|
||||
CLOSED // 投诉关闭
|
||||
*/
|
||||
BizType string `json:"biz_type"` // 业务类型
|
||||
OrderID string `json:"order_id"` // 订单ID
|
||||
}
|
||||
|
||||
// SecurityRiskComplaintsNotifyContent 安全风险投诉商户通知的 biz_content 内容
|
||||
type SecurityRiskComplaintsNotifyContent struct {
|
||||
ComplaintID string `json:"complaint_id"` // 投诉ID
|
||||
MessageType string `json:"message_type"` // 消息类型,可能的枚举值如下(用Go语法注释):
|
||||
/*
|
||||
USER_REPLY // 用户回复
|
||||
MERCHANT_REPLY // 商户回复
|
||||
SYSTEM_NOTIFY // 系统通知
|
||||
*/
|
||||
ReplyContent string `json:"reply_content"` // 回复内容
|
||||
ReplyTime string `json:"reply_time"` // 回复时间,格式:YYYY-MM-DD HH:mm:ss
|
||||
}
|
||||
|
||||
func (l *AlipayFromLogic) AlipayFrom(w http.ResponseWriter, r *http.Request) error {
|
||||
// 1. 解析表单
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
logx.Errorf("支付宝from消息回调,解析表单失败: %v", err)
|
||||
// 保存失败记录
|
||||
l.saveCallbackRecord(r, "", "", "failed", "解析表单失败: "+err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取基本信息
|
||||
appID := r.Form.Get("app_id")
|
||||
msgMethod := r.Form.Get("msg_method")
|
||||
|
||||
// 2. 验签
|
||||
client := l.svcCtx.AlipayService.AlipayClient
|
||||
if err := client.VerifySign(r.Form); err != nil {
|
||||
logx.Errorf("支付宝from消息回调,验签失败: %v", err)
|
||||
// 保存失败记录
|
||||
l.saveCallbackRecord(r, appID, msgMethod, "failed", "验签失败: "+err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
// 3. 验证 app_id
|
||||
if appID == "" {
|
||||
logx.Errorf("支付宝from消息回调,app_id为空")
|
||||
// 保存失败记录
|
||||
l.saveCallbackRecord(r, appID, msgMethod, "failed", "app_id为空")
|
||||
return nil
|
||||
}
|
||||
if appID != l.svcCtx.Config.Alipay.AppID {
|
||||
logx.Errorf("支付宝from消息回调,app_id不匹配,期望: %s, 实际: %s", l.svcCtx.Config.Alipay.AppID, appID)
|
||||
// 保存失败记录
|
||||
l.saveCallbackRecord(r, appID, msgMethod, "failed", "app_id不匹配")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 4. 获取 msg_method 判断业务类型
|
||||
if msgMethod == "" {
|
||||
logx.Errorf("支付宝from消息回调,msg_method为空")
|
||||
// 保存失败记录
|
||||
l.saveCallbackRecord(r, appID, msgMethod, "failed", "msg_method为空")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 5. 先保存回调记录(pending状态)
|
||||
callbackID := l.saveCallbackRecord(r, appID, msgMethod, "pending", "")
|
||||
if callbackID == "" {
|
||||
logx.Errorf("支付宝from消息回调,保存回调记录失败")
|
||||
// 即使保存失败也继续处理,避免影响业务
|
||||
}
|
||||
|
||||
// 6. 根据 msg_method 路由到对应的处理函数
|
||||
var handleErr error
|
||||
switch msgMethod {
|
||||
case "alipay.merchant.tradecomplain.changed":
|
||||
// 交易投诉通知回调
|
||||
handleErr = l.handleTradeComplainChanged(w, r, callbackID)
|
||||
case "alipay.security.risk.complaints.merchants.notify":
|
||||
// 安全风险投诉商户通知回调
|
||||
handleErr = l.handleSecurityRiskComplaintsNotify(w, r, callbackID)
|
||||
default:
|
||||
logx.Infof("支付宝from消息回调,未处理的msg_method: %s", msgMethod)
|
||||
// 更新为已处理(未处理的也标记为已处理)
|
||||
if callbackID != "" {
|
||||
l.updateCallbackStatus(callbackID, "processed", "")
|
||||
}
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 7. 根据处理结果更新状态
|
||||
if callbackID != "" {
|
||||
if handleErr != nil {
|
||||
l.updateCallbackStatus(callbackID, "failed", handleErr.Error())
|
||||
} else {
|
||||
l.updateCallbackStatus(callbackID, "processed", "")
|
||||
}
|
||||
}
|
||||
|
||||
return handleErr
|
||||
}
|
||||
|
||||
// saveCallbackRecord 保存回调记录到数据库
|
||||
func (l *AlipayFromLogic) saveCallbackRecord(r *http.Request, appID, msgMethod, status, errorMsg string) string {
|
||||
callback := &model.AlipayFromCallback{
|
||||
Id: uuid.NewString(),
|
||||
MsgMethod: msgMethod,
|
||||
AppId: appID,
|
||||
NotifyId: lzUtils.StringToNullString(r.Form.Get("notify_id")),
|
||||
BizContent: r.Form.Get("biz_content"),
|
||||
Status: status,
|
||||
ErrorMessage: lzUtils.StringToNullString(errorMsg),
|
||||
}
|
||||
|
||||
_, err := l.svcCtx.AlipayFromCallbackModel.Insert(l.ctx, nil, callback)
|
||||
if err != nil {
|
||||
logx.Errorf("保存支付宝from回调记录失败: %v", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return callback.Id
|
||||
}
|
||||
|
||||
// updateCallbackStatus 更新回调记录状态
|
||||
func (l *AlipayFromLogic) updateCallbackStatus(callbackID, status, errorMsg string) {
|
||||
callback, err := l.svcCtx.AlipayFromCallbackModel.FindOne(l.ctx, callbackID)
|
||||
if err != nil {
|
||||
logx.Errorf("查找支付宝from回调记录失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
callback.Status = status
|
||||
if errorMsg != "" {
|
||||
callback.ErrorMessage = lzUtils.StringToNullString(errorMsg)
|
||||
}
|
||||
|
||||
err = l.svcCtx.AlipayFromCallbackModel.UpdateWithVersion(l.ctx, nil, callback)
|
||||
if err != nil {
|
||||
logx.Errorf("更新支付宝from回调记录状态失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// handleTradeComplainChanged 处理交易投诉通知回调
|
||||
func (l *AlipayFromLogic) handleTradeComplainChanged(w http.ResponseWriter, r *http.Request, callbackID string) error {
|
||||
// 获取 biz_content
|
||||
bizContent := r.Form.Get("biz_content")
|
||||
if bizContent == "" {
|
||||
logx.Errorf("支付宝交易投诉通知回调,biz_content为空")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 解析 biz_content JSON
|
||||
var content TradeComplainContent
|
||||
if err := json.Unmarshal([]byte(bizContent), &content); err != nil {
|
||||
logx.Errorf("支付宝交易投诉通知回调,解析biz_content失败: %v, content: %s", err, bizContent)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
logx.Infof("支付宝交易投诉通知回调,投诉事件ID: %s, 订单ID: %s, 状态: %s, 业务类型: %s",
|
||||
content.ComplainEventID, content.OrderID, content.Status, content.BizType)
|
||||
|
||||
// TODO: 后续在这里添加具体的业务处理逻辑
|
||||
// 例如:根据订单ID查找订单,更新订单状态,发送通知等
|
||||
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleSecurityRiskComplaintsNotify 处理安全风险投诉商户通知回调
|
||||
func (l *AlipayFromLogic) handleSecurityRiskComplaintsNotify(w http.ResponseWriter, r *http.Request, callbackID string) error {
|
||||
// 获取 biz_content
|
||||
bizContent := r.Form.Get("biz_content")
|
||||
if bizContent == "" {
|
||||
logx.Errorf("支付宝安全风险投诉商户通知回调,biz_content为空")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 解析 biz_content JSON
|
||||
var content SecurityRiskComplaintsNotifyContent
|
||||
if err := json.Unmarshal([]byte(bizContent), &content); err != nil {
|
||||
logx.Errorf("支付宝安全风险投诉商户通知回调,解析biz_content失败: %v, content: %s", err, bizContent)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
logx.Infof("支付宝安全风险投诉商户通知回调,投诉ID: %s, 消息类型: %s, 回复内容: %s, 回复时间: %s",
|
||||
content.ComplaintID, content.MessageType, content.ReplyContent, content.ReplyTime)
|
||||
|
||||
// 根据投诉ID查询详情并更新投诉记录
|
||||
if err := l.svcCtx.AlipayComplaintService.QueryComplaintByTaskId(l.ctx, content.ComplaintID); err != nil {
|
||||
logx.Errorf("查询并更新投诉记录失败, complaint_id: %s, error: %v", content.ComplaintID, err)
|
||||
// 即使失败也返回成功,避免支付宝重复通知
|
||||
}
|
||||
|
||||
alipay.ACKNotification(w)
|
||||
return nil
|
||||
}
|
||||
@@ -38,7 +38,19 @@ func (l *PaymentCheckLogic) PaymentCheck(req *types.PaymentCheckReq) (resp *type
|
||||
Status: order.Status,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
if strings.HasPrefix(req.OrderNo, "W_") {
|
||||
// 白名单订单
|
||||
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: "whitelist",
|
||||
Status: order.Status,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 查询订单(包括代理订单)
|
||||
order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
|
||||
if err != nil {
|
||||
|
||||
@@ -72,6 +72,11 @@ func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "whitelist":
|
||||
paymentTypeResp, err = l.WhitelistOrderPayment(req, session)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 开发环境测试支付模式:跳过实际支付流程
|
||||
@@ -182,6 +187,37 @@ func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp,
|
||||
} else {
|
||||
logx.Infof("开发测试模式,代理升级成功,订单号: %s, 代理ID: %s", paymentTypeResp.outTradeNo, upgradeRecord.AgentId)
|
||||
}
|
||||
} else if strings.HasPrefix(paymentTypeResp.outTradeNo, "W_") {
|
||||
// 白名单订单:更新白名单订单状态并处理白名单记录
|
||||
whitelistOrder, findWhitelistErr := l.svcCtx.WhitelistOrderModel.FindOneByOrderNo(context.Background(), paymentTypeResp.outTradeNo)
|
||||
if findWhitelistErr != nil {
|
||||
logx.Errorf("开发测试模式,查找白名单订单失败,订单号: %s, 错误: %+v", paymentTypeResp.outTradeNo, findWhitelistErr)
|
||||
return
|
||||
}
|
||||
|
||||
// 更新白名单订单状态为已支付并处理白名单记录
|
||||
err := l.svcCtx.WhitelistOrderModel.Trans(context.Background(), func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 更新白名单订单状态为已支付
|
||||
whitelistOrder.Status = 2 // 已支付
|
||||
now := time.Now()
|
||||
whitelistOrder.PayTime = sql.NullTime{Time: now, Valid: true}
|
||||
if updateErr := l.svcCtx.WhitelistOrderModel.UpdateWithVersion(transCtx, session, whitelistOrder); updateErr != nil {
|
||||
return errors.Wrapf(updateErr, "更新白名单订单状态失败")
|
||||
}
|
||||
|
||||
// 调用 WhitelistService 处理白名单记录
|
||||
if processErr := l.svcCtx.WhitelistService.ProcessPaidWhitelistOrder(transCtx, session, order, whitelistOrder); processErr != nil {
|
||||
return errors.Wrapf(processErr, "处理白名单订单失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logx.Errorf("开发测试模式,处理白名单订单失败,订单号: %s, 错误: %+v", paymentTypeResp.outTradeNo, err)
|
||||
} else {
|
||||
logx.Infof("开发测试模式,白名单订单支付成功,订单号: %s", paymentTypeResp.outTradeNo)
|
||||
}
|
||||
} else {
|
||||
// 查询订单:发送支付成功通知任务,触发后续流程(生成报告和代理处理)
|
||||
if sendErr := l.svcCtx.AsynqService.SendQueryTask(finalOrderID); sendErr != nil {
|
||||
@@ -202,6 +238,181 @@ func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp,
|
||||
}
|
||||
}
|
||||
|
||||
// WhitelistOrderPayment 白名单下架支付订单
|
||||
// PaymentReq.Id 支持两种格式:
|
||||
// 1. "{idCard}|{featureApiId}" - 单个模块下架(创建新订单)
|
||||
// 2. 订单号(以 W_ 开头) - 批量订单支付(使用已存在的订单)
|
||||
func (l *PaymentLogic) WhitelistOrderPayment(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)
|
||||
}
|
||||
|
||||
// 判断是订单号还是 "{idCard}|{featureApiId}" 格式
|
||||
var whitelistOrder *model.WhitelistOrder
|
||||
var orderNo string
|
||||
var amount float64
|
||||
var description string
|
||||
|
||||
if strings.HasPrefix(req.Id, "W_") {
|
||||
// 格式2:订单号,使用已存在的批量订单
|
||||
orderNo = req.Id
|
||||
whitelistOrder, err = l.svcCtx.WhitelistOrderModel.FindOneByOrderNo(l.ctx, orderNo)
|
||||
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)
|
||||
}
|
||||
|
||||
// 验证订单状态和用户
|
||||
if whitelistOrder.Status != 1 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("订单状态不正确,无法支付"), "")
|
||||
}
|
||||
if whitelistOrder.UserId != userID {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("无权支付此订单"), "")
|
||||
}
|
||||
|
||||
amount = whitelistOrder.TotalAmount
|
||||
description = "模块屏蔽(批量)"
|
||||
|
||||
// 获取用户信息(用于内部用户测试金额)
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取用户信息失败: %v", err)
|
||||
}
|
||||
if user.Inside == 1 {
|
||||
amount = 0.01
|
||||
}
|
||||
|
||||
// 检查是否已存在支付订单(Order表)
|
||||
existingOrder, findErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo)
|
||||
if findErr == nil && existingOrder != nil {
|
||||
// 已存在支付订单,直接使用
|
||||
return &PaymentTypeResp{
|
||||
amount: amount,
|
||||
outTradeNo: orderNo,
|
||||
description: description,
|
||||
orderID: existingOrder.Id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 创建主订单记录(用于支付系统)
|
||||
order := model.Order{
|
||||
Id: uuid.NewString(),
|
||||
OrderNo: orderNo,
|
||||
UserId: userID,
|
||||
ProductId: "", // 白名单订单没有具体产品ID
|
||||
PaymentPlatform: req.PayMethod,
|
||||
PaymentScene: "app", // 白名单订单,使用简短标识
|
||||
Amount: amount,
|
||||
Status: "pending",
|
||||
}
|
||||
if _, insertOrderErr := l.svcCtx.OrderModel.Insert(l.ctx, session, &order); insertOrderErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建支付订单失败: %+v", insertOrderErr)
|
||||
}
|
||||
|
||||
return &PaymentTypeResp{
|
||||
amount: amount,
|
||||
outTradeNo: orderNo,
|
||||
description: description,
|
||||
orderID: order.Id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 格式1:"{idCard}|{featureApiId}" - 单个模块下架
|
||||
parts := strings.SplitN(req.Id, "|", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("参数错误:id 格式不正确,应为订单号(W_开头)或 {idCard}|{featureApiId}"), "")
|
||||
}
|
||||
idCard := parts[0]
|
||||
featureApiId := parts[1]
|
||||
|
||||
if idCard == "" || featureApiId == "" {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("参数错误:身份证号或模块标识为空"), "")
|
||||
}
|
||||
|
||||
// 查询 feature 信息
|
||||
feature, err := l.svcCtx.FeatureModel.FindOneByApiId(l.ctx, featureApiId)
|
||||
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)
|
||||
}
|
||||
|
||||
amount = feature.WhitelistPrice
|
||||
if amount <= 0 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("该模块无需付费下架"), "")
|
||||
}
|
||||
|
||||
// 获取用户信息(用于内部用户测试金额)
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成白名单订单, 获取用户信息失败: %v", err)
|
||||
}
|
||||
if user.Inside == 1 {
|
||||
amount = 0.01
|
||||
}
|
||||
|
||||
// 生成订单号(白名单订单前缀 W_,限制长度不超过32)
|
||||
base := l.svcCtx.AlipayService.GenerateOutTradeNo()
|
||||
outTradeNo := "W_" + base
|
||||
if len(outTradeNo) > 32 {
|
||||
outTradeNo = outTradeNo[:32]
|
||||
}
|
||||
|
||||
// 创建主订单记录(用于支付系统)
|
||||
order := model.Order{
|
||||
Id: uuid.NewString(),
|
||||
OrderNo: outTradeNo,
|
||||
UserId: userID,
|
||||
ProductId: "", // 白名单订单没有具体产品ID
|
||||
PaymentPlatform: req.PayMethod,
|
||||
PaymentScene: "app", // 白名单订单,使用简短标识
|
||||
Amount: amount,
|
||||
Status: "pending",
|
||||
}
|
||||
if _, insertOrderErr := l.svcCtx.OrderModel.Insert(l.ctx, session, &order); insertOrderErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成白名单订单, 保存订单失败: %+v", insertOrderErr)
|
||||
}
|
||||
|
||||
// 创建白名单订单(业务订单,状态=待支付)
|
||||
whitelistOrder = &model.WhitelistOrder{
|
||||
Id: uuid.NewString(),
|
||||
OrderNo: outTradeNo,
|
||||
UserId: userID,
|
||||
IdCard: idCard,
|
||||
TotalAmount: amount,
|
||||
Status: 1, // 待支付
|
||||
}
|
||||
if _, err := l.svcCtx.WhitelistOrderModel.Insert(l.ctx, session, whitelistOrder); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成白名单订单, 保存白名单订单失败: %+v", err)
|
||||
}
|
||||
|
||||
// 创建白名单订单明细
|
||||
orderItem := &model.WhitelistOrderItem{
|
||||
Id: uuid.NewString(),
|
||||
OrderId: whitelistOrder.Id,
|
||||
FeatureId: feature.Id,
|
||||
FeatureApiId: feature.ApiId,
|
||||
FeatureName: feature.Name,
|
||||
Price: amount,
|
||||
}
|
||||
if _, err := l.svcCtx.WhitelistOrderItemModel.Insert(l.ctx, session, orderItem); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成白名单订单, 保存白名单订单明细失败: %+v", err)
|
||||
}
|
||||
|
||||
description = fmt.Sprintf("模块下架:%s", feature.Name)
|
||||
|
||||
return &PaymentTypeResp{
|
||||
amount: amount,
|
||||
outTradeNo: outTradeNo,
|
||||
description: description,
|
||||
orderID: order.Id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Session) (resp *PaymentTypeResp, err error) {
|
||||
userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if getUidErr != nil {
|
||||
|
||||
@@ -45,6 +45,9 @@ func (l *WechatPayCallbackLogic) WechatPayCallback(w http.ResponseWriter, r *htt
|
||||
} else if strings.HasPrefix(orderNo, "U_") {
|
||||
// 代理升级订单处理
|
||||
return l.handleAgentUpgradeOrderPayment(w, notification)
|
||||
} else if strings.HasPrefix(orderNo, "W_") {
|
||||
// 白名单下架订单处理
|
||||
return l.handleWhitelistOrderPayment(w, notification)
|
||||
} else if strings.HasPrefix(orderNo, "A_") {
|
||||
// 旧系统会员充值订单(已废弃,新系统使用升级功能)
|
||||
// return l.handleAgentVipOrderPayment(w, notification)
|
||||
@@ -109,6 +112,91 @@ func (l *WechatPayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter,
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理白名单下架订单支付
|
||||
func (l *WechatPayCallbackLogic) handleWhitelistOrderPayment(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("微信白名单支付回调,金额不一致")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 3. 检查订单状态
|
||||
if order.Status != "pending" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 4. 查找白名单订单
|
||||
whitelistOrder, findWhitelistErr := l.svcCtx.WhitelistOrderModel.FindOneByOrderNo(l.ctx, orderNo)
|
||||
if findWhitelistErr != nil {
|
||||
logx.Errorf("微信白名单支付回调,查找白名单订单失败,订单号: %s, 错误: %+v", orderNo, findWhitelistErr)
|
||||
return nil
|
||||
}
|
||||
if whitelistOrder.Status != 1 {
|
||||
// 白名单订单状态不是待支付,直接返回成功
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 5. 处理支付状态
|
||||
switch *notification.TradeState {
|
||||
case service.TradeStateSuccess:
|
||||
order.Status = "paid"
|
||||
order.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||
order.PlatformOrderId = lzUtils.StringToNullString(*notification.TransactionId)
|
||||
whitelistOrder.Status = 2 // 已支付
|
||||
whitelistOrder.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||
case service.TradeStateClosed:
|
||||
order.Status = "closed"
|
||||
order.CloseTime = lzUtils.TimeToNullTime(time.Now())
|
||||
whitelistOrder.Status = 3 // 已取消
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
// 6. 更新订单和白名单订单 + 创建白名单记录并删除报告数据
|
||||
err := l.svcCtx.WhitelistOrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 6.1 更新订单状态
|
||||
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(ctx, session, order); updateErr != nil {
|
||||
return errors.Wrapf(updateErr, "微信白名单支付回调,更新订单状态失败")
|
||||
}
|
||||
|
||||
// 6.2 更新白名单订单状态
|
||||
if updateErr := l.svcCtx.WhitelistOrderModel.UpdateWithVersion(ctx, session, whitelistOrder); updateErr != nil {
|
||||
return errors.Wrapf(updateErr, "微信白名单支付回调,更新白名单订单状态失败")
|
||||
}
|
||||
|
||||
// 6.3 如果支付成功,调用 WhitelistService 处理白名单和报告数据
|
||||
if whitelistOrder.Status == 2 {
|
||||
if processErr := l.svcCtx.WhitelistService.ProcessPaidWhitelistOrder(ctx, session, order, whitelistOrder); processErr != nil {
|
||||
return errors.Wrapf(processErr, "微信白名单支付回调,处理白名单订单失败")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
logx.Errorf("微信白名单支付回调,事务处理失败: %+v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理代理升级订单支付
|
||||
func (l *WechatPayCallbackLogic) handleAgentUpgradeOrderPayment(w http.ResponseWriter, notification *payments.Transaction) error {
|
||||
orderNo := *notification.OutTradeNo
|
||||
|
||||
@@ -114,6 +114,21 @@ func (l *WechatPayRefundCallbackLogic) handleQueryOrderRefund(orderNo string, st
|
||||
return errors.Wrapf(err, "更新订单和退款记录失败: %s", orderNo)
|
||||
}
|
||||
|
||||
// 退款成功后,检查并处理代理订单(在事务外执行,避免影响退款流程)
|
||||
if status == refunddomestic.STATUS_SUCCESS {
|
||||
// 检查代理订单是否已处理
|
||||
agentOrder, err := l.svcCtx.AgentOrderModel.FindOneByOrderId(l.ctx, order.Id)
|
||||
if err == nil && agentOrder != nil && agentOrder.ProcessStatus == 1 {
|
||||
// 代理订单已处理,需要撤销收益
|
||||
if cancelErr := l.svcCtx.AgentService.CancelAgentCommission(l.ctx, order.Id); cancelErr != nil {
|
||||
logx.Errorf("撤销代理收益失败,订单ID: %s, 错误: %v", order.Id, cancelErr)
|
||||
// 不阻断退款流程,只记录日志(退款已成功,不能回滚)
|
||||
} else {
|
||||
logx.Infof("成功撤销代理收益,订单ID: %s", order.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user