mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-03-30 04:20:47 +00:00
217 lines
6.2 KiB
Go
217 lines
6.2 KiB
Go
package controller
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/Calcium-Ion/go-epay/epay"
|
|
"github.com/QuantumNous/new-api/common"
|
|
"github.com/QuantumNous/new-api/model"
|
|
"github.com/QuantumNous/new-api/service"
|
|
"github.com/QuantumNous/new-api/setting/operation_setting"
|
|
"github.com/QuantumNous/new-api/setting/system_setting"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
type SubscriptionEpayPayRequest struct {
|
|
PlanId int `json:"plan_id"`
|
|
PaymentMethod string `json:"payment_method"`
|
|
}
|
|
|
|
func SubscriptionRequestEpay(c *gin.Context) {
|
|
var req SubscriptionEpayPayRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil || req.PlanId <= 0 {
|
|
common.ApiErrorMsg(c, "参数错误")
|
|
return
|
|
}
|
|
|
|
plan, err := model.GetSubscriptionPlanById(req.PlanId)
|
|
if err != nil {
|
|
common.ApiError(c, err)
|
|
return
|
|
}
|
|
if !plan.Enabled {
|
|
common.ApiErrorMsg(c, "套餐未启用")
|
|
return
|
|
}
|
|
if plan.PriceAmount < 0.01 {
|
|
common.ApiErrorMsg(c, "套餐金额过低")
|
|
return
|
|
}
|
|
if !operation_setting.ContainsPayMethod(req.PaymentMethod) {
|
|
common.ApiErrorMsg(c, "支付方式不存在")
|
|
return
|
|
}
|
|
|
|
userId := c.GetInt("id")
|
|
if plan.MaxPurchasePerUser > 0 {
|
|
count, err := model.CountUserSubscriptionsByPlan(userId, plan.Id)
|
|
if err != nil {
|
|
common.ApiError(c, err)
|
|
return
|
|
}
|
|
if count >= int64(plan.MaxPurchasePerUser) {
|
|
common.ApiErrorMsg(c, "已达到该套餐购买上限")
|
|
return
|
|
}
|
|
}
|
|
|
|
callBackAddress := service.GetCallbackAddress()
|
|
returnUrl, err := url.Parse(callBackAddress + "/api/subscription/epay/return")
|
|
if err != nil {
|
|
common.ApiErrorMsg(c, "回调地址配置错误")
|
|
return
|
|
}
|
|
notifyUrl, err := url.Parse(callBackAddress + "/api/subscription/epay/notify")
|
|
if err != nil {
|
|
common.ApiErrorMsg(c, "回调地址配置错误")
|
|
return
|
|
}
|
|
|
|
tradeNo := fmt.Sprintf("%s%d", common.GetRandomString(6), time.Now().Unix())
|
|
tradeNo = fmt.Sprintf("SUBUSR%dNO%s", userId, tradeNo)
|
|
|
|
client := GetEpayClient()
|
|
if client == nil {
|
|
common.ApiErrorMsg(c, "当前管理员未配置支付信息")
|
|
return
|
|
}
|
|
|
|
order := &model.SubscriptionOrder{
|
|
UserId: userId,
|
|
PlanId: plan.Id,
|
|
Money: plan.PriceAmount,
|
|
TradeNo: tradeNo,
|
|
PaymentMethod: req.PaymentMethod,
|
|
CreateTime: time.Now().Unix(),
|
|
Status: common.TopUpStatusPending,
|
|
}
|
|
if err := order.Insert(); err != nil {
|
|
common.ApiErrorMsg(c, "创建订单失败")
|
|
return
|
|
}
|
|
uri, params, err := client.Purchase(&epay.PurchaseArgs{
|
|
Type: req.PaymentMethod,
|
|
ServiceTradeNo: tradeNo,
|
|
Name: fmt.Sprintf("SUB:%s", plan.Title),
|
|
Money: strconv.FormatFloat(plan.PriceAmount, 'f', 2, 64),
|
|
Device: epay.PC,
|
|
NotifyUrl: notifyUrl,
|
|
ReturnUrl: returnUrl,
|
|
})
|
|
if err != nil {
|
|
_ = model.ExpireSubscriptionOrder(tradeNo)
|
|
common.ApiErrorMsg(c, "拉起支付失败")
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"message": "success", "data": params, "url": uri})
|
|
}
|
|
|
|
func SubscriptionEpayNotify(c *gin.Context) {
|
|
var params map[string]string
|
|
|
|
if c.Request.Method == "POST" {
|
|
// POST 请求:从 POST body 解析参数
|
|
if err := c.Request.ParseForm(); err != nil {
|
|
_, _ = c.Writer.Write([]byte("fail"))
|
|
return
|
|
}
|
|
params = lo.Reduce(lo.Keys(c.Request.PostForm), func(r map[string]string, t string, i int) map[string]string {
|
|
r[t] = c.Request.PostForm.Get(t)
|
|
return r
|
|
}, map[string]string{})
|
|
} else {
|
|
// GET 请求:从 URL Query 解析参数
|
|
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{})
|
|
}
|
|
|
|
if len(params) == 0 {
|
|
_, _ = c.Writer.Write([]byte("fail"))
|
|
return
|
|
}
|
|
|
|
client := GetEpayClient()
|
|
if client == nil {
|
|
_, _ = c.Writer.Write([]byte("fail"))
|
|
return
|
|
}
|
|
verifyInfo, err := client.Verify(params)
|
|
if err != nil || !verifyInfo.VerifyStatus {
|
|
_, _ = c.Writer.Write([]byte("fail"))
|
|
return
|
|
}
|
|
|
|
if verifyInfo.TradeStatus != epay.StatusTradeSuccess {
|
|
_, _ = c.Writer.Write([]byte("fail"))
|
|
return
|
|
}
|
|
|
|
LockOrder(verifyInfo.ServiceTradeNo)
|
|
defer UnlockOrder(verifyInfo.ServiceTradeNo)
|
|
|
|
if err := model.CompleteSubscriptionOrder(verifyInfo.ServiceTradeNo, common.GetJsonString(verifyInfo)); err != nil {
|
|
_, _ = c.Writer.Write([]byte("fail"))
|
|
return
|
|
}
|
|
|
|
_, _ = c.Writer.Write([]byte("success"))
|
|
}
|
|
|
|
// SubscriptionEpayReturn handles browser return after payment.
|
|
// It verifies the payload and completes the order, then redirects to console.
|
|
func SubscriptionEpayReturn(c *gin.Context) {
|
|
var params map[string]string
|
|
|
|
if c.Request.Method == "POST" {
|
|
// POST 请求:从 POST body 解析参数
|
|
if err := c.Request.ParseForm(); err != nil {
|
|
c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=fail")
|
|
return
|
|
}
|
|
params = lo.Reduce(lo.Keys(c.Request.PostForm), func(r map[string]string, t string, i int) map[string]string {
|
|
r[t] = c.Request.PostForm.Get(t)
|
|
return r
|
|
}, map[string]string{})
|
|
} else {
|
|
// GET 请求:从 URL Query 解析参数
|
|
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{})
|
|
}
|
|
|
|
if len(params) == 0 {
|
|
c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=fail")
|
|
return
|
|
}
|
|
|
|
client := GetEpayClient()
|
|
if client == nil {
|
|
c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=fail")
|
|
return
|
|
}
|
|
verifyInfo, err := client.Verify(params)
|
|
if err != nil || !verifyInfo.VerifyStatus {
|
|
c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=fail")
|
|
return
|
|
}
|
|
if verifyInfo.TradeStatus == epay.StatusTradeSuccess {
|
|
LockOrder(verifyInfo.ServiceTradeNo)
|
|
defer UnlockOrder(verifyInfo.ServiceTradeNo)
|
|
if err := model.CompleteSubscriptionOrder(verifyInfo.ServiceTradeNo, common.GetJsonString(verifyInfo)); err != nil {
|
|
c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=fail")
|
|
return
|
|
}
|
|
c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=success")
|
|
return
|
|
}
|
|
c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=pending")
|
|
}
|