No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 

222 líneas
6.3 KiB

  1. package controller
  2. import (
  3. "fmt"
  4. "net/http"
  5. "net/url"
  6. "strconv"
  7. "time"
  8. "github.com/Calcium-Ion/go-epay/epay"
  9. "github.com/QuantumNous/new-api/common"
  10. "github.com/QuantumNous/new-api/model"
  11. "github.com/QuantumNous/new-api/service"
  12. "github.com/QuantumNous/new-api/setting/operation_setting"
  13. "github.com/QuantumNous/new-api/setting/system_setting"
  14. "github.com/gin-gonic/gin"
  15. "github.com/samber/lo"
  16. )
  17. type SubscriptionEpayPayRequest struct {
  18. PlanId int `json:"plan_id"`
  19. PaymentMethod string `json:"payment_method"`
  20. }
  21. func SubscriptionRequestEpay(c *gin.Context) {
  22. var req SubscriptionEpayPayRequest
  23. if err := c.ShouldBindJSON(&req); err != nil || req.PlanId <= 0 {
  24. common.ApiErrorMsg(c, "参数错误")
  25. return
  26. }
  27. plan, err := model.GetSubscriptionPlanById(req.PlanId)
  28. if err != nil {
  29. common.ApiError(c, err)
  30. return
  31. }
  32. if !plan.Enabled {
  33. common.ApiErrorMsg(c, "套餐未启用")
  34. return
  35. }
  36. if plan.PriceAmount < 0.01 {
  37. common.ApiErrorMsg(c, "套餐金额过低")
  38. return
  39. }
  40. if !operation_setting.ContainsPayMethod(req.PaymentMethod) {
  41. common.ApiErrorMsg(c, "支付方式不存在")
  42. return
  43. }
  44. userId := c.GetInt("id")
  45. if plan.MaxPurchasePerUser > 0 {
  46. count, err := model.CountUserSubscriptionsByPlan(userId, plan.Id)
  47. if err != nil {
  48. common.ApiError(c, err)
  49. return
  50. }
  51. if count >= int64(plan.MaxPurchasePerUser) {
  52. common.ApiErrorMsg(c, "已达到该套餐购买上限")
  53. return
  54. }
  55. }
  56. callBackAddress := service.GetCallbackAddress()
  57. returnUrl, err := url.Parse(callBackAddress + "/api/subscription/epay/return")
  58. if err != nil {
  59. common.ApiErrorMsg(c, "回调地址配置错误")
  60. return
  61. }
  62. notifyUrl, err := url.Parse(callBackAddress + "/api/subscription/epay/notify")
  63. if err != nil {
  64. common.ApiErrorMsg(c, "回调地址配置错误")
  65. return
  66. }
  67. tradeNo := fmt.Sprintf("%s%d", common.GetRandomString(6), time.Now().Unix())
  68. tradeNo = fmt.Sprintf("SUBUSR%dNO%s", userId, tradeNo)
  69. client := GetEpayClient()
  70. if client == nil {
  71. common.ApiErrorMsg(c, "当前管理员未配置支付信息")
  72. return
  73. }
  74. order := &model.SubscriptionOrder{
  75. UserId: userId,
  76. PlanId: plan.Id,
  77. Money: plan.PriceAmount,
  78. TradeNo: tradeNo,
  79. PaymentMethod: req.PaymentMethod,
  80. CreateTime: time.Now().Unix(),
  81. Status: common.TopUpStatusPending,
  82. }
  83. if err := order.Insert(); err != nil {
  84. common.ApiErrorMsg(c, "创建订单失败")
  85. return
  86. }
  87. uri, params, err := client.Purchase(&epay.PurchaseArgs{
  88. Type: req.PaymentMethod,
  89. ServiceTradeNo: tradeNo,
  90. Name: fmt.Sprintf("SUB:%s", plan.Title),
  91. Money: strconv.FormatFloat(plan.PriceAmount, 'f', 2, 64),
  92. Device: epay.PC,
  93. NotifyUrl: notifyUrl,
  94. ReturnUrl: returnUrl,
  95. })
  96. if err != nil {
  97. _ = model.ExpireSubscriptionOrder(tradeNo)
  98. common.ApiErrorMsg(c, "拉起支付失败")
  99. return
  100. }
  101. c.JSON(http.StatusOK, gin.H{"message": "success", "data": params, "url": uri})
  102. }
  103. func SubscriptionEpayNotify(c *gin.Context) {
  104. if !isEpayWebhookEnabled() {
  105. _, _ = c.Writer.Write([]byte("fail"))
  106. return
  107. }
  108. var params map[string]string
  109. if c.Request.Method == "POST" {
  110. // POST 请求:从 POST body 解析参数
  111. if err := c.Request.ParseForm(); err != nil {
  112. _, _ = c.Writer.Write([]byte("fail"))
  113. return
  114. }
  115. params = lo.Reduce(lo.Keys(c.Request.PostForm), func(r map[string]string, t string, i int) map[string]string {
  116. r[t] = c.Request.PostForm.Get(t)
  117. return r
  118. }, map[string]string{})
  119. } else {
  120. // GET 请求:从 URL Query 解析参数
  121. params = lo.Reduce(lo.Keys(c.Request.URL.Query()), func(r map[string]string, t string, i int) map[string]string {
  122. r[t] = c.Request.URL.Query().Get(t)
  123. return r
  124. }, map[string]string{})
  125. }
  126. if len(params) == 0 {
  127. _, _ = c.Writer.Write([]byte("fail"))
  128. return
  129. }
  130. client := GetEpayClient()
  131. if client == nil {
  132. _, _ = c.Writer.Write([]byte("fail"))
  133. return
  134. }
  135. verifyInfo, err := client.Verify(params)
  136. if err != nil || !verifyInfo.VerifyStatus {
  137. _, _ = c.Writer.Write([]byte("fail"))
  138. return
  139. }
  140. if verifyInfo.TradeStatus != epay.StatusTradeSuccess {
  141. _, _ = c.Writer.Write([]byte("fail"))
  142. return
  143. }
  144. LockOrder(verifyInfo.ServiceTradeNo)
  145. defer UnlockOrder(verifyInfo.ServiceTradeNo)
  146. if err := model.CompleteSubscriptionOrder(verifyInfo.ServiceTradeNo, common.GetJsonString(verifyInfo)); err != nil {
  147. _, _ = c.Writer.Write([]byte("fail"))
  148. return
  149. }
  150. _, _ = c.Writer.Write([]byte("success"))
  151. }
  152. // SubscriptionEpayReturn handles browser return after payment.
  153. // It verifies the payload and completes the order, then redirects to console.
  154. func SubscriptionEpayReturn(c *gin.Context) {
  155. var params map[string]string
  156. if c.Request.Method == "POST" {
  157. // POST 请求:从 POST body 解析参数
  158. if err := c.Request.ParseForm(); err != nil {
  159. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=fail")
  160. return
  161. }
  162. params = lo.Reduce(lo.Keys(c.Request.PostForm), func(r map[string]string, t string, i int) map[string]string {
  163. r[t] = c.Request.PostForm.Get(t)
  164. return r
  165. }, map[string]string{})
  166. } else {
  167. // GET 请求:从 URL Query 解析参数
  168. params = lo.Reduce(lo.Keys(c.Request.URL.Query()), func(r map[string]string, t string, i int) map[string]string {
  169. r[t] = c.Request.URL.Query().Get(t)
  170. return r
  171. }, map[string]string{})
  172. }
  173. if len(params) == 0 {
  174. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=fail")
  175. return
  176. }
  177. client := GetEpayClient()
  178. if client == nil {
  179. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=fail")
  180. return
  181. }
  182. verifyInfo, err := client.Verify(params)
  183. if err != nil || !verifyInfo.VerifyStatus {
  184. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=fail")
  185. return
  186. }
  187. if verifyInfo.TradeStatus == epay.StatusTradeSuccess {
  188. LockOrder(verifyInfo.ServiceTradeNo)
  189. defer UnlockOrder(verifyInfo.ServiceTradeNo)
  190. if err := model.CompleteSubscriptionOrder(verifyInfo.ServiceTradeNo, common.GetJsonString(verifyInfo)); err != nil {
  191. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=fail")
  192. return
  193. }
  194. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=success")
  195. return
  196. }
  197. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=pending")
  198. }