subscription_payment_epay.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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. PaymentProvider: model.PaymentProviderEpay,
  81. CreateTime: time.Now().Unix(),
  82. Status: common.TopUpStatusPending,
  83. }
  84. if err := order.Insert(); err != nil {
  85. common.ApiErrorMsg(c, "创建订单失败")
  86. return
  87. }
  88. uri, params, err := client.Purchase(&epay.PurchaseArgs{
  89. Type: req.PaymentMethod,
  90. ServiceTradeNo: tradeNo,
  91. Name: fmt.Sprintf("SUB:%s", plan.Title),
  92. Money: strconv.FormatFloat(plan.PriceAmount, 'f', 2, 64),
  93. Device: epay.PC,
  94. NotifyUrl: notifyUrl,
  95. ReturnUrl: returnUrl,
  96. })
  97. if err != nil {
  98. _ = model.ExpireSubscriptionOrder(tradeNo, model.PaymentProviderEpay)
  99. common.ApiErrorMsg(c, "拉起支付失败")
  100. return
  101. }
  102. c.JSON(http.StatusOK, gin.H{"message": "success", "data": params, "url": uri})
  103. }
  104. func SubscriptionEpayNotify(c *gin.Context) {
  105. var params map[string]string
  106. if c.Request.Method == "POST" {
  107. // POST 请求:从 POST body 解析参数
  108. if err := c.Request.ParseForm(); err != nil {
  109. _, _ = c.Writer.Write([]byte("fail"))
  110. return
  111. }
  112. params = lo.Reduce(lo.Keys(c.Request.PostForm), func(r map[string]string, t string, i int) map[string]string {
  113. r[t] = c.Request.PostForm.Get(t)
  114. return r
  115. }, map[string]string{})
  116. } else {
  117. // GET 请求:从 URL Query 解析参数
  118. params = lo.Reduce(lo.Keys(c.Request.URL.Query()), func(r map[string]string, t string, i int) map[string]string {
  119. r[t] = c.Request.URL.Query().Get(t)
  120. return r
  121. }, map[string]string{})
  122. }
  123. if len(params) == 0 {
  124. _, _ = c.Writer.Write([]byte("fail"))
  125. return
  126. }
  127. client := GetEpayClient()
  128. if client == nil {
  129. _, _ = c.Writer.Write([]byte("fail"))
  130. return
  131. }
  132. verifyInfo, err := client.Verify(params)
  133. if err != nil || !verifyInfo.VerifyStatus {
  134. _, _ = c.Writer.Write([]byte("fail"))
  135. return
  136. }
  137. if verifyInfo.TradeStatus != epay.StatusTradeSuccess {
  138. _, _ = c.Writer.Write([]byte("fail"))
  139. return
  140. }
  141. LockOrder(verifyInfo.ServiceTradeNo)
  142. defer UnlockOrder(verifyInfo.ServiceTradeNo)
  143. if err := model.CompleteSubscriptionOrder(verifyInfo.ServiceTradeNo, common.GetJsonString(verifyInfo), model.PaymentProviderEpay, verifyInfo.Type); err != nil {
  144. _, _ = c.Writer.Write([]byte("fail"))
  145. return
  146. }
  147. _, _ = c.Writer.Write([]byte("success"))
  148. }
  149. // SubscriptionEpayReturn handles browser return after payment.
  150. // It verifies the payload and completes the order, then redirects to console.
  151. func SubscriptionEpayReturn(c *gin.Context) {
  152. var params map[string]string
  153. if c.Request.Method == "POST" {
  154. // POST 请求:从 POST body 解析参数
  155. if err := c.Request.ParseForm(); err != nil {
  156. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=fail")
  157. return
  158. }
  159. params = lo.Reduce(lo.Keys(c.Request.PostForm), func(r map[string]string, t string, i int) map[string]string {
  160. r[t] = c.Request.PostForm.Get(t)
  161. return r
  162. }, map[string]string{})
  163. } else {
  164. // GET 请求:从 URL Query 解析参数
  165. params = lo.Reduce(lo.Keys(c.Request.URL.Query()), func(r map[string]string, t string, i int) map[string]string {
  166. r[t] = c.Request.URL.Query().Get(t)
  167. return r
  168. }, map[string]string{})
  169. }
  170. if len(params) == 0 {
  171. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=fail")
  172. return
  173. }
  174. client := GetEpayClient()
  175. if client == nil {
  176. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=fail")
  177. return
  178. }
  179. verifyInfo, err := client.Verify(params)
  180. if err != nil || !verifyInfo.VerifyStatus {
  181. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=fail")
  182. return
  183. }
  184. if verifyInfo.TradeStatus == epay.StatusTradeSuccess {
  185. LockOrder(verifyInfo.ServiceTradeNo)
  186. defer UnlockOrder(verifyInfo.ServiceTradeNo)
  187. if err := model.CompleteSubscriptionOrder(verifyInfo.ServiceTradeNo, common.GetJsonString(verifyInfo), model.PaymentProviderEpay, verifyInfo.Type); err != nil {
  188. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=fail")
  189. return
  190. }
  191. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=success")
  192. return
  193. }
  194. c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=pending")
  195. }