mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-09-29 14:46:38 +08:00
224 lines
5.2 KiB
Go
224 lines
5.2 KiB
Go
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,
|
||
})
|
||
}
|