| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- 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/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{})
- } 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/subscription?pay=fail")
- return
- }
- 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")
- }
|