File size: 3,284 Bytes
daa8246 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | 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,
},
})
}
|