mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-04-18 11:47:28 +00:00
Apply consistent code formatting across the entire codebase using gofmt and lint:fix tools. This ensures adherence to Go community standards and improves code readability and maintainability. Changes include: - Run gofmt on all .go files to standardize formatting - Apply lint:fix to automatically resolve linting issues - Fix code style inconsistencies and formatting violations No functional changes were made in this commit.
154 lines
4.5 KiB
Go
154 lines
4.5 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")
|
|
|
|
callBackAddress := service.GetCallbackAddress()
|
|
returnUrl, _ := url.Parse(callBackAddress + "/api/subscription/epay/return")
|
|
notifyUrl, _ := url.Parse(callBackAddress + "/api/subscription/epay/notify")
|
|
|
|
tradeNo := fmt.Sprintf("%s%d", common.GetRandomString(6), time.Now().Unix())
|
|
tradeNo = fmt.Sprintf("SUBUSR%dNO%s", userId, tradeNo)
|
|
|
|
client := GetEpayClient()
|
|
if client == nil {
|
|
c.JSON(http.StatusOK, gin.H{"message": "error", "data": "当前管理员未配置支付信息"})
|
|
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 {
|
|
c.JSON(http.StatusOK, gin.H{"message": "error", "data": "拉起支付失败"})
|
|
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 {
|
|
c.JSON(http.StatusOK, gin.H{"message": "error", "data": "创建订单失败"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"message": "success", "data": params, "url": uri})
|
|
}
|
|
|
|
func SubscriptionEpayNotify(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 {
|
|
_, _ = c.Writer.Write([]byte("fail"))
|
|
return
|
|
}
|
|
verifyInfo, err := client.Verify(params)
|
|
if err == nil && verifyInfo.VerifyStatus {
|
|
_, _ = c.Writer.Write([]byte("success"))
|
|
} else {
|
|
_, _ = c.Writer.Write([]byte("fail"))
|
|
return
|
|
}
|
|
|
|
if verifyInfo.TradeStatus != epay.StatusTradeSuccess {
|
|
return
|
|
}
|
|
|
|
LockOrder(verifyInfo.ServiceTradeNo)
|
|
defer UnlockOrder(verifyInfo.ServiceTradeNo)
|
|
|
|
if err := model.CompleteSubscriptionOrder(verifyInfo.ServiceTradeNo, jsonString(verifyInfo)); err != nil {
|
|
// do not fail webhook response after signature verified
|
|
return
|
|
}
|
|
}
|
|
|
|
// SubscriptionEpayReturn handles browser return after payment.
|
|
// It verifies the payload and completes the order, then redirects to console.
|
|
func SubscriptionEpayReturn(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 {
|
|
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)
|
|
_ = model.CompleteSubscriptionOrder(verifyInfo.ServiceTradeNo, jsonString(verifyInfo))
|
|
c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=success")
|
|
return
|
|
}
|
|
c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=pending")
|
|
}
|