package controller import ( "errors" "fmt" "net/http" "sync" "one-api/common" "one-api/common/config" "one-api/common/logger" "one-api/common/utils" "one-api/model" "one-api/payment" "one-api/payment/types" "github.com/gin-gonic/gin" ) type OrderRequest struct { UUID string `json:"uuid" binding:"required"` Amount int `json:"amount" binding:"required"` } type OrderResponse struct { TradeNo string `json:"trade_no"` *types.PayRequest } // CreateOrder func CreateOrder(c *gin.Context) { var orderReq OrderRequest if err := c.ShouldBindJSON(&orderReq); err != nil { common.APIRespondWithError(c, http.StatusOK, errors.New("invalid request")) return } if orderReq.Amount <= 0 || orderReq.Amount < config.PaymentMinAmount { common.APIRespondWithError(c, http.StatusOK, fmt.Errorf("金额必须大于等于 %d", config.PaymentMinAmount)) return } userId := c.GetInt("id") // 关闭用户未完成的订单 go model.CloseUnfinishedOrder() paymentService, err := payment.NewPaymentService(orderReq.UUID) if err != nil { common.APIRespondWithError(c, http.StatusOK, err) return } // 获取手续费和支付金额 fee, payMoney := calculateOrderAmount(paymentService.Payment, orderReq.Amount) // 开始支付 tradeNo := utils.GenerateTradeNo() payRequest, err := paymentService.Pay(tradeNo, payMoney) if err != nil { common.APIRespondWithError(c, http.StatusOK, errors.New("创建支付失败,请稍后再试")) return } // 创建订单 order := &model.Order{ UserId: userId, TradeNo: tradeNo, Amount: orderReq.Amount, OrderAmount: payMoney, OrderCurrency: paymentService.Payment.Currency, Fee: fee, Status: model.OrderStatusPending, Quota: orderReq.Amount * int(config.QuotaPerUnit), } err = order.Insert() if err != nil { common.APIRespondWithError(c, http.StatusOK, errors.New("创建订单失败,请稍后再试")) return } orderResp := &OrderResponse{ TradeNo: tradeNo, PayRequest: payRequest, } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", "data": orderResp, }) } // tradeNo lock var orderLocks sync.Map var createLock sync.Mutex // LockOrder 尝试对给定订单号加锁 func LockOrder(tradeNo string) { lock, ok := orderLocks.Load(tradeNo) if !ok { createLock.Lock() defer createLock.Unlock() lock, ok = orderLocks.Load(tradeNo) if !ok { lock = new(sync.Mutex) orderLocks.Store(tradeNo, lock) } } lock.(*sync.Mutex).Lock() } // UnlockOrder 释放给定订单号的锁 func UnlockOrder(tradeNo string) { lock, ok := orderLocks.Load(tradeNo) if ok { lock.(*sync.Mutex).Unlock() } } func PaymentCallback(c *gin.Context) { uuid := c.Param("uuid") paymentService, err := payment.NewPaymentService(uuid) if err != nil { common.APIRespondWithError(c, http.StatusOK, errors.New("payment not found")) return } payNotify, err := paymentService.HandleCallback(c, paymentService.Payment.Config) if err != nil { return } LockOrder(payNotify.GatewayNo) defer UnlockOrder(payNotify.GatewayNo) order, err := model.GetOrderByTradeNo(payNotify.TradeNo) if err != nil { logger.SysError(fmt.Sprintf("gateway callback failed to find order, trade_no: %s,", payNotify.TradeNo)) return } fmt.Println(order.Status, order.Status != model.OrderStatusPending) if order.Status != model.OrderStatusPending { return } order.GatewayNo = payNotify.GatewayNo order.Status = model.OrderStatusSuccess err = order.Update() if err != nil { logger.SysError(fmt.Sprintf("gateway callback failed to update order, trade_no: %s,", payNotify.TradeNo)) return } err = model.IncreaseUserQuota(order.UserId, order.Quota) if err != nil { logger.SysError(fmt.Sprintf("gateway callback failed to increase user quota, trade_no: %s,", payNotify.TradeNo)) return } model.RecordLog(order.UserId, model.LogTypeTopup, fmt.Sprintf("在线充值成功,充值quota: %d,支付金额:%.2f %s", order.Quota, order.OrderAmount, order.OrderCurrency)) } func CheckOrderStatus(c *gin.Context) { tradeNo := c.Query("trade_no") userId := c.GetInt("id") success := false if tradeNo != "" { order, err := model.GetUserOrder(userId, tradeNo) if err == nil { if order.Status == model.OrderStatusSuccess { success = true } } } c.JSON(http.StatusOK, gin.H{ "success": success, "message": "", }) } func calculateOrderAmount(payment *model.Payment, amount int) (fee, payMoney float64) { if payment.PercentFee > 0 { fee = utils.Decimal(float64(amount)*payment.PercentFee, 2) } else if payment.FixedFee > 0 { fee = payment.FixedFee } total := utils.Decimal(float64(amount)+fee, 2) if payment.Currency == model.CurrencyTypeUSD { payMoney = total } else { payMoney = utils.Decimal(total*config.PaymentUSDRate, 2) } return } func GetOrderList(c *gin.Context) { var params model.SearchOrderParams if err := c.ShouldBindQuery(¶ms); err != nil { common.APIRespondWithError(c, http.StatusOK, err) return } payments, err := model.GetOrderList(¶ms) if err != nil { common.APIRespondWithError(c, http.StatusOK, err) return } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", "data": payments, }) }