subscription_payment_epay.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  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. common.ApiSuccess(c, gin.H{"data": params, "url": uri})
  102. }
  103. func SubscriptionEpayNotify(c *gin.Context) {
  104. if err := c.Request.ParseForm(); err != nil {
  105. _, _ = c.Writer.Write([]byte("fail"))
  106. return
  107. }
  108. params := lo.Reduce(lo.Keys(c.Request.PostForm), func(r map[string]string, t string, i int) map[string]string {
  109. r[t] = c.Request.PostForm.Get(t)
  110. return r
  111. }, map[string]string{})
  112. if len(params) == 0 {
  113. params = lo.Reduce(lo.Keys(c.Request.URL.Query()), func(r map[string]string, t string, i int) map[string]string {
  114. r[t] = c.Request.URL.Query().Get(t)
  115. return r
  116. }, map[string]string{})
  117. }
  118. client := GetEpayClient()
  119. if client == nil {
  120. _, _ = c.Writer.Write([]byte("fail"))
  121. return
  122. }
  123. verifyInfo, err := client.Verify(params)
  124. if err != nil || !verifyInfo.VerifyStatus {
  125. _, _ = c.Writer.Write([]byte("fail"))
  126. return
  127. }
  128. if verifyInfo.TradeStatus != epay.StatusTradeSuccess {
  129. _, _ = c.Writer.Write([]byte("fail"))
  130. return
  131. }
  132. LockOrder(verifyInfo.ServiceTradeNo)
  133. defer UnlockOrder(verifyInfo.ServiceTradeNo)
  134. if err := model.CompleteSubscriptionOrder(verifyInfo.ServiceTradeNo, common.GetJsonString(verifyInfo)); err != nil {
  135. _, _ = c.Writer.Write([]byte("fail"))
  136. return
  137. }
  138. _, _ = c.Writer.Write([]byte("success"))
  139. }
  140. // SubscriptionEpayReturn handles browser return after payment.
  141. // It verifies the payload and completes the order, then redirects to console.
  142. func SubscriptionEpayReturn(c *gin.Context) {
  143. if err := c.Request.ParseForm(); err != nil {
  144. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=fail")
  145. return
  146. }
  147. params := lo.Reduce(lo.Keys(c.Request.PostForm), func(r map[string]string, t string, i int) map[string]string {
  148. r[t] = c.Request.PostForm.Get(t)
  149. return r
  150. }, map[string]string{})
  151. if len(params) == 0 {
  152. params = lo.Reduce(lo.Keys(c.Request.URL.Query()), func(r map[string]string, t string, i int) map[string]string {
  153. r[t] = c.Request.URL.Query().Get(t)
  154. return r
  155. }, map[string]string{})
  156. }
  157. client := GetEpayClient()
  158. if client == nil {
  159. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=fail")
  160. return
  161. }
  162. verifyInfo, err := client.Verify(params)
  163. if err != nil || !verifyInfo.VerifyStatus {
  164. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=fail")
  165. return
  166. }
  167. if verifyInfo.TradeStatus == epay.StatusTradeSuccess {
  168. LockOrder(verifyInfo.ServiceTradeNo)
  169. defer UnlockOrder(verifyInfo.ServiceTradeNo)
  170. if err := model.CompleteSubscriptionOrder(verifyInfo.ServiceTradeNo, common.GetJsonString(verifyInfo)); err != nil {
  171. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=fail")
  172. return
  173. }
  174. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=success")
  175. return
  176. }
  177. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/subscription?pay=pending")
  178. }