package controller import ( "bytes" "io" "log" "time" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/setting" "github.com/QuantumNous/new-api/setting/operation_setting" "github.com/gin-gonic/gin" "github.com/thanhpk/randstr" ) type SubscriptionCreemPayRequest struct { PlanId int `json:"plan_id"` } func SubscriptionRequestCreemPay(c *gin.Context) { var req SubscriptionCreemPayRequest // Keep body for debugging consistency (like RequestCreemPay) bodyBytes, err := io.ReadAll(c.Request.Body) if err != nil { log.Printf("read subscription creem pay req body err: %v", err) c.JSON(200, gin.H{"message": "error", "data": "read query error"}) return } c.Request.Body = io.NopCloser(bytes.NewReader(bodyBytes)) if err := c.ShouldBindJSON(&req); err != nil || req.PlanId <= 0 { c.JSON(200, gin.H{"message": "error", "data": "参数错误"}) return } plan, err := model.GetSubscriptionPlanById(req.PlanId) if err != nil { common.ApiError(c, err) return } if !plan.Enabled { common.ApiErrorMsg(c, "套餐未启用") return } if plan.CreemProductId == "" { common.ApiErrorMsg(c, "该套餐未配置 CreemProductId") return } if setting.CreemWebhookSecret == "" && !setting.CreemTestMode { common.ApiErrorMsg(c, "Creem Webhook 未配置") return } userId := c.GetInt("id") user, err := model.GetUserById(userId, false) if err != nil { common.ApiError(c, err) return } if user == nil { common.ApiErrorMsg(c, "用户不存在") return } 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 } } reference := "sub-creem-ref-" + randstr.String(6) referenceId := "sub_ref_" + common.Sha1([]byte(reference+time.Now().String()+user.Username)) // create pending order first order := &model.SubscriptionOrder{ UserId: userId, PlanId: plan.Id, Money: plan.PriceAmount, TradeNo: referenceId, PaymentMethod: PaymentMethodCreem, CreateTime: time.Now().Unix(), Status: common.TopUpStatusPending, } if err := order.Insert(); err != nil { c.JSON(200, gin.H{"message": "error", "data": "创建订单失败"}) return } // Reuse Creem checkout generator by building a lightweight product reference. currency := "USD" switch operation_setting.GetGeneralSetting().QuotaDisplayType { case operation_setting.QuotaDisplayTypeCNY: currency = "CNY" case operation_setting.QuotaDisplayTypeUSD: currency = "USD" default: currency = "USD" } product := &CreemProduct{ ProductId: plan.CreemProductId, Name: plan.Title, Price: plan.PriceAmount, Currency: currency, Quota: 0, } checkoutUrl, err := genCreemLink(referenceId, product, user.Email, user.Username) if err != nil { log.Printf("获取Creem支付链接失败: %v", err) c.JSON(200, gin.H{"message": "error", "data": "拉起支付失败"}) return } c.JSON(200, gin.H{ "message": "success", "data": gin.H{ "checkout_url": checkoutUrl, "order_id": referenceId, }, }) }