mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-04-18 17:27:28 +00:00
Return subscription epay pay success responses via ApiSuccess to include the consistent success field and align with error schema.
197 lines
5.7 KiB
Go
197 lines
5.7 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
|
|
}
|
|
common.ApiSuccess(c, gin.H{"data": params, "url": uri})
|
|
}
|
|
|
|
func SubscriptionEpayNotify(c *gin.Context) {
|
|
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{})
|
|
if len(params) == 0 {
|
|
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 {
|
|
_, _ = 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) {
|
|
if err := c.Request.ParseForm(); err != nil {
|
|
c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?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{})
|
|
if len(params) == 0 {
|
|
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 {
|
|
c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=fail")
|
|
return
|
|
}
|
|
verifyInfo, err := client.Verify(params)
|
|
if err != nil || !verifyInfo.VerifyStatus {
|
|
c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?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/subscription?pay=fail")
|
|
return
|
|
}
|
|
c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=success")
|
|
return
|
|
}
|
|
c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=pending")
|
|
}
|