package service import ( "context" "database/sql" "fmt" "net/http" "time" "tyc-server/app/main/api/internal/config" "tyc-server/app/main/model" "tyc-server/common/ctxdata" "tyc-server/pkg/core/payment" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/stores/sqlx" ) // PayService 支付服务 type PayService struct { config config.Config orderModel model.OrderModel orderRefundModel model.OrderRefundModel productModel model.ProductModel userAuthModel model.UserAuthModel userModel model.UserModel asyncService *AsynqService alipayService *payment.AliPayService wechatService *payment.WechatPayService appleService *payment.ApplePayService } // NewPayService 创建支付服务 func NewPayService(c config.Config, orderModel model.OrderModel, orderRefundModel model.OrderRefundModel, productModel model.ProductModel, userAuthModel model.UserAuthModel, userModel model.UserModel, asyncService *AsynqService) *PayService { ps := &PayService{ config: c, orderModel: orderModel, orderRefundModel: orderRefundModel, productModel: productModel, userAuthModel: userAuthModel, userModel: userModel, asyncService: asyncService, } // 根据配置选择性初始化支付服务 if c.Alipay.Enabled { // 初始化支付宝服务 alipayConfig := payment.AlipayConfig{ AppID: c.Alipay.AppID, PrivateKey: c.Alipay.PrivateKey, AlipayPublicKey: c.Alipay.AlipayPublicKey, IsProduction: c.Alipay.IsProduction, NotifyURL: c.Alipay.NotifyUrl, ReturnURL: c.Alipay.ReturnURL, } alipayService, err := payment.NewAliPayService(alipayConfig) if err != nil { panic(fmt.Sprintf("初始化支付宝服务失败: %v", err)) } ps.alipayService = alipayService } if c.Wxpay.Enabled { // 初始化微信支付服务 wechatConfig := &payment.WechatPayConfig{ AppID: c.Wxpay.AppID, MchID: c.Wxpay.MchID, SerialNo: c.Wxpay.MchCertificateSerialNumber, MchAPIv3Key: c.Wxpay.MchApiv3Key, PrivateKey: c.Wxpay.MchPrivateKeyPath, NotifyURL: c.Wxpay.NotifyUrl, RefundURL: c.Wxpay.RefundNotifyUrl, } wechatService, err := payment.NewWechatPayService(wechatConfig) if err != nil { panic(fmt.Sprintf("初始化微信支付服务失败: %v", err)) } ps.wechatService = wechatService } if c.Applepay.Enabled { // 初始化苹果支付服务 appleConfig := payment.ApplePayConfig{ BundleID: c.Applepay.BundleID, KeyID: c.Applepay.KeyID, TeamID: c.Applepay.IssuerID, PrivateKey: c.Applepay.LoadPrivateKeyPath, IsProduction: !c.Applepay.Sandbox, NotifyURL: c.Applepay.ProductionVerifyURL, RefundNotifyURL: c.Applepay.SandboxVerifyURL, } appleService, err := payment.NewApplePayService(appleConfig) if err != nil { panic(fmt.Sprintf("初始化苹果支付服务失败: %v", err)) } ps.appleService = appleService } return ps } // CreateOrder 创建支付订单 func (p *PayService) CreateOrder(ctx context.Context, req *CreateOrderRequest) (*CreateOrderResponse, error) { platform, ok := ctx.Value("platform").(string) if !ok { return nil, fmt.Errorf("获取平台失败") } userID, err := ctxdata.GetUidFromCtx(ctx) if err != nil { return nil, fmt.Errorf("获取用户信息失败: %v", err) } product, err := p.productModel.FindOneByProductEn(ctx, req.ProductEn) if err != nil { return nil, fmt.Errorf("获取商品失败: %v", err) } user, err := p.userModel.FindOne(ctx, userID) if err != nil { return nil, fmt.Errorf("获取用户失败: %v", err) } outTradeNo := req.OrderNo var orderID int64 amount := product.SellPrice if user.Inside == 1 { amount = 0.01 } // 使用事务创建订单 var payData interface{} err = p.orderModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error { // 根据支付方式创建支付订单 switch req.PayMethod { case "alipay": orderReq := &payment.AlipayCreateOrderRequest{ Amount: amount, Subject: product.ProductName, Platform: payment.PlatformH5, OutTradeNo: outTradeNo, } resp, err := p.alipayService.CreateOrder(ctx, orderReq) if err != nil { return fmt.Errorf("创建支付宝订单失败: %v", err) } payData = resp.PayParams case "wechat": orderReq := &payment.Order{ OutTradeNo: outTradeNo, Subject: product.ProductName, Amount: amount, Platform: payment.PlatformApp, NotifyURL: p.config.Wxpay.NotifyUrl, } // 如果是小程序支付,需要获取用户的 OpenID if platform == "mp-weixin" { userAuth, err := p.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWechatMiniOpenID) if err != nil { return fmt.Errorf("获取用户认证信息失败: %v", err) } orderReq.OpenID = userAuth.AuthKey } resp, err := p.wechatService.CreateOrder(orderReq) if err != nil { return fmt.Errorf("创建微信支付订单失败: %v", err) } payData = resp.PayParams outTradeNo = resp.OrderNo // case "apple": // orderReq := &payment.AppleCreateOrderRequest{ // Amount: amount, // Subject: product.ProductName, // OutTradeNo: outTradeNo, // Platform: payment.PlatformApp, // NotifyURL: p.config.Applepay.ProductionVerifyURL, // } // resp, err := p.appleService.CreateOrder(ctx, orderReq) // if err != nil { // return fmt.Errorf("创建苹果支付订单失败: %v", err) // } // payData = resp.PayParams default: return fmt.Errorf("不支持的支付方式: %s", req.PayMethod) } // 创建订单记录 order := &model.Order{ OrderNo: outTradeNo, UserId: userID, ProductId: product.Id, PaymentPlatform: string(req.PayMethod), PaymentScene: platform, Amount: amount, Status: model.OrderStatusPending, } result, err := p.orderModel.Insert(ctx, session, order) if err != nil { return fmt.Errorf("创建订单记录失败: %v", err) } orderID, err = result.LastInsertId() if err != nil { return fmt.Errorf("获取订单ID失败: %v", err) } return nil }) if err != nil { return nil, err } return &CreateOrderResponse{ OrderNo: outTradeNo, PayData: payData, PayMethod: req.PayMethod, OrderID: orderID, }, nil } // HandlePaymentCallback 处理支付回调 func (p *PayService) HandlePaymentCallback(ctx context.Context, payType string, req *http.Request) error { return p.orderModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error { var payOrder *payment.QueryOrderResponse var err error switch payType { case "alipay": payOrder, err = p.handleAlipayCallback(ctx, req, session) case "wechat": payOrder, err = p.handleWechatCallback(ctx, req, session) case "apple": payOrder, err = p.handleAppleCallback(ctx, req, session) default: return fmt.Errorf("不支持的支付类型: %s", payType) } if err != nil { return err } if payOrder == nil { return fmt.Errorf("支付订单信息为空") } // 查询订单 order, err := p.orderModel.FindOneByOrderNo(ctx, payOrder.OrderNo) if err != nil { return fmt.Errorf("查询订单失败: %v", err) } // 验证订单金额 if payOrder.Amount != order.Amount { return fmt.Errorf("订单金额不匹配") } // 根据支付状态更新订单状态 switch payOrder.Status { case payment.StatusSuccess: order.Status = model.OrderStatusPaid case payment.StatusFailed: order.Status = model.OrderStatusFailed case payment.StatusClosed: order.Status = model.OrderStatusClosed case payment.StatusRefunded: order.Status = model.OrderStatusRefunding default: return fmt.Errorf("不支持的支付状态: %s", payOrder.Status) } // 更新订单状态 order.PlatformOrderId = sql.NullString{String: payOrder.TransactionID, Valid: true} _, err = p.orderModel.Update(ctx, session, order) if err != nil { return fmt.Errorf("更新订单状态失败: %v", err) } if asyncErr := p.asyncService.SendQueryTask(order.Id); asyncErr != nil { logx.Errorf("异步任务调度失败: %v", asyncErr) return asyncErr } return nil }) } // handleAlipayCallback 处理支付宝回调 func (p *PayService) handleAlipayCallback(ctx context.Context, req *http.Request, session sqlx.Session) (*payment.QueryOrderResponse, error) { return p.alipayService.HandlePaymentNotification(req) } // handleWechatCallback 处理微信支付回调 func (p *PayService) handleWechatCallback(ctx context.Context, req *http.Request, session sqlx.Session) (*payment.QueryOrderResponse, error) { notification, err := p.wechatService.HandlePaymentNotification(req) if err != nil { return nil, fmt.Errorf("处理微信支付回调失败: %v", err) } // 将微信支付通知转换为统一的查询订单响应格式 return &payment.QueryOrderResponse{ OrderNo: notification.OrderNo, TransactionID: notification.TransactionID, Status: notification.Status, Amount: notification.Amount, PayTime: notification.PayTime, PayMethod: payment.MethodWechat, }, nil } // handleAppleCallback 处理苹果支付回调 func (p *PayService) handleAppleCallback(ctx context.Context, req *http.Request, session sqlx.Session) (*payment.QueryOrderResponse, error) { receipt := req.FormValue("receipt-data") if receipt == "" { return nil, fmt.Errorf("无效的苹果支付收据") } verifyResp, err := p.appleService.VerifyReceipt(ctx, receipt) if err != nil { return nil, fmt.Errorf("验证苹果支付收据失败: %v", err) } if verifyResp.Status != payment.StatusSuccess { return nil, fmt.Errorf("苹果支付验证失败,状态码: %s", verifyResp.Status) } // 将苹果支付验证响应转换为统一的查询订单响应格式 return &payment.QueryOrderResponse{ OrderNo: verifyResp.OrderNo, TransactionID: verifyResp.TransactionID, Status: verifyResp.Status, Amount: verifyResp.Amount, PayTime: time.Now(), PayMethod: payment.MethodApple, }, nil } // Refund 申请退款 func (p *PayService) Refund(ctx context.Context, req *RefundRequest) (*RefundResponse, error) { // 查询订单信息 order, err := p.orderModel.FindOne(ctx, req.OrderId) if err != nil { return nil, fmt.Errorf("查询订单失败: %v", err) } // 验证订单状态 if order.Status != model.OrderStatusPaid { return nil, fmt.Errorf("订单状态为 %s,无法退款", order.Status) } if req.RefundAmount > order.Amount { return nil, fmt.Errorf("退款金额 %.2f 不能大于订单金额 %.2f", req.RefundAmount, order.Amount) } var result refundResult result.status = model.OrderRefundStatusPending order.Status = model.OrderStatusRefunding // 使用事务处理退款流程 err = p.orderModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error { // 根据支付方式申请退款 switch order.PaymentPlatform { case string(PayMethodAlipay): refundReq := &payment.AlipayRefundRequest{ OrderNo: order.OrderNo, RefundAmount: req.RefundAmount, } resp, err := p.alipayService.Refund(ctx, refundReq) if err != nil { return fmt.Errorf("支付宝退款申请失败: %v", err) } result = refundResult{ refundNo: resp.RefundNo, status: model.OrderRefundStatusSuccess, refundTime: resp.RefundTime, refundAmount: resp.RefundAmount, platformRefundId: sql.NullString{String: resp.RefundID, Valid: true}, } order.Status = model.OrderStatusRefund default: return fmt.Errorf("不支持的支付平台退款: %s", order.PaymentPlatform) } // 创建退款记录 refund := &model.OrderRefund{ RefundNo: result.refundNo, PlatformRefundId: result.platformRefundId, OrderId: order.Id, UserId: order.UserId, ProductId: order.ProductId, RefundAmount: result.refundAmount, RefundReason: sql.NullString{String: req.Reason, Valid: true}, Status: result.status, RefundTime: sql.NullTime{Time: result.refundTime, Valid: !result.refundTime.IsZero()}, } if _, err := p.orderRefundModel.Insert(ctx, session, refund); err != nil { return fmt.Errorf("创建退款记录失败: %v", err) } // 更新订单状态 order.Status = model.OrderStatusRefund order.RefundTime = sql.NullTime{Time: result.refundTime, Valid: !result.refundTime.IsZero()} if _, err := p.orderModel.Update(ctx, session, order); err != nil { return fmt.Errorf("更新订单状态失败: %v", err) } return nil }) if err != nil { return nil, err } return &RefundResponse{ RefundNo: result.refundNo, Amount: result.refundAmount, Status: result.status, }, nil } // QueryOrderStatus 查询订单状态 func (p *PayService) QueryOrderStatus(ctx context.Context, orderNo string) (*OrderStatusResponse, error) { // 查询订单信息 order, err := p.orderModel.FindOneByOrderNo(ctx, orderNo) if err != nil { return nil, fmt.Errorf("查询订单失败: %v", err) } // 根据支付方式查询支付状态 var payStatus payment.PaymentStatus switch order.PaymentPlatform { case "alipay": queryReq := &payment.QueryOrderRequest{ OrderNo: orderNo, } resp, err := p.alipayService.QueryOrder(ctx, queryReq) if err != nil { return nil, fmt.Errorf("查询支付宝订单状态失败: %v", err) } payStatus = resp.Status // case "wechat": // queryReq := &payment.OrderQuery{ // OrderNo: orderNo, // } // resp, err := p.wechatService.QueryOrder(queryReq) // if err != nil { // return nil, fmt.Errorf("查询微信支付订单状态失败: %v", err) // } // payStatus = resp.Status // case "apple": // return nil, fmt.Errorf("苹果支付暂不支持主动查询订单状态") default: return nil, fmt.Errorf("不支持的支付方式: %s", order.PaymentPlatform) } return &OrderStatusResponse{ OrderNo: orderNo, PayMethod: PayMethod(order.PaymentPlatform), PayStatus: string(payStatus), OrderStatus: order.Status, Amount: order.Amount, }, nil } type PayMethod string const ( PayMethodAlipay PayMethod = "alipay" PayMethodWechat PayMethod = "wechat" PayMethodApple PayMethod = "apple" ) // 请求和响应结构体定义 type CreateOrderRequest struct { PayMethod PayMethod `json:"pay_method"` // 支付方式:alipay, wechat, apple ProductEn string `json:"product_en"` // 商品英文名 OrderNo string `json:"order_no"` // 订单号 } type CreateOrderResponse struct { OrderNo string `json:"order_no"` // 订单号 PayData interface{} `json:"pay_data"` // 支付数据 PayMethod PayMethod `json:"pay_type"` // 支付方式 OrderID int64 `json:"order_id"` // 订单ID } type RefundRequest struct { OrderId int64 `json:"order_id"` // 订单ID RefundAmount float64 `json:"refund_amount"` // 退款金额 Reason string `json:"reason"` // 退款原因 } // 定义退款结果结构 type refundResult struct { refundNo string status string refundTime time.Time refundAmount float64 platformRefundId sql.NullString } type RefundResponse struct { RefundNo string `json:"refund_no"` // 退款单号 Amount float64 `json:"amount"` // 退款金额 Status string `json:"status"` // 退款状态 } type OrderStatusResponse struct { OrderNo string `json:"order_no"` // 订单号 PayMethod PayMethod `json:"pay_type"` // 支付方式 PayStatus string `json:"pay_status"` // 支付状态 OrderStatus string `json:"order_status"` // 订单状态 Amount float64 `json:"amount"` // 订单金额 }