| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- package controller
- import (
- "fmt"
- "log"
- "net/url"
- "one-api/common"
- "one-api/model"
- "one-api/service"
- "one-api/setting"
- "strconv"
- "sync"
- "time"
- "github.com/Calcium-Ion/go-epay/epay"
- "github.com/gin-gonic/gin"
- "github.com/samber/lo"
- "github.com/shopspring/decimal"
- )
- type EpayRequest struct {
- Amount int64 `json:"amount"`
- PaymentMethod string `json:"payment_method"`
- TopUpCode string `json:"top_up_code"`
- }
- type AmountRequest struct {
- Amount int64 `json:"amount"`
- TopUpCode string `json:"top_up_code"`
- }
- func GetEpayClient() *epay.Client {
- if setting.PayAddress == "" || setting.EpayId == "" || setting.EpayKey == "" {
- return nil
- }
- withUrl, err := epay.NewClient(&epay.Config{
- PartnerID: setting.EpayId,
- Key: setting.EpayKey,
- }, setting.PayAddress)
- if err != nil {
- return nil
- }
- return withUrl
- }
- func getPayMoney(amount int64, group string) float64 {
- dAmount := decimal.NewFromInt(amount)
- if !common.DisplayInCurrencyEnabled {
- dQuotaPerUnit := decimal.NewFromFloat(common.QuotaPerUnit)
- dAmount = dAmount.Div(dQuotaPerUnit)
- }
- topupGroupRatio := common.GetTopupGroupRatio(group)
- if topupGroupRatio == 0 {
- topupGroupRatio = 1
- }
- dTopupGroupRatio := decimal.NewFromFloat(topupGroupRatio)
- dPrice := decimal.NewFromFloat(setting.Price)
- payMoney := dAmount.Mul(dPrice).Mul(dTopupGroupRatio)
- return payMoney.InexactFloat64()
- }
- func getMinTopup() int64 {
- minTopup := setting.MinTopUp
- if !common.DisplayInCurrencyEnabled {
- dMinTopup := decimal.NewFromInt(int64(minTopup))
- dQuotaPerUnit := decimal.NewFromFloat(common.QuotaPerUnit)
- minTopup = int(dMinTopup.Mul(dQuotaPerUnit).IntPart())
- }
- return int64(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")
- group, err := model.GetUserGroup(id, true)
- if err != nil {
- c.JSON(200, gin.H{"message": "error", "data": "获取用户分组失败"})
- return
- }
- payMoney := getPayMoney(req.Amount, group)
- if payMoney < 0.01 {
- c.JSON(200, gin.H{"message": "error", "data": "充值金额过低"})
- return
- }
- if !setting.ContainsPayMethod(req.PaymentMethod) {
- c.JSON(200, gin.H{"message": "error", "data": "支付方式不存在"})
- return
- }
- callBackAddress := service.GetCallbackAddress()
- returnUrl, _ := url.Parse(setting.ServerAddress + "/console/log")
- notifyUrl, _ := url.Parse(callBackAddress + "/api/user/epay/notify")
- tradeNo := fmt.Sprintf("%s%d", common.GetRandomString(6), time.Now().Unix())
- tradeNo = fmt.Sprintf("USR%dNO%s", id, tradeNo)
- client := GetEpayClient()
- if client == nil {
- c.JSON(200, gin.H{"message": "error", "data": "当前管理员未配置支付信息"})
- return
- }
- uri, params, err := client.Purchase(&epay.PurchaseArgs{
- Type: req.PaymentMethod,
- ServiceTradeNo: tradeNo,
- Name: fmt.Sprintf("TUC%d", req.Amount),
- 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 {
- dAmount := decimal.NewFromInt(int64(amount))
- dQuotaPerUnit := decimal.NewFromFloat(common.QuotaPerUnit)
- amount = dAmount.Div(dQuotaPerUnit).IntPart()
- }
- topUp := &model.TopUp{
- UserId: id,
- Amount: amount,
- Money: payMoney,
- TradeNo: 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
- dAmount := decimal.NewFromInt(int64(topUp.Amount))
- dQuotaPerUnit := decimal.NewFromFloat(common.QuotaPerUnit)
- quotaToAdd := int(dAmount.Mul(dQuotaPerUnit).IntPart())
- err = model.IncreaseUserQuota(topUp.UserId, quotaToAdd, true)
- if err != nil {
- log.Printf("易支付回调更新用户失败: %v", topUp)
- return
- }
- log.Printf("易支付回调更新用户成功 %v", topUp)
- model.RecordLog(topUp.UserId, model.LogTypeTopup, fmt.Sprintf("使用在线充值成功,充值金额: %v,支付金额:%f", common.LogQuota(quotaToAdd), 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")
- group, err := model.GetUserGroup(id, true)
- if err != nil {
- c.JSON(200, gin.H{"message": "error", "data": "获取用户分组失败"})
- return
- }
- payMoney := getPayMoney(req.Amount, group)
- 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)})
- }
|