mirror of
https://github.com/linux-do/new-api.git
synced 2025-09-18 00:16:37 +08:00
243 lines
6.4 KiB
Go
243 lines
6.4 KiB
Go
package controller
|
||
|
||
import (
|
||
"fmt"
|
||
"github.com/Calcium-Ion/go-epay/epay"
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/samber/lo"
|
||
"log"
|
||
"net/url"
|
||
"one-api/common"
|
||
"one-api/constant"
|
||
"one-api/model"
|
||
"one-api/service"
|
||
"strconv"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
type EpayRequest struct {
|
||
Amount int `json:"amount"`
|
||
PaymentMethod string `json:"payment_method"`
|
||
TopUpCode string `json:"top_up_code"`
|
||
}
|
||
|
||
type AmountRequest struct {
|
||
Amount int `json:"amount"`
|
||
TopUpCode string `json:"top_up_code"`
|
||
}
|
||
|
||
func GetEpayClient() *epay.Client {
|
||
if constant.PayAddress == "" || constant.EpayId == "" || constant.EpayKey == "" {
|
||
return nil
|
||
}
|
||
withUrl, err := epay.NewClient(&epay.Config{
|
||
PartnerID: constant.EpayId,
|
||
Key: constant.EpayKey,
|
||
}, constant.PayAddress)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
return withUrl
|
||
}
|
||
|
||
func getPayMoney(amount float64, user model.User) float64 {
|
||
if !common.DisplayInCurrencyEnabled {
|
||
amount = amount / common.QuotaPerUnit
|
||
}
|
||
// 别问为什么用float64,问就是这么点钱没必要
|
||
topupGroupRatio := common.GetTopupGroupRatio(user.Group)
|
||
if topupGroupRatio == 0 {
|
||
topupGroupRatio = 1
|
||
}
|
||
payMoney := amount * constant.Price * topupGroupRatio
|
||
return payMoney
|
||
}
|
||
|
||
func getMinTopup() int {
|
||
minTopup := constant.MinTopUp
|
||
if !common.DisplayInCurrencyEnabled {
|
||
minTopup = minTopup * int(common.QuotaPerUnit)
|
||
}
|
||
return minTopup
|
||
}
|
||
|
||
func RequestEpay(c *gin.Context) {
|
||
var req EpayRequest
|
||
err := c.ShouldBindJSON(&req)
|
||
if err != nil {
|
||
c.JSON(200, gin.H{"message": "error", "data": "参数错误"})
|
||
return
|
||
}
|
||
if req.Amount < getMinTopup() {
|
||
c.JSON(200, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", getMinTopup())})
|
||
return
|
||
}
|
||
|
||
id := c.GetInt("id")
|
||
user, _ := model.GetUserById(id, false)
|
||
payMoney := getPayMoney(float64(req.Amount), *user)
|
||
if payMoney < 0.01 {
|
||
c.JSON(200, gin.H{"message": "error", "data": "充值金额过低"})
|
||
return
|
||
}
|
||
|
||
var payType epay.PurchaseType
|
||
if req.PaymentMethod == "zfb" {
|
||
payType = epay.Alipay
|
||
}
|
||
if req.PaymentMethod == "wx" {
|
||
req.PaymentMethod = "wxpay"
|
||
payType = epay.WechatPay
|
||
}
|
||
callBackAddress := service.GetCallbackAddress()
|
||
returnUrl, _ := url.Parse(constant.ServerAddress + "/log")
|
||
notifyUrl, _ := url.Parse(callBackAddress + "/api/user/epay/notify")
|
||
tradeNo := fmt.Sprintf("%s%d", common.GetRandomString(6), time.Now().Unix())
|
||
client := GetEpayClient()
|
||
if client == nil {
|
||
c.JSON(200, gin.H{"message": "error", "data": "当前管理员未配置支付信息"})
|
||
return
|
||
}
|
||
uri, params, err := client.Purchase(&epay.PurchaseArgs{
|
||
Type: payType,
|
||
ServiceTradeNo: "A" + tradeNo,
|
||
Name: "B" + tradeNo,
|
||
Money: strconv.FormatFloat(payMoney, 'f', 2, 64),
|
||
Device: epay.PC,
|
||
NotifyUrl: notifyUrl,
|
||
ReturnUrl: returnUrl,
|
||
})
|
||
if err != nil {
|
||
c.JSON(200, gin.H{"message": "error", "data": "拉起支付失败"})
|
||
return
|
||
}
|
||
amount := req.Amount
|
||
if !common.DisplayInCurrencyEnabled {
|
||
amount = amount / int(common.QuotaPerUnit)
|
||
}
|
||
topUp := &model.TopUp{
|
||
UserId: id,
|
||
Amount: amount,
|
||
Money: payMoney,
|
||
TradeNo: "A" + tradeNo,
|
||
CreateTime: time.Now().Unix(),
|
||
Status: "pending",
|
||
}
|
||
err = topUp.Insert()
|
||
if err != nil {
|
||
c.JSON(200, gin.H{"message": "error", "data": "创建订单失败"})
|
||
return
|
||
}
|
||
c.JSON(200, gin.H{"message": "success", "data": params, "url": uri})
|
||
}
|
||
|
||
// 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 EpayNotify(c *gin.Context) {
|
||
params := lo.Reduce(lo.Keys(c.Request.URL.Query()), func(r map[string]string, t string, i int) map[string]string {
|
||
r[t] = c.Request.URL.Query().Get(t)
|
||
return r
|
||
}, map[string]string{})
|
||
client := GetEpayClient()
|
||
if client == nil {
|
||
log.Println("易支付回调失败 未找到配置信息")
|
||
_, err := c.Writer.Write([]byte("fail"))
|
||
if err != nil {
|
||
log.Println("易支付回调写入失败")
|
||
return
|
||
}
|
||
}
|
||
verifyInfo, err := client.Verify(params)
|
||
if err == nil && verifyInfo.VerifyStatus {
|
||
_, err := c.Writer.Write([]byte("success"))
|
||
if err != nil {
|
||
log.Println("易支付回调写入失败")
|
||
}
|
||
} else {
|
||
_, err := c.Writer.Write([]byte("fail"))
|
||
if err != nil {
|
||
log.Println("易支付回调写入失败")
|
||
}
|
||
log.Println("易支付回调签名验证失败")
|
||
return
|
||
}
|
||
|
||
if verifyInfo.TradeStatus == epay.StatusTradeSuccess {
|
||
log.Println(verifyInfo)
|
||
LockOrder(verifyInfo.ServiceTradeNo)
|
||
defer UnlockOrder(verifyInfo.ServiceTradeNo)
|
||
topUp := model.GetTopUpByTradeNo(verifyInfo.ServiceTradeNo)
|
||
if topUp == nil {
|
||
log.Printf("易支付回调未找到订单: %v", verifyInfo)
|
||
return
|
||
}
|
||
if topUp.Status == "pending" {
|
||
topUp.Status = "success"
|
||
err := topUp.Update()
|
||
if err != nil {
|
||
log.Printf("易支付回调更新订单失败: %v", topUp)
|
||
return
|
||
}
|
||
//user, _ := model.GetUserById(topUp.UserId, false)
|
||
//user.Quota += topUp.Amount * 500000
|
||
err = model.IncreaseUserQuota(topUp.UserId, topUp.Amount*int(common.QuotaPerUnit))
|
||
if err != nil {
|
||
log.Printf("易支付回调更新用户失败: %v", topUp)
|
||
return
|
||
}
|
||
log.Printf("易支付回调更新用户成功 %v", topUp)
|
||
model.RecordLog(topUp.UserId, model.LogTypeTopup, fmt.Sprintf("使用在线充值成功,充值金额: %v,支付金额:%f", common.LogQuota(topUp.Amount*int(common.QuotaPerUnit)), topUp.Money))
|
||
}
|
||
} else {
|
||
log.Printf("易支付异常回调: %v", verifyInfo)
|
||
}
|
||
}
|
||
|
||
func RequestAmount(c *gin.Context) {
|
||
var req AmountRequest
|
||
err := c.ShouldBindJSON(&req)
|
||
if err != nil {
|
||
c.JSON(200, gin.H{"message": "error", "data": "参数错误"})
|
||
return
|
||
}
|
||
|
||
if req.Amount < getMinTopup() {
|
||
c.JSON(200, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", getMinTopup())})
|
||
return
|
||
}
|
||
id := c.GetInt("id")
|
||
user, _ := model.GetUserById(id, false)
|
||
payMoney := getPayMoney(float64(req.Amount), *user)
|
||
if payMoney <= 0.01 {
|
||
c.JSON(200, gin.H{"message": "error", "data": "充值金额过低"})
|
||
return
|
||
}
|
||
c.JSON(200, gin.H{"message": "success", "data": strconv.FormatFloat(payMoney, 'f', 2, 64)})
|
||
}
|