subscription_payment_creem.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. package controller
  2. import (
  3. "bytes"
  4. "io"
  5. "log"
  6. "time"
  7. "github.com/QuantumNous/new-api/common"
  8. "github.com/QuantumNous/new-api/model"
  9. "github.com/QuantumNous/new-api/setting"
  10. "github.com/QuantumNous/new-api/setting/operation_setting"
  11. "github.com/gin-gonic/gin"
  12. "github.com/thanhpk/randstr"
  13. )
  14. type SubscriptionCreemPayRequest struct {
  15. PlanId int `json:"plan_id"`
  16. }
  17. func SubscriptionRequestCreemPay(c *gin.Context) {
  18. var req SubscriptionCreemPayRequest
  19. // Keep body for debugging consistency (like RequestCreemPay)
  20. bodyBytes, err := io.ReadAll(c.Request.Body)
  21. if err != nil {
  22. log.Printf("read subscription creem pay req body err: %v", err)
  23. c.JSON(200, gin.H{"message": "error", "data": "read query error"})
  24. return
  25. }
  26. c.Request.Body = io.NopCloser(bytes.NewReader(bodyBytes))
  27. if err := c.ShouldBindJSON(&req); err != nil || req.PlanId <= 0 {
  28. c.JSON(200, gin.H{"message": "error", "data": "参数错误"})
  29. return
  30. }
  31. plan, err := model.GetSubscriptionPlanById(req.PlanId)
  32. if err != nil {
  33. common.ApiError(c, err)
  34. return
  35. }
  36. if !plan.Enabled {
  37. common.ApiErrorMsg(c, "套餐未启用")
  38. return
  39. }
  40. if plan.CreemProductId == "" {
  41. common.ApiErrorMsg(c, "该套餐未配置 CreemProductId")
  42. return
  43. }
  44. if setting.CreemWebhookSecret == "" && !setting.CreemTestMode {
  45. common.ApiErrorMsg(c, "Creem Webhook 未配置")
  46. return
  47. }
  48. userId := c.GetInt("id")
  49. user, err := model.GetUserById(userId, false)
  50. if err != nil {
  51. common.ApiError(c, err)
  52. return
  53. }
  54. if user == nil {
  55. common.ApiErrorMsg(c, "用户不存在")
  56. return
  57. }
  58. if plan.MaxPurchasePerUser > 0 {
  59. count, err := model.CountUserSubscriptionsByPlan(userId, plan.Id)
  60. if err != nil {
  61. common.ApiError(c, err)
  62. return
  63. }
  64. if count >= int64(plan.MaxPurchasePerUser) {
  65. common.ApiErrorMsg(c, "已达到该套餐购买上限")
  66. return
  67. }
  68. }
  69. reference := "sub-creem-ref-" + randstr.String(6)
  70. referenceId := "sub_ref_" + common.Sha1([]byte(reference+time.Now().String()+user.Username))
  71. // create pending order first
  72. order := &model.SubscriptionOrder{
  73. UserId: userId,
  74. PlanId: plan.Id,
  75. Money: plan.PriceAmount,
  76. TradeNo: referenceId,
  77. PaymentMethod: PaymentMethodCreem,
  78. CreateTime: time.Now().Unix(),
  79. Status: common.TopUpStatusPending,
  80. }
  81. if err := order.Insert(); err != nil {
  82. c.JSON(200, gin.H{"message": "error", "data": "创建订单失败"})
  83. return
  84. }
  85. // Reuse Creem checkout generator by building a lightweight product reference.
  86. currency := "USD"
  87. switch operation_setting.GetGeneralSetting().QuotaDisplayType {
  88. case operation_setting.QuotaDisplayTypeCNY:
  89. currency = "CNY"
  90. case operation_setting.QuotaDisplayTypeUSD:
  91. currency = "USD"
  92. default:
  93. currency = "USD"
  94. }
  95. product := &CreemProduct{
  96. ProductId: plan.CreemProductId,
  97. Name: plan.Title,
  98. Price: plan.PriceAmount,
  99. Currency: currency,
  100. Quota: 0,
  101. }
  102. checkoutUrl, err := genCreemLink(referenceId, product, user.Email, user.Username)
  103. if err != nil {
  104. log.Printf("获取Creem支付链接失败: %v", err)
  105. c.JSON(200, gin.H{"message": "error", "data": "拉起支付失败"})
  106. return
  107. }
  108. c.JSON(200, gin.H{
  109. "message": "success",
  110. "data": gin.H{
  111. "checkout_url": checkoutUrl,
  112. "order_id": referenceId,
  113. },
  114. })
  115. }