| 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" { |
| |
| 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 { |
| |
| 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")) |
| } |
|
|
| |
| |
| func SubscriptionEpayReturn(c *gin.Context) { |
| var params map[string]string |
|
|
| if c.Request.Method == "POST" { |
| |
| 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 { |
| |
| 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") |
| } |
|
|