diff --git a/controller/option.go b/controller/option.go index d42db346..4849bcc6 100644 --- a/controller/option.go +++ b/controller/option.go @@ -3,9 +3,11 @@ package controller import ( "fmt" "net/http" + "strconv" "strings" "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/i18n" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/setting" "github.com/QuantumNous/new-api/setting/console_setting" @@ -27,6 +29,19 @@ var completionRatioMetaOptionKeys = []string{ "AudioCompletionRatio", } +func isPaymentComplianceOptionKey(key string) bool { + return strings.HasPrefix(key, "payment_setting.compliance_") +} + +func isPositiveOptionValue(value string) bool { + intValue, err := strconv.Atoi(strings.TrimSpace(value)) + if err == nil { + return intValue > 0 + } + floatValue, err := strconv.ParseFloat(strings.TrimSpace(value), 64) + return err == nil && floatValue > 0 +} + func isVisiblePublicKeyOption(key string) bool { switch key { case "WaffoPancakeWebhookPublicKey", "WaffoPancakeWebhookTestKey": @@ -104,7 +119,6 @@ func GetOptions(c *gin.Context) { "message": "", "data": options, }) - return } type OptionUpdateRequest struct { @@ -133,6 +147,18 @@ func UpdateOption(c *gin.Context) { option.Value = fmt.Sprintf("%v", option.Value) } switch option.Key { + case "QuotaForInviter", "QuotaForInvitee": + if isPositiveOptionValue(option.Value.(string)) && !operation_setting.IsPaymentComplianceConfirmed() { + common.ApiErrorI18n(c, i18n.MsgPaymentComplianceRequired) + return + } + default: + if isPaymentComplianceOptionKey(option.Key) { + common.ApiErrorMsg(c, "合规确认字段不允许通过通用设置接口修改") + return + } + } + switch option.Key { case "GitHubOAuthEnabled": if option.Value == "true" && common.GitHubClientId == "" { c.JSON(http.StatusOK, gin.H{ @@ -324,5 +350,4 @@ func UpdateOption(c *gin.Context) { "success": true, "message": "", }) - return } diff --git a/controller/payment_compliance.go b/controller/payment_compliance.go new file mode 100644 index 00000000..831e43a3 --- /dev/null +++ b/controller/payment_compliance.go @@ -0,0 +1,82 @@ +package controller + +import ( + "fmt" + "net/http" + "strconv" + "time" + + "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/i18n" + "github.com/QuantumNous/new-api/logger" + "github.com/QuantumNous/new-api/model" + "github.com/QuantumNous/new-api/setting/operation_setting" + + "github.com/gin-gonic/gin" +) + +type PaymentComplianceRequest struct { + Confirmed bool `json:"confirmed"` +} + +func requirePaymentCompliance(c *gin.Context) bool { + if !operation_setting.IsPaymentComplianceConfirmed() { + common.ApiErrorI18n(c, i18n.MsgPaymentComplianceRequired) + return false + } + return true +} + +func ConfirmPaymentCompliance(c *gin.Context) { + if c.GetBool("use_access_token") { + c.JSON(http.StatusForbidden, gin.H{ + "success": false, + "message": "This operation requires dashboard session authentication. API access token is not allowed.", + }) + return + } + + var req PaymentComplianceRequest + if err := common.DecodeJson(c.Request.Body, &req); err != nil { + common.ApiErrorMsg(c, "参数错误") + return + } + if !req.Confirmed { + common.ApiErrorMsg(c, "请确认合规声明") + return + } + + now := time.Now().Unix() + userId := c.GetInt("id") + clientIP := c.ClientIP() + + updates := map[string]string{ + "payment_setting.compliance_confirmed": "true", + "payment_setting.compliance_terms_version": operation_setting.CurrentComplianceTermsVersion, + "payment_setting.compliance_confirmed_at": strconv.FormatInt(now, 10), + "payment_setting.compliance_confirmed_by": strconv.Itoa(userId), + "payment_setting.compliance_confirmed_ip": clientIP, + } + + for key, value := range updates { + if err := model.UpdateOption(key, value); err != nil { + common.ApiError(c, err) + return + } + } + + logger.LogInfo(c.Request.Context(), fmt.Sprintf( + "payment compliance confirmed user_id=%d ip=%s terms_version=%s confirmed_at=%d", + userId, + clientIP, + operation_setting.CurrentComplianceTermsVersion, + now, + )) + + common.ApiSuccess(c, gin.H{ + "confirmed": true, + "terms_version": operation_setting.CurrentComplianceTermsVersion, + "confirmed_at": now, + "confirmed_by": userId, + }) +} diff --git a/controller/payment_webhook_availability.go b/controller/payment_webhook_availability.go index 9e7a08ae..6b16e53f 100644 --- a/controller/payment_webhook_availability.go +++ b/controller/payment_webhook_availability.go @@ -7,7 +7,14 @@ import ( "github.com/QuantumNous/new-api/setting/operation_setting" ) +func isPaymentComplianceConfirmed() bool { + return operation_setting.IsPaymentComplianceConfirmed() +} + func isStripeTopUpEnabled() bool { + if !isPaymentComplianceConfirmed() { + return false + } return strings.TrimSpace(setting.StripeApiSecret) != "" && strings.TrimSpace(setting.StripeWebhookSecret) != "" && strings.TrimSpace(setting.StripePriceId) != "" @@ -22,6 +29,9 @@ func isStripeWebhookEnabled() bool { } func isCreemTopUpEnabled() bool { + if !isPaymentComplianceConfirmed() { + return false + } products := strings.TrimSpace(setting.CreemProducts) return strings.TrimSpace(setting.CreemApiKey) != "" && products != "" && @@ -37,6 +47,9 @@ func isCreemWebhookEnabled() bool { } func isWaffoTopUpEnabled() bool { + if !isPaymentComplianceConfirmed() { + return false + } if !setting.WaffoEnabled { return false } @@ -61,6 +74,9 @@ func isWaffoWebhookEnabled() bool { } func isWaffoPancakeTopUpEnabled() bool { + if !isPaymentComplianceConfirmed() { + return false + } if !setting.WaffoPancakeEnabled { return false } @@ -86,6 +102,9 @@ func isWaffoPancakeWebhookEnabled() bool { } func isEpayTopUpEnabled() bool { + if !isPaymentComplianceConfirmed() { + return false + } return isEpayWebhookConfigured() && len(operation_setting.PayMethods) > 0 } diff --git a/controller/payment_webhook_availability_test.go b/controller/payment_webhook_availability_test.go index 0534acc4..f277602b 100644 --- a/controller/payment_webhook_availability_test.go +++ b/controller/payment_webhook_availability_test.go @@ -8,7 +8,21 @@ import ( "github.com/stretchr/testify/require" ) +func confirmPaymentComplianceForTest(t *testing.T) { + t.Helper() + paymentSetting := operation_setting.GetPaymentSetting() + originalConfirmed := paymentSetting.ComplianceConfirmed + originalTermsVersion := paymentSetting.ComplianceTermsVersion + t.Cleanup(func() { + paymentSetting.ComplianceConfirmed = originalConfirmed + paymentSetting.ComplianceTermsVersion = originalTermsVersion + }) + paymentSetting.ComplianceConfirmed = true + paymentSetting.ComplianceTermsVersion = operation_setting.CurrentComplianceTermsVersion +} + func TestStripeWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) { + confirmPaymentComplianceForTest(t) originalAPISecret := setting.StripeApiSecret originalWebhookSecret := setting.StripeWebhookSecret originalPriceID := setting.StripePriceId @@ -31,6 +45,7 @@ func TestStripeWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) { } func TestCreemWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) { + confirmPaymentComplianceForTest(t) originalAPIKey := setting.CreemApiKey originalProducts := setting.CreemProducts originalWebhookSecret := setting.CreemWebhookSecret @@ -53,6 +68,7 @@ func TestCreemWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) { } func TestWaffoWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) { + confirmPaymentComplianceForTest(t) originalEnabled := setting.WaffoEnabled originalSandbox := setting.WaffoSandbox originalAPIKey := setting.WaffoApiKey @@ -97,6 +113,7 @@ func TestWaffoWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) { } func TestWaffoPancakeWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) { + confirmPaymentComplianceForTest(t) originalEnabled := setting.WaffoPancakeEnabled originalSandbox := setting.WaffoPancakeSandbox originalMerchantID := setting.WaffoPancakeMerchantID @@ -141,6 +158,7 @@ func TestWaffoPancakeWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) { } func TestEpayWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) { + confirmPaymentComplianceForTest(t) originalPayAddress := operation_setting.PayAddress originalEpayID := operation_setting.EpayId originalEpayKey := operation_setting.EpayKey diff --git a/controller/redemption.go b/controller/redemption.go index 76c35bc3..6130a22c 100644 --- a/controller/redemption.go +++ b/controller/redemption.go @@ -8,6 +8,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/i18n" "github.com/QuantumNous/new-api/model" + "github.com/QuantumNous/new-api/setting/operation_setting" "github.com/gin-gonic/gin" ) @@ -59,6 +60,11 @@ func GetRedemption(c *gin.Context) { } func AddRedemption(c *gin.Context) { + if !operation_setting.IsPaymentComplianceConfirmed() { + common.ApiErrorI18n(c, i18n.MsgPaymentComplianceRequired) + return + } + redemption := model.Redemption{} err := c.ShouldBindJSON(&redemption) if err != nil { diff --git a/controller/subscription.go b/controller/subscription.go index c6095312..4ce65249 100644 --- a/controller/subscription.go +++ b/controller/subscription.go @@ -6,6 +6,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/model" + "github.com/QuantumNous/new-api/setting/operation_setting" "github.com/QuantumNous/new-api/setting/ratio_setting" "github.com/gin-gonic/gin" "gorm.io/gorm" @@ -24,6 +25,11 @@ type BillingPreferenceRequest struct { // ---- User APIs ---- func GetSubscriptionPlans(c *gin.Context) { + if !operation_setting.IsPaymentComplianceConfirmed() { + common.ApiSuccess(c, []SubscriptionPlanDTO{}) + return + } + var plans []model.SubscriptionPlan if err := model.DB.Where("enabled = ?", true).Order("sort_order desc, id desc").Find(&plans).Error; err != nil { common.ApiError(c, err) @@ -108,6 +114,10 @@ type AdminUpsertSubscriptionPlanRequest struct { } func AdminCreateSubscriptionPlan(c *gin.Context) { + if !requirePaymentCompliance(c) { + return + } + var req AdminUpsertSubscriptionPlanRequest if err := c.ShouldBindJSON(&req); err != nil { common.ApiErrorMsg(c, "参数错误") @@ -166,6 +176,10 @@ func AdminCreateSubscriptionPlan(c *gin.Context) { } func AdminUpdateSubscriptionPlan(c *gin.Context) { + if !requirePaymentCompliance(c) { + return + } + id, _ := strconv.Atoi(c.Param("id")) if id <= 0 { common.ApiErrorMsg(c, "无效的ID") @@ -259,6 +273,10 @@ type AdminUpdateSubscriptionPlanStatusRequest struct { } func AdminUpdateSubscriptionPlanStatus(c *gin.Context) { + if !requirePaymentCompliance(c) { + return + } + id, _ := strconv.Atoi(c.Param("id")) if id <= 0 { common.ApiErrorMsg(c, "无效的ID") @@ -283,6 +301,10 @@ type AdminBindSubscriptionRequest struct { } func AdminBindSubscription(c *gin.Context) { + if !requirePaymentCompliance(c) { + return + } + var req AdminBindSubscriptionRequest if err := c.ShouldBindJSON(&req); err != nil || req.UserId <= 0 || req.PlanId <= 0 { common.ApiErrorMsg(c, "参数错误") @@ -322,6 +344,10 @@ type AdminCreateUserSubscriptionRequest struct { // AdminCreateUserSubscription creates a new user subscription from a plan (no payment). func AdminCreateUserSubscription(c *gin.Context) { + if !requirePaymentCompliance(c) { + return + } + userId, _ := strconv.Atoi(c.Param("id")) if userId <= 0 { common.ApiErrorMsg(c, "无效的用户ID") diff --git a/controller/subscription_payment_creem.go b/controller/subscription_payment_creem.go index 18e1a584..a449bc19 100644 --- a/controller/subscription_payment_creem.go +++ b/controller/subscription_payment_creem.go @@ -21,6 +21,10 @@ type SubscriptionCreemPayRequest struct { } func SubscriptionRequestCreemPay(c *gin.Context) { + if !requirePaymentCompliance(c) { + return + } + var req SubscriptionCreemPayRequest // Keep body for debugging consistency (like RequestCreemPay) diff --git a/controller/subscription_payment_epay.go b/controller/subscription_payment_epay.go index ecd38472..7dece6ba 100644 --- a/controller/subscription_payment_epay.go +++ b/controller/subscription_payment_epay.go @@ -22,6 +22,10 @@ type SubscriptionEpayPayRequest struct { } func SubscriptionRequestEpay(c *gin.Context) { + if !requirePaymentCompliance(c) { + return + } + var req SubscriptionEpayPayRequest if err := c.ShouldBindJSON(&req); err != nil || req.PlanId <= 0 { common.ApiErrorMsg(c, "参数错误") diff --git a/controller/subscription_payment_stripe.go b/controller/subscription_payment_stripe.go index 3efe7b38..f6e95867 100644 --- a/controller/subscription_payment_stripe.go +++ b/controller/subscription_payment_stripe.go @@ -21,6 +21,10 @@ type SubscriptionStripePayRequest struct { } func SubscriptionRequestStripePay(c *gin.Context) { + if !requirePaymentCompliance(c) { + return + } + var req SubscriptionStripePayRequest if err := c.ShouldBindJSON(&req); err != nil || req.PlanId <= 0 { common.ApiErrorMsg(c, "参数错误") diff --git a/controller/topup.go b/controller/topup.go index 208de2c7..1de19679 100644 --- a/controller/topup.go +++ b/controller/topup.go @@ -22,8 +22,13 @@ import ( ) func GetTopUpInfo(c *gin.Context) { + complianceConfirmed := operation_setting.IsPaymentComplianceConfirmed() + // 获取支付方式 payMethods := operation_setting.PayMethods + if !complianceConfirmed { + payMethods = []map[string]string{} + } // 如果启用了 Stripe 支付,添加到支付方法列表 if isStripeTopUpEnabled() { @@ -90,11 +95,14 @@ func GetTopUpInfo(c *gin.Context) { } data := gin.H{ - "enable_online_topup": isEpayTopUpEnabled(), - "enable_stripe_topup": isStripeTopUpEnabled(), - "enable_creem_topup": isCreemTopUpEnabled(), - "enable_waffo_topup": enableWaffo, - "enable_waffo_pancake_topup": enableWaffoPancake, + "enable_online_topup": isEpayTopUpEnabled(), + "enable_stripe_topup": isStripeTopUpEnabled(), + "enable_creem_topup": isCreemTopUpEnabled(), + "enable_waffo_topup": enableWaffo, + "enable_waffo_pancake_topup": enableWaffoPancake, + "enable_redemption": complianceConfirmed, + "payment_compliance_confirmed": complianceConfirmed, + "payment_compliance_terms_version": operation_setting.CurrentComplianceTermsVersion, "waffo_pay_methods": func() interface{} { if enableWaffo { return setting.GetWaffoPayMethods() diff --git a/controller/user.go b/controller/user.go index b5722668..555ef9b3 100644 --- a/controller/user.go +++ b/controller/user.go @@ -17,6 +17,7 @@ import ( "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting" + "github.com/QuantumNous/new-api/setting/operation_setting" "github.com/QuantumNous/new-api/constant" @@ -327,6 +328,10 @@ type TransferAffQuotaRequest struct { } func TransferAffQuota(c *gin.Context) { + if !requirePaymentCompliance(c) { + return + } + id := c.GetInt("id") user, err := model.GetUserById(id, true) if err != nil { @@ -1081,6 +1086,11 @@ func getTopUpLock(userID int) *topUpTryLock { } func TopUp(c *gin.Context) { + if !operation_setting.IsPaymentComplianceConfirmed() { + common.ApiErrorI18n(c, i18n.MsgPaymentComplianceRequired) + return + } + id := c.GetInt("id") lock := getTopUpLock(id) if !lock.TryLock() { diff --git a/i18n/keys.go b/i18n/keys.go index 8e551d2e..6cf5c1bd 100644 --- a/i18n/keys.go +++ b/i18n/keys.go @@ -142,16 +142,17 @@ const ( // Payment related messages const ( - MsgPaymentNotConfigured = "payment.not_configured" - MsgPaymentMethodNotExists = "payment.method_not_exists" - MsgPaymentCallbackError = "payment.callback_error" - MsgPaymentCreateFailed = "payment.create_failed" - MsgPaymentStartFailed = "payment.start_failed" - MsgPaymentAmountTooLow = "payment.amount_too_low" - MsgPaymentStripeNotConfig = "payment.stripe_not_configured" - MsgPaymentWebhookNotConfig = "payment.webhook_not_configured" - MsgPaymentPriceIdNotConfig = "payment.price_id_not_configured" - MsgPaymentCreemNotConfig = "payment.creem_not_configured" + MsgPaymentNotConfigured = "payment.not_configured" + MsgPaymentMethodNotExists = "payment.method_not_exists" + MsgPaymentCallbackError = "payment.callback_error" + MsgPaymentCreateFailed = "payment.create_failed" + MsgPaymentStartFailed = "payment.start_failed" + MsgPaymentAmountTooLow = "payment.amount_too_low" + MsgPaymentStripeNotConfig = "payment.stripe_not_configured" + MsgPaymentWebhookNotConfig = "payment.webhook_not_configured" + MsgPaymentPriceIdNotConfig = "payment.price_id_not_configured" + MsgPaymentCreemNotConfig = "payment.creem_not_configured" + MsgPaymentComplianceRequired = "payment.compliance_required" ) // Topup related messages diff --git a/i18n/locales/en.yaml b/i18n/locales/en.yaml index 064abc70..198aa274 100644 --- a/i18n/locales/en.yaml +++ b/i18n/locales/en.yaml @@ -134,6 +134,7 @@ payment.stripe_not_configured: "Stripe is not configured or key is invalid" payment.webhook_not_configured: "Webhook is not configured" payment.price_id_not_configured: "StripePriceId is not configured for this plan" payment.creem_not_configured: "CreemProductId is not configured for this plan" +payment.compliance_required: "Payment, redemption, subscription, and invitation reward features are disabled. The administrator must confirm compliance terms before enabling them." # Topup messages topup.not_provided: "Payment order number not provided" diff --git a/i18n/locales/zh-CN.yaml b/i18n/locales/zh-CN.yaml index b05ca743..45a10a58 100644 --- a/i18n/locales/zh-CN.yaml +++ b/i18n/locales/zh-CN.yaml @@ -135,6 +135,7 @@ payment.stripe_not_configured: "Stripe 未配置或密钥无效" payment.webhook_not_configured: "Webhook 未配置" payment.price_id_not_configured: "该套餐未配置 StripePriceId" payment.creem_not_configured: "该套餐未配置 CreemProductId" +payment.compliance_required: "支付、兑换码、订阅计划和邀请返利功能已禁用。管理员需先确认合规声明后方可启用。" # Topup messages topup.not_provided: "未提供支付单号" diff --git a/i18n/locales/zh-TW.yaml b/i18n/locales/zh-TW.yaml index 6ae222b7..ad5a6eae 100644 --- a/i18n/locales/zh-TW.yaml +++ b/i18n/locales/zh-TW.yaml @@ -135,6 +135,7 @@ payment.stripe_not_configured: "Stripe 未設定或密鑰無效" payment.webhook_not_configured: "Webhook 未設定" payment.price_id_not_configured: "該訂閱方案未設定 StripePriceId" payment.creem_not_configured: "該訂閱方案未設定 CreemProductId" +payment.compliance_required: "支付、兌換碼、訂閱方案和邀請返利功能已停用。管理員需先確認合規聲明後方可啟用。" # Topup messages topup.not_provided: "未提供支付單號" diff --git a/model/user.go b/model/user.go index b632ef9a..5079acaf 100644 --- a/model/user.go +++ b/model/user.go @@ -11,6 +11,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" + "github.com/QuantumNous/new-api/setting/operation_setting" "github.com/bytedance/gopkg/util/gopool" "gorm.io/gorm" @@ -420,7 +421,7 @@ func (user *User) Insert(inviterId int) error { if common.QuotaForNewUser > 0 { RecordLog(user.Id, LogTypeSystem, fmt.Sprintf("新用户注册赠送 %s", logger.LogQuota(common.QuotaForNewUser))) } - if inviterId != 0 { + if inviterId != 0 && operation_setting.IsPaymentComplianceConfirmed() { if common.QuotaForInvitee > 0 { _ = IncreaseUserQuota(user.Id, common.QuotaForInvitee, true) RecordLog(user.Id, LogTypeSystem, fmt.Sprintf("使用邀请码赠送 %s", logger.LogQuota(common.QuotaForInvitee))) @@ -481,7 +482,7 @@ func (user *User) FinalizeOAuthUserCreation(inviterId int) { if common.QuotaForNewUser > 0 { RecordLog(user.Id, LogTypeSystem, fmt.Sprintf("新用户注册赠送 %s", logger.LogQuota(common.QuotaForNewUser))) } - if inviterId != 0 { + if inviterId != 0 && operation_setting.IsPaymentComplianceConfirmed() { if common.QuotaForInvitee > 0 { _ = IncreaseUserQuota(user.Id, common.QuotaForInvitee, true) RecordLog(user.Id, LogTypeSystem, fmt.Sprintf("使用邀请码赠送 %s", logger.LogQuota(common.QuotaForInvitee))) diff --git a/router/api-router.go b/router/api-router.go index 72b55856..64ccbe15 100644 --- a/router/api-router.go +++ b/router/api-router.go @@ -181,6 +181,7 @@ func SetApiRouter(router *gin.Engine) { { optionRoute.GET("/", controller.GetOptions) optionRoute.PUT("/", controller.UpdateOption) + optionRoute.POST("/payment_compliance", controller.ConfirmPaymentCompliance) optionRoute.GET("/channel_affinity_cache", controller.GetChannelAffinityCacheStats) optionRoute.DELETE("/channel_affinity_cache", controller.ClearChannelAffinityCache) optionRoute.POST("/rest_model_ratio", controller.ResetModelRatio) diff --git a/setting/operation_setting/payment_setting.go b/setting/operation_setting/payment_setting.go index 84162f4e..b08d466b 100644 --- a/setting/operation_setting/payment_setting.go +++ b/setting/operation_setting/payment_setting.go @@ -5,8 +5,16 @@ import "github.com/QuantumNous/new-api/setting/config" type PaymentSetting struct { AmountOptions []int `json:"amount_options"` AmountDiscount map[int]float64 `json:"amount_discount"` // 充值金额对应的折扣,例如 100 元 0.9 表示 100 元充值享受 9 折优惠 + + ComplianceConfirmed bool `json:"compliance_confirmed"` + ComplianceTermsVersion string `json:"compliance_terms_version"` + ComplianceConfirmedAt int64 `json:"compliance_confirmed_at"` + ComplianceConfirmedBy int `json:"compliance_confirmed_by"` + ComplianceConfirmedIP string `json:"compliance_confirmed_ip"` } +const CurrentComplianceTermsVersion = "v1" + // 默认配置 var paymentSetting = PaymentSetting{ AmountOptions: []int{10, 20, 50, 100, 200, 500}, @@ -21,3 +29,8 @@ func init() { func GetPaymentSetting() *PaymentSetting { return &paymentSetting } + +func IsPaymentComplianceConfirmed() bool { + return paymentSetting.ComplianceConfirmed && + paymentSetting.ComplianceTermsVersion == CurrentComplianceTermsVersion +} diff --git a/web/classic/src/components/common/modals/RiskAcknowledgementModal.jsx b/web/classic/src/components/common/modals/RiskAcknowledgementModal.jsx index 54aa62ea..812a8e87 100644 --- a/web/classic/src/components/common/modals/RiskAcknowledgementModal.jsx +++ b/web/classic/src/components/common/modals/RiskAcknowledgementModal.jsx @@ -62,6 +62,7 @@ const RiskAcknowledgementModal = React.memo(function RiskAcknowledgementModal({ checklist = [], inputPrompt = '', requiredText = '', + requiredTextParts = [], inputPlaceholder = '', mismatchText = '', cancelText = '', @@ -72,24 +73,68 @@ const RiskAcknowledgementModal = React.memo(function RiskAcknowledgementModal({ const isMobile = useIsMobile(); const [checkedItems, setCheckedItems] = useState([]); const [typedText, setTypedText] = useState(''); + const [typedTextParts, setTypedTextParts] = useState([]); + + const normalizedRequiredTextParts = useMemo(() => { + let inputIndex = 0; + return requiredTextParts.map((part) => { + if (part.type === 'input') { + const normalizedPart = { ...part, inputIndex }; + inputIndex += 1; + return normalizedPart; + } + return part; + }); + }, [requiredTextParts]); + + const requiredTextInputCount = useMemo( + () => + normalizedRequiredTextParts.filter((part) => part.type === 'input') + .length, + [normalizedRequiredTextParts], + ); + const hasSegmentedRequiredText = requiredTextInputCount > 0; + const requiredTextToDisplay = hasSegmentedRequiredText + ? normalizedRequiredTextParts.map((part) => part.text).join('') + : requiredText; useEffect(() => { if (!visible) return; setCheckedItems(Array(checklist.length).fill(false)); setTypedText(''); - }, [visible, checklist.length]); + setTypedTextParts(Array(requiredTextInputCount).fill('')); + }, [visible, checklist.length, requiredTextInputCount]); const allChecked = useMemo(() => { if (checklist.length === 0) return true; - return checkedItems.length === checklist.length && checkedItems.every(Boolean); + return ( + checkedItems.length === checklist.length && checkedItems.every(Boolean) + ); }, [checkedItems, checklist.length]); const typedMatched = useMemo(() => { + if (hasSegmentedRequiredText) { + return normalizedRequiredTextParts.every((part) => { + if (part.type === 'static') return true; + return ( + typedTextParts[part.inputIndex ?? 0]?.trim() === part.text.trim() + ); + }); + } if (!requiredText) return true; return typedText.trim() === requiredText.trim(); - }, [typedText, requiredText]); + }, [ + hasSegmentedRequiredText, + normalizedRequiredTextParts, + requiredText, + typedText, + typedTextParts, + ]); const detailText = useMemo(() => detailItems.join(', '), [detailItems]); + const hasTypedRequiredText = hasSegmentedRequiredText + ? typedTextParts.some((part) => part.trim() !== '') + : typedText.length > 0; const canConfirm = allChecked && typedMatched; const handleChecklistChange = useCallback((index, checked) => { @@ -100,6 +145,14 @@ const RiskAcknowledgementModal = React.memo(function RiskAcknowledgementModal({ }); }, []); + const handleTextPartChange = useCallback((index, value) => { + setTypedTextParts((previous) => { + const next = [...previous]; + next[index] = value; + return next; + }); + }, []); + return (
- {detailItems.length > 0 ? ( @@ -176,7 +228,7 @@ const RiskAcknowledgementModal = React.memo(function RiskAcknowledgementModal({
) : null} - {requiredText ? ( + {requiredTextToDisplay ? (
{inputPrompt ? {inputPrompt} : null}
- {requiredText} + {requiredTextToDisplay}
- event.preventDefault()} - onCut={(event) => event.preventDefault()} - onPaste={(event) => event.preventDefault()} - onDrop={(event) => event.preventDefault()} - /> - {!typedMatched && typedText ? ( + {hasSegmentedRequiredText ? ( +
+ {normalizedRequiredTextParts.map((part, index) => + part.type === 'static' ? ( + + {part.text} + + ) : ( + + handleTextPartChange(part.inputIndex ?? 0, value) + } + placeholder={ + part.placeholder || part.text || inputPlaceholder + } + autoFocus={visible && part.inputIndex === 0} + onCopy={(event) => event.preventDefault()} + onCut={(event) => event.preventDefault()} + onPaste={(event) => event.preventDefault()} + onDrop={(event) => event.preventDefault()} + style={{ width: isMobile ? '100%' : 260 }} + /> + ), + )} +
+ ) : ( + event.preventDefault()} + onCut={(event) => event.preventDefault()} + onPaste={(event) => event.preventDefault()} + onDrop={(event) => event.preventDefault()} + /> + )} + {!typedMatched && hasTypedRequiredText ? ( {mismatchText} diff --git a/web/classic/src/components/settings/PaymentSetting.jsx b/web/classic/src/components/settings/PaymentSetting.jsx index 080c3e6e..362473e2 100644 --- a/web/classic/src/components/settings/PaymentSetting.jsx +++ b/web/classic/src/components/settings/PaymentSetting.jsx @@ -18,15 +18,18 @@ For commercial licensing, please contact support@quantumnous.com */ import React, { useEffect, useState } from 'react'; -import { Card, Spin, Tabs } from '@douyinfe/semi-ui'; +import { Banner, Button, Card, Spin, Tabs } from '@douyinfe/semi-ui'; import SettingsGeneralPayment from '../../pages/Setting/Payment/SettingsGeneralPayment'; import SettingsPaymentGateway from '../../pages/Setting/Payment/SettingsPaymentGateway'; import SettingsPaymentGatewayStripe from '../../pages/Setting/Payment/SettingsPaymentGatewayStripe'; import SettingsPaymentGatewayCreem from '../../pages/Setting/Payment/SettingsPaymentGatewayCreem'; import SettingsPaymentGatewayWaffo from '../../pages/Setting/Payment/SettingsPaymentGatewayWaffo'; import SettingsPaymentGatewayWaffoPancake from '../../pages/Setting/Payment/SettingsPaymentGatewayWaffoPancake'; -import { API, showError, toBoolean } from '../../helpers'; +import { API, showError, showSuccess, toBoolean } from '../../helpers'; import { useTranslation } from 'react-i18next'; +import RiskAcknowledgementModal from '../common/modals/RiskAcknowledgementModal'; + +const CURRENT_COMPLIANCE_TERMS_VERSION = 'v1'; const PaymentSetting = () => { const { t } = useTranslation(); @@ -60,9 +63,59 @@ const PaymentSetting = () => { WaffoPancakeCurrency: 'USD', WaffoPancakeUnitPrice: 1.0, WaffoPancakeMinTopUp: 1, + 'payment_setting.compliance_confirmed': false, + 'payment_setting.compliance_terms_version': '', + 'payment_setting.compliance_confirmed_at': 0, + 'payment_setting.compliance_confirmed_by': 0, }); let [loading, setLoading] = useState(false); + const [complianceVisible, setComplianceVisible] = useState(false); + + const complianceStatements = [ + t('你已合法取得所接入模型 API、账号、密钥和额度的授权;'), + t( + '你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力,不进行未经授权的转售、倒卖、分销或其他违规商业化使用。', + ), + t( + '如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;', + ), + t( + '你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。', + ), + t('你理解并自行承担部署、运营和收费行为产生的法律责任。'), + t( + '你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。', + ), + ]; + const requiredComplianceText = t( + '我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任', + ); + const requiredComplianceTextParts = [ + { + type: 'input', + text: t('我已阅读并理解上述合规提醒'), + }, + { type: 'static', text: t(',') }, + { + type: 'input', + text: t('知悉相关法律风险'), + }, + { type: 'static', text: t(',') }, + { + type: 'input', + text: t('并确认自行承担部署'), + }, + { type: 'static', text: t('、') }, + { + type: 'input', + text: t('运营和收费行为产生的法律责任'), + }, + ]; + const complianceConfirmed = + inputs['payment_setting.compliance_confirmed'] && + inputs['payment_setting.compliance_terms_version'] === + CURRENT_COMPLIANCE_TERMS_VERSION; const getOptions = async () => { const res = await API.get('/api/option/'); @@ -104,6 +157,16 @@ const PaymentSetting = () => { newInputs['AmountDiscount'] = item.value; } break; + case 'payment_setting.compliance_confirmed': + newInputs[item.key] = toBoolean(item.value); + break; + case 'payment_setting.compliance_confirmed_at': + case 'payment_setting.compliance_confirmed_by': + newInputs[item.key] = parseInt(item.value) || 0; + break; + case 'payment_setting.compliance_terms_version': + newInputs[item.key] = item.value; + break; case 'Price': case 'MinTopUp': case 'StripeUnitPrice': @@ -154,59 +217,143 @@ const PaymentSetting = () => { onRefresh(); }, []); + const confirmCompliance = async () => { + try { + const res = await API.post('/api/option/payment_compliance', { + confirmed: true, + }); + if (res.data.success) { + showSuccess(t('合规声明确认成功')); + setComplianceVisible(false); + await onRefresh(); + } else { + showError(res.data.message || t('确认失败')); + } + } catch (error) { + showError(t('确认失败')); + } + }; + return ( <> - + + {t( + '确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。', + )} + + +
+ } + closeIcon={null} + style={{ marginBottom: 16 }} + fullMode={false} + /> + ) : ( + + )} +
- - - - - - - - - - - - - - - - {/**/} - {/* */} - {/**/} - + + + + + + + + + + + + + + + + + {/**/} + {/* */} + {/**/} + +
+ setComplianceVisible(false)} + onConfirm={confirmCompliance} + /> ); diff --git a/web/classic/src/components/table/subscriptions/SubscriptionsActions.jsx b/web/classic/src/components/table/subscriptions/SubscriptionsActions.jsx index 3d329625..587b4812 100644 --- a/web/classic/src/components/table/subscriptions/SubscriptionsActions.jsx +++ b/web/classic/src/components/table/subscriptions/SubscriptionsActions.jsx @@ -20,7 +20,7 @@ For commercial licensing, please contact support@quantumnous.com import React from 'react'; import { Button } from '@douyinfe/semi-ui'; -const SubscriptionsActions = ({ openCreate, t }) => { +const SubscriptionsActions = ({ openCreate, t, disabled = false }) => { return (
diff --git a/web/classic/src/components/table/subscriptions/SubscriptionsColumnDefs.jsx b/web/classic/src/components/table/subscriptions/SubscriptionsColumnDefs.jsx index 222b0512..952dd2a3 100644 --- a/web/classic/src/components/table/subscriptions/SubscriptionsColumnDefs.jsx +++ b/web/classic/src/components/table/subscriptions/SubscriptionsColumnDefs.jsx @@ -228,7 +228,11 @@ const renderPaymentConfig = (text, record, t, enableEpay) => { ); }; -const renderOperations = (text, record, { openEdit, setPlanEnabled, t }) => { +const renderOperations = ( + text, + record, + { openEdit, setPlanEnabled, t, complianceConfirmed }, +) => { const isEnabled = record?.plan?.enabled; const handleToggle = () => { @@ -256,11 +260,18 @@ const renderOperations = (text, record, { openEdit, setPlanEnabled, t }) => { type='tertiary' size='small' onClick={() => openEdit(record)} + disabled={!complianceConfirmed} > {t('编辑')} {isEnabled ? ( - ) : ( @@ -269,6 +280,7 @@ const renderOperations = (text, record, { openEdit, setPlanEnabled, t }) => { type='primary' size='small' onClick={handleToggle} + disabled={!complianceConfirmed} > {t('启用')} @@ -282,6 +294,7 @@ export const getSubscriptionsColumns = ({ openEdit, setPlanEnabled, enableEpay, + complianceConfirmed = true, }) => { return [ { @@ -351,7 +364,12 @@ export const getSubscriptionsColumns = ({ fixed: 'right', width: 160, render: (text, record) => - renderOperations(text, record, { openEdit, setPlanEnabled, t }), + renderOperations(text, record, { + openEdit, + setPlanEnabled, + t, + complianceConfirmed, + }), }, ]; }; diff --git a/web/classic/src/components/table/subscriptions/SubscriptionsTable.jsx b/web/classic/src/components/table/subscriptions/SubscriptionsTable.jsx index 4892f66c..de35ce86 100644 --- a/web/classic/src/components/table/subscriptions/SubscriptionsTable.jsx +++ b/web/classic/src/components/table/subscriptions/SubscriptionsTable.jsx @@ -35,6 +35,7 @@ const SubscriptionsTable = (subscriptionsData) => { setPlanEnabled, t, enableEpay, + complianceConfirmed, } = subscriptionsData; const columns = useMemo(() => { @@ -43,8 +44,9 @@ const SubscriptionsTable = (subscriptionsData) => { openEdit, setPlanEnabled, enableEpay, + complianceConfirmed, }); - }, [t, openEdit, setPlanEnabled, enableEpay]); + }, [t, openEdit, setPlanEnabled, enableEpay, complianceConfirmed]); const tableColumns = useMemo(() => { return compactMode diff --git a/web/classic/src/components/table/subscriptions/index.jsx b/web/classic/src/components/table/subscriptions/index.jsx index 86504482..8424ec5a 100644 --- a/web/classic/src/components/table/subscriptions/index.jsx +++ b/web/classic/src/components/table/subscriptions/index.jsx @@ -17,7 +17,7 @@ along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ -import React, { useContext } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { Banner } from '@douyinfe/semi-ui'; import CardPro from '../../common/ui/CardPro'; import SubscriptionsTable from './SubscriptionsTable'; @@ -28,12 +28,14 @@ import { useSubscriptionsData } from '../../../hooks/subscriptions/useSubscripti import { useIsMobile } from '../../../hooks/common/useIsMobile'; import { createCardProPagination } from '../../../helpers/utils'; import { StatusContext } from '../../../context/Status'; +import { API } from '../../../helpers'; const SubscriptionsPage = () => { const subscriptionsData = useSubscriptionsData(); const isMobile = useIsMobile(); const [statusState] = useContext(StatusContext); const enableEpay = !!statusState?.status?.enable_online_topup; + const [complianceConfirmed, setComplianceConfirmed] = useState(true); const { showEdit, @@ -47,6 +49,22 @@ const SubscriptionsPage = () => { t, } = subscriptionsData; + useEffect(() => { + const loadComplianceStatus = async () => { + try { + const res = await API.get('/api/user/topup/info'); + if (res.data?.success) { + setComplianceConfirmed( + res.data.data?.payment_compliance_confirmed !== false, + ); + } + } catch (error) { + // Keep the page usable if status loading fails; backend still enforces. + } + }; + loadComplianceStatus(); + }, []); + return ( <> {
{/* Mobile: actions first; Desktop: actions left */}
- +
{ })} t={t} > - + {!complianceConfirmed && ( + + )} + ); diff --git a/web/classic/src/components/topup/InvitationCard.jsx b/web/classic/src/components/topup/InvitationCard.jsx index 7ae6b0a5..cc30cf8f 100644 --- a/web/classic/src/components/topup/InvitationCard.jsx +++ b/web/classic/src/components/topup/InvitationCard.jsx @@ -38,6 +38,7 @@ const InvitationCard = ({ setOpenTransfer, affLink, handleAffLinkClick, + complianceConfirmed = true, }) => { return ( @@ -81,6 +82,7 @@ const InvitationCard = ({ theme='solid' size='small' disabled={ + !complianceConfirmed || !userState?.user?.aff_quota || userState?.user?.aff_quota <= 0 } @@ -91,6 +93,16 @@ const InvitationCard = ({ {t('划转到余额')}
+ {!complianceConfirmed && ( + + {t('邀请奖励划转已禁用,管理员需先确认合规声明。')} + + )} {/* 统计数据 */}
diff --git a/web/classic/src/components/topup/RechargeCard.jsx b/web/classic/src/components/topup/RechargeCard.jsx index 1e2923ca..f89d8ed7 100644 --- a/web/classic/src/components/topup/RechargeCard.jsx +++ b/web/classic/src/components/topup/RechargeCard.jsx @@ -96,6 +96,7 @@ const RechargeCard = ({ activeSubscriptions = [], allSubscriptions = [], reloadSubscriptionSelf, + enableRedemption = true, }) => { const onlineFormApiRef = useRef(null); const redeemFormApiRef = useRef(null); @@ -566,57 +567,66 @@ const RechargeCard = ({ {/* 兑换码充值 */} - - {t('兑换码充值')} - - } - > -
(redeemFormApiRef.current = api)} - initValues={{ redemptionCode: redemptionCode }} + {enableRedemption ? ( + + {t('兑换码充值')} + + } > - setRedemptionCode(value)} - prefix={} - suffix={ -
- -
- } - showClear - style={{ width: '100%' }} - extraText={ - topUpLink && ( - - {t('在找兑换码?')} - (redeemFormApiRef.current = api)} + initValues={{ redemptionCode: redemptionCode }} + > + setRedemptionCode(value)} + prefix={} + suffix={ +
+ +
+ } + showClear + style={{ width: '100%' }} + extraText={ + topUpLink && ( + + {t('在找兑换码?')} + + {t('购买兑换码')} + -
- ) - } - /> - -
+ ) + } + /> + +
+ ) : ( + + )} ); diff --git a/web/classic/src/components/topup/index.jsx b/web/classic/src/components/topup/index.jsx index 881e39e9..dd2ef165 100644 --- a/web/classic/src/components/topup/index.jsx +++ b/web/classic/src/components/topup/index.jsx @@ -110,6 +110,8 @@ const TopUp = () => { const [topupInfo, setTopupInfo] = useState({ amount_options: [], discount: {}, + enable_redemption: true, + payment_compliance_confirmed: true, }); const confirmPayMethods = [ @@ -645,7 +647,7 @@ const TopUp = () => { ? data.waffo_min_topup : enableWaffoPancakeTopUp ? data.waffo_pancake_min_topup - : 1; + : 1; setEnableOnlineTopUp(enableOnlineTopUp); setEnableStripeTopUp(enableStripeTopUp); setEnableCreemTopUp(enableCreemTopUp); @@ -657,6 +659,14 @@ const TopUp = () => { setMinTopUp(minTopUpValue); setTopUpCount(minTopUpValue); setTopUpLink(data.topup_link || ''); + setTopupInfo((prev) => ({ + ...prev, + enable_redemption: data.enable_redemption !== false, + payment_compliance_confirmed: + data.payment_compliance_confirmed !== false, + payment_compliance_terms_version: + data.payment_compliance_terms_version || '', + })); // 设置 Creem 产品 try { @@ -984,6 +994,7 @@ const TopUp = () => { activeSubscriptions={activeSubscriptions} allSubscriptions={allSubscriptions} reloadSubscriptionSelf={getSubscriptionSelf} + enableRedemption={topupInfo.enable_redemption !== false} /> { setOpenTransfer={setOpenTransfer} affLink={affLink} handleAffLinkClick={handleAffLinkClick} + complianceConfirmed={topupInfo.payment_compliance_confirmed !== false} />
diff --git a/web/classic/src/i18n/locales/en.json b/web/classic/src/i18n/locales/en.json index e9ed14ff..ea6bca1b 100644 --- a/web/classic/src/i18n/locales/en.json +++ b/web/classic/src/i18n/locales/en.json @@ -3794,6 +3794,36 @@ "见上方动态计费详情": "See dynamic pricing details above", "含时间条件": "Time rules", "含请求条件": "Request rules", - "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(Currently only supports Epay interface, the default callback address is the server address above!)" + "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(Currently only supports Epay interface, the default callback address is the server address above!)", + "你已合法取得所接入模型 API、账号、密钥和额度的授权;": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.", + "你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力,不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.", + "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "If you provide generative AI services to the public in mainland China, you will fulfill legal compliance obligations.", + "你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.", + "你理解并自行承担部署、运营和收费行为产生的法律责任。": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.", + "你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.", + "我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.", + "需要确认合规声明": "Compliance confirmation required", + "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "Payment, redemption codes, subscription plans, and invitation rewards remain locked until confirmation.", + "确认合规声明": "Confirm compliance terms", + "合规声明已确认": "Compliance confirmed", + "确认时间:{{time}},确认用户:#{{userId}}": "Confirmed at {{time}} by user #{{userId}}", + "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read carefully.", + "请输入以下文字以确认:": "Please type the following text to confirm:", + "请输入确认文案": "Type the confirmation text here", + "输入内容与要求文案不一致": "The entered text does not match the required text.", + "确认并启用": "Confirm and enable", + "合规声明确认成功": "Compliance confirmed successfully", + "确认失败": "Confirmation failed", + "兑换码功能已禁用,管理员需先确认合规声明。": "Redemption codes are disabled until the administrator confirms compliance terms.", + "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "Subscription plan creation and changes are locked until compliance terms are confirmed in payment settings.", + "邀请奖励划转已禁用,管理员需先确认合规声明。": "Invitation reward transfer is disabled until compliance terms are confirmed.", + "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "Non-zero invitation rewards require compliance confirmation in payment settings.", + "非零值需先确认合规声明": "Non-zero values require compliance confirmation first", + "我已阅读并理解上述合规提醒": "I have read and understood the above compliance reminder", + "知悉相关法律风险": "acknowledge the related legal risks", + "并确认自行承担部署": "confirm that I bear legal responsibility arising from deployment", + "运营和收费行为产生的法律责任": "operation and charging behavior", + ",": ", ", + "、": ", " } } diff --git a/web/classic/src/i18n/locales/fr.json b/web/classic/src/i18n/locales/fr.json index 22e200f4..a24d32ba 100644 --- a/web/classic/src/i18n/locales/fr.json +++ b/web/classic/src/i18n/locales/fr.json @@ -3649,6 +3649,36 @@ "默认用户消息": "Bonjour", "默认补全倍率": "Taux de complétion par défaut", "阶梯计费(表达式解析失败)": "Facturation par paliers (échec de l'analyse de l'expression)", - "阶梯计费(未匹配到对应阶梯)": "Facturation par paliers (aucun palier correspondant)" + "阶梯计费(未匹配到对应阶梯)": "Facturation par paliers (aucun palier correspondant)", + "你已合法取得所接入模型 API、账号、密钥和额度的授权;": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.", + "你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力,不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.", + "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "If you provide generative AI services to the public in mainland China, you will fulfill legal compliance obligations.", + "你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.", + "你理解并自行承担部署、运营和收费行为产生的法律责任。": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.", + "你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.", + "我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.", + "需要确认合规声明": "Compliance confirmation required", + "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "Payment, redemption codes, subscription plans, and invitation rewards remain locked until confirmation.", + "确认合规声明": "Confirm compliance terms", + "合规声明已确认": "Compliance confirmed", + "确认时间:{{time}},确认用户:#{{userId}}": "Confirmed at {{time}} by user #{{userId}}", + "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read carefully.", + "请输入以下文字以确认:": "Please type the following text to confirm:", + "请输入确认文案": "Type the confirmation text here", + "输入内容与要求文案不一致": "The entered text does not match the required text.", + "确认并启用": "Confirm and enable", + "合规声明确认成功": "Compliance confirmed successfully", + "确认失败": "Confirmation failed", + "兑换码功能已禁用,管理员需先确认合规声明。": "Redemption codes are disabled until the administrator confirms compliance terms.", + "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "Subscription plan creation and changes are locked until compliance terms are confirmed in payment settings.", + "邀请奖励划转已禁用,管理员需先确认合规声明。": "Invitation reward transfer is disabled until compliance terms are confirmed.", + "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "Non-zero invitation rewards require compliance confirmation in payment settings.", + "非零值需先确认合规声明": "Non-zero values require compliance confirmation first", + "我已阅读并理解上述合规提醒": "I have read and understood the above compliance reminder", + "知悉相关法律风险": "acknowledge the related legal risks", + "并确认自行承担部署": "confirm that I bear legal responsibility arising from deployment", + "运营和收费行为产生的法律责任": "operation and charging behavior", + ",": ", ", + "、": ", " } } diff --git a/web/classic/src/i18n/locales/ja.json b/web/classic/src/i18n/locales/ja.json index 8de2af2a..dde2a1a5 100644 --- a/web/classic/src/i18n/locales/ja.json +++ b/web/classic/src/i18n/locales/ja.json @@ -3618,6 +3618,36 @@ "默认用户消息": "こんにちは", "默认补全倍率": "デフォルト補完倍率", "阶梯计费(表达式解析失败)": "段階課金(式の解析に失敗)", - "阶梯计费(未匹配到对应阶梯)": "段階課金(一致する階層なし)" + "阶梯计费(未匹配到对应阶梯)": "段階課金(一致する階層なし)", + "你已合法取得所接入模型 API、账号、密钥和额度的授权;": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.", + "你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力,不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.", + "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "If you provide generative AI services to the public in mainland China, you will fulfill legal compliance obligations.", + "你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.", + "你理解并自行承担部署、运营和收费行为产生的法律责任。": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.", + "你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.", + "我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.", + "需要确认合规声明": "Compliance confirmation required", + "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "Payment, redemption codes, subscription plans, and invitation rewards remain locked until confirmation.", + "确认合规声明": "Confirm compliance terms", + "合规声明已确认": "Compliance confirmed", + "确认时间:{{time}},确认用户:#{{userId}}": "Confirmed at {{time}} by user #{{userId}}", + "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read carefully.", + "请输入以下文字以确认:": "Please type the following text to confirm:", + "请输入确认文案": "Type the confirmation text here", + "输入内容与要求文案不一致": "The entered text does not match the required text.", + "确认并启用": "Confirm and enable", + "合规声明确认成功": "Compliance confirmed successfully", + "确认失败": "Confirmation failed", + "兑换码功能已禁用,管理员需先确认合规声明。": "Redemption codes are disabled until the administrator confirms compliance terms.", + "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "Subscription plan creation and changes are locked until compliance terms are confirmed in payment settings.", + "邀请奖励划转已禁用,管理员需先确认合规声明。": "Invitation reward transfer is disabled until compliance terms are confirmed.", + "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "Non-zero invitation rewards require compliance confirmation in payment settings.", + "非零值需先确认合规声明": "Non-zero values require compliance confirmation first", + "我已阅读并理解上述合规提醒": "I have read and understood the above compliance reminder", + "知悉相关法律风险": "acknowledge the related legal risks", + "并确认自行承担部署": "confirm that I bear legal responsibility arising from deployment", + "运营和收费行为产生的法律责任": "operation and charging behavior", + ",": ", ", + "、": ", " } } diff --git a/web/classic/src/i18n/locales/ru.json b/web/classic/src/i18n/locales/ru.json index 7269d572..b934dfe1 100644 --- a/web/classic/src/i18n/locales/ru.json +++ b/web/classic/src/i18n/locales/ru.json @@ -3669,6 +3669,36 @@ "默认用户消息": "Здравствуйте", "默认补全倍率": "Коэффициент завершения по умолчанию", "阶梯计费(表达式解析失败)": "Многоуровневая тарификация (ошибка разбора выражения)", - "阶梯计费(未匹配到对应阶梯)": "Многоуровневая тарификация (подходящий уровень не найден)" + "阶梯计费(未匹配到对应阶梯)": "Многоуровневая тарификация (подходящий уровень не найден)", + "你已合法取得所接入模型 API、账号、密钥和额度的授权;": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.", + "你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力,不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.", + "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "If you provide generative AI services to the public in mainland China, you will fulfill legal compliance obligations.", + "你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.", + "你理解并自行承担部署、运营和收费行为产生的法律责任。": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.", + "你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.", + "我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.", + "需要确认合规声明": "Compliance confirmation required", + "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "Payment, redemption codes, subscription plans, and invitation rewards remain locked until confirmation.", + "确认合规声明": "Confirm compliance terms", + "合规声明已确认": "Compliance confirmed", + "确认时间:{{time}},确认用户:#{{userId}}": "Confirmed at {{time}} by user #{{userId}}", + "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read carefully.", + "请输入以下文字以确认:": "Please type the following text to confirm:", + "请输入确认文案": "Type the confirmation text here", + "输入内容与要求文案不一致": "The entered text does not match the required text.", + "确认并启用": "Confirm and enable", + "合规声明确认成功": "Compliance confirmed successfully", + "确认失败": "Confirmation failed", + "兑换码功能已禁用,管理员需先确认合规声明。": "Redemption codes are disabled until the administrator confirms compliance terms.", + "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "Subscription plan creation and changes are locked until compliance terms are confirmed in payment settings.", + "邀请奖励划转已禁用,管理员需先确认合规声明。": "Invitation reward transfer is disabled until compliance terms are confirmed.", + "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "Non-zero invitation rewards require compliance confirmation in payment settings.", + "非零值需先确认合规声明": "Non-zero values require compliance confirmation first", + "我已阅读并理解上述合规提醒": "I have read and understood the above compliance reminder", + "知悉相关法律风险": "acknowledge the related legal risks", + "并确认自行承担部署": "confirm that I bear legal responsibility arising from deployment", + "运营和收费行为产生的法律责任": "operation and charging behavior", + ",": ", ", + "、": ", " } } diff --git a/web/classic/src/i18n/locales/vi.json b/web/classic/src/i18n/locales/vi.json index c48c69f2..771a25fc 100644 --- a/web/classic/src/i18n/locales/vi.json +++ b/web/classic/src/i18n/locales/vi.json @@ -4183,6 +4183,36 @@ "默认用户消息": "Xin chào", "默认补全倍率": "Tỷ lệ hoàn thành mặc định", "阶梯计费(表达式解析失败)": "Thanh toán theo bậc (không phân tích được biểu thức)", - "阶梯计费(未匹配到对应阶梯)": "Thanh toán theo bậc (không tìm thấy bậc phù hợp)" + "阶梯计费(未匹配到对应阶梯)": "Thanh toán theo bậc (không tìm thấy bậc phù hợp)", + "你已合法取得所接入模型 API、账号、密钥和额度的授权;": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.", + "你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力,不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.", + "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "If you provide generative AI services to the public in mainland China, you will fulfill legal compliance obligations.", + "你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.", + "你理解并自行承担部署、运营和收费行为产生的法律责任。": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.", + "你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.", + "我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.", + "需要确认合规声明": "Compliance confirmation required", + "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "Payment, redemption codes, subscription plans, and invitation rewards remain locked until confirmation.", + "确认合规声明": "Confirm compliance terms", + "合规声明已确认": "Compliance confirmed", + "确认时间:{{time}},确认用户:#{{userId}}": "Confirmed at {{time}} by user #{{userId}}", + "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read carefully.", + "请输入以下文字以确认:": "Please type the following text to confirm:", + "请输入确认文案": "Type the confirmation text here", + "输入内容与要求文案不一致": "The entered text does not match the required text.", + "确认并启用": "Confirm and enable", + "合规声明确认成功": "Compliance confirmed successfully", + "确认失败": "Confirmation failed", + "兑换码功能已禁用,管理员需先确认合规声明。": "Redemption codes are disabled until the administrator confirms compliance terms.", + "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "Subscription plan creation and changes are locked until compliance terms are confirmed in payment settings.", + "邀请奖励划转已禁用,管理员需先确认合规声明。": "Invitation reward transfer is disabled until compliance terms are confirmed.", + "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "Non-zero invitation rewards require compliance confirmation in payment settings.", + "非零值需先确认合规声明": "Non-zero values require compliance confirmation first", + "我已阅读并理解上述合规提醒": "I have read and understood the above compliance reminder", + "知悉相关法律风险": "acknowledge the related legal risks", + "并确认自行承担部署": "confirm that I bear legal responsibility arising from deployment", + "运营和收费行为产生的法律责任": "operation and charging behavior", + ",": ", ", + "、": ", " } } diff --git a/web/classic/src/i18n/locales/zh-CN.json b/web/classic/src/i18n/locales/zh-CN.json index fcb0b78a..e1141b04 100644 --- a/web/classic/src/i18n/locales/zh-CN.json +++ b/web/classic/src/i18n/locales/zh-CN.json @@ -3778,6 +3778,36 @@ "缓存创建-1h": "缓存创建-1h", "见上方动态计费详情": "见上方动态计费详情", "含时间条件": "含时间条件", - "含请求条件": "含请求条件" + "含请求条件": "含请求条件", + "你已合法取得所接入模型 API、账号、密钥和额度的授权;": "你已合法取得所接入模型 API、账号、密钥和额度的授权;", + "你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力,不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力,不进行未经授权的转售、倒卖、分销或其他违规商业化使用。", + "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;", + "你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。", + "你理解并自行承担部署、运营和收费行为产生的法律责任。": "你理解并自行承担部署、运营和收费行为产生的法律责任。", + "你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。", + "我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任", + "需要确认合规声明": "需要确认合规声明", + "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。", + "确认合规声明": "确认合规声明", + "合规声明已确认": "合规声明已确认", + "确认时间:{{time}},确认用户:#{{userId}}": "确认时间:{{time}},确认用户:#{{userId}}", + "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。", + "请输入以下文字以确认:": "请输入以下文字以确认:", + "请输入确认文案": "请输入确认文案", + "输入内容与要求文案不一致": "输入内容与要求文案不一致", + "确认并启用": "确认并启用", + "合规声明确认成功": "合规声明确认成功", + "确认失败": "确认失败", + "兑换码功能已禁用,管理员需先确认合规声明。": "兑换码功能已禁用,管理员需先确认合规声明。", + "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。", + "邀请奖励划转已禁用,管理员需先确认合规声明。": "邀请奖励划转已禁用,管理员需先确认合规声明。", + "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。", + "非零值需先确认合规声明": "非零值需先确认合规声明", + "我已阅读并理解上述合规提醒": "我已阅读并理解上述合规提醒", + "知悉相关法律风险": "知悉相关法律风险", + "并确认自行承担部署": "并确认自行承担部署", + "运营和收费行为产生的法律责任": "运营和收费行为产生的法律责任", + ",": ",", + "、": "、" } } diff --git a/web/classic/src/i18n/locales/zh-TW.json b/web/classic/src/i18n/locales/zh-TW.json index 86a7cb35..3be48fb4 100644 --- a/web/classic/src/i18n/locales/zh-TW.json +++ b/web/classic/src/i18n/locales/zh-TW.json @@ -3642,6 +3642,36 @@ "默认用户消息": "你好", "默认补全倍率": "預設補全倍率", "阶梯计费(表达式解析失败)": "階梯計費(表達式解析失敗)", - "阶梯计费(未匹配到对应阶梯)": "階梯計費(未匹配到對應階梯)" + "阶梯计费(未匹配到对应阶梯)": "階梯計費(未匹配到對應階梯)", + "你已合法取得所接入模型 API、账号、密钥和额度的授权;": "你已合法取得所接入模型 API、账号、密钥和额度的授权;", + "你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力,不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "你承諾僅在已取得上游服務商、模型服務提供方或相關權利方合法授權的範圍內使用其 API、帳號、金鑰、額度及服務能力,不進行未經授權的轉售、倒賣、分銷或其他違規商業化使用。", + "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;", + "你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "你承諾不會利用本系統實施、協助實施或變相實施違反適用法律法規、監管要求、平台規則、社會公共利益或第三方合法權益的行為。", + "你理解并自行承担部署、运营和收费行为产生的法律责任。": "你理解并自行承担部署、运营和收费行为产生的法律责任。", + "你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "你理解本合規提醒僅用於風險提示,不構成法律意見、合規審查結論或對你使用本系統行為合法性的保證;你應根據實際業務場景自行諮詢專業法律或合規顧問。", + "我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "我已閱讀並理解上述合規提醒,知悉相關法律風險,並確認自行承擔部署、營運和收費行為產生的法律責任", + "需要确认合规声明": "需要确认合规声明", + "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。", + "确认合规声明": "确认合规声明", + "合规声明已确认": "合规声明已确认", + "确认时间:{{time}},确认用户:#{{userId}}": "确认时间:{{time}},确认用户:#{{userId}}", + "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。", + "请输入以下文字以确认:": "请输入以下文字以确认:", + "请输入确认文案": "请输入确认文案", + "输入内容与要求文案不一致": "输入内容与要求文案不一致", + "确认并启用": "确认并启用", + "合规声明确认成功": "合规声明确认成功", + "确认失败": "确认失败", + "兑换码功能已禁用,管理员需先确认合规声明。": "兑换码功能已禁用,管理员需先确认合规声明。", + "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。", + "邀请奖励划转已禁用,管理员需先确认合规声明。": "邀请奖励划转已禁用,管理员需先确认合规声明。", + "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。", + "非零值需先确认合规声明": "非零值需先确认合规声明", + "我已阅读并理解上述合规提醒": "我已閱讀並理解上述合規提醒", + "知悉相关法律风险": "知悉相關法律風險", + "并确认自行承担部署": "並確認自行承擔部署", + "运营和收费行为产生的法律责任": "營運和收費行為產生的法律責任", + ",": ",", + "、": "、" } } diff --git a/web/classic/src/i18n/locales/zh.json b/web/classic/src/i18n/locales/zh.json index 38c0b9ee..88ac70c1 100644 --- a/web/classic/src/i18n/locales/zh.json +++ b/web/classic/src/i18n/locales/zh.json @@ -2561,7 +2561,6 @@ "默认补全倍率": "默认补全倍率", "每日签到": "每日签到", "今日已签到,累计签到": "今日已签到,累计签到", - "天": "天", "每日签到可获得随机额度奖励": "每日签到可获得随机额度奖励", "今日已签到": "今日已签到", "立即签到": "立即签到", @@ -2595,6 +2594,36 @@ "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。": "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。", "提示:端点映射仅用于模型广场展示,不会影响模型真实调用。如需配置真实调用,请前往「渠道管理」。": "提示:端点映射仅用于模型广场展示,不会影响模型真实调用。如需配置真实调用,请前往「渠道管理」。", "阶梯计费(表达式解析失败)": "阶梯计费(表达式解析失败)", - "阶梯计费(未匹配到对应阶梯)": "阶梯计费(未匹配到对应阶梯)" + "阶梯计费(未匹配到对应阶梯)": "阶梯计费(未匹配到对应阶梯)", + "你已合法取得所接入模型 API、账号、密钥和额度的授权;": "你已合法取得所接入模型 API、账号、密钥和额度的授权;", + "你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力,不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力,不进行未经授权的转售、倒卖、分销或其他违规商业化使用。", + "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;", + "你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。", + "你理解并自行承担部署、运营和收费行为产生的法律责任。": "你理解并自行承担部署、运营和收费行为产生的法律责任。", + "你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。", + "我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任", + "需要确认合规声明": "需要确认合规声明", + "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。", + "确认合规声明": "确认合规声明", + "合规声明已确认": "合规声明已确认", + "确认时间:{{time}},确认用户:#{{userId}}": "确认时间:{{time}},确认用户:#{{userId}}", + "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。", + "请输入以下文字以确认:": "请输入以下文字以确认:", + "请输入确认文案": "请输入确认文案", + "输入内容与要求文案不一致": "输入内容与要求文案不一致", + "确认并启用": "确认并启用", + "合规声明确认成功": "合规声明确认成功", + "确认失败": "确认失败", + "兑换码功能已禁用,管理员需先确认合规声明。": "兑换码功能已禁用,管理员需先确认合规声明。", + "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。", + "邀请奖励划转已禁用,管理员需先确认合规声明。": "邀请奖励划转已禁用,管理员需先确认合规声明。", + "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。", + "非零值需先确认合规声明": "非零值需先确认合规声明", + "我已阅读并理解上述合规提醒": "我已阅读并理解上述合规提醒", + "知悉相关法律风险": "知悉相关法律风险", + "并确认自行承担部署": "并确认自行承担部署", + "运营和收费行为产生的法律责任": "运营和收费行为产生的法律责任", + ",": ",", + "、": "、" } } diff --git a/web/classic/src/pages/Setting/Operation/SettingsCreditLimit.jsx b/web/classic/src/pages/Setting/Operation/SettingsCreditLimit.jsx index 52476932..4a227091 100644 --- a/web/classic/src/pages/Setting/Operation/SettingsCreditLimit.jsx +++ b/web/classic/src/pages/Setting/Operation/SettingsCreditLimit.jsx @@ -18,7 +18,7 @@ For commercial licensing, please contact support@quantumnous.com */ import React, { useEffect, useState, useRef } from 'react'; -import { Button, Col, Form, Row, Spin } from '@douyinfe/semi-ui'; +import { Banner, Button, Col, Form, Row, Spin } from '@douyinfe/semi-ui'; import { useTranslation } from 'react-i18next'; import { compareObjects, @@ -40,6 +40,9 @@ export default function SettingsCreditLimit(props) { }); const refForm = useRef(); const [inputsRow, setInputsRow] = useState(inputs); + const complianceConfirmed = + props.options?.['payment_setting.compliance_confirmed'] === true || + props.options?.['payment_setting.compliance_confirmed'] === 'true'; function onSubmit() { const updateArray = compareObjects(inputs, inputsRow); @@ -90,6 +93,16 @@ export default function SettingsCreditLimit(props) { return ( <> + {!complianceConfirmed && ( + + )}
(refForm.current = formAPI)} @@ -137,7 +150,9 @@ export default function SettingsCreditLimit(props) { step={1} min={0} suffix={'Token'} - extraText={''} + extraText={ + !complianceConfirmed ? t('非零值需先确认合规声明') : '' + } placeholder={t('例如:2000')} onChange={(value) => setInputs({ @@ -156,7 +171,9 @@ export default function SettingsCreditLimit(props) { step={1} min={0} suffix={'Token'} - extraText={''} + extraText={ + !complianceConfirmed ? t('非零值需先确认合规声明') : '' + } placeholder={t('例如:1000')} onChange={(value) => setInputs({ diff --git a/web/default/src/components/risk-acknowledgement-dialog.tsx b/web/default/src/components/risk-acknowledgement-dialog.tsx new file mode 100644 index 00000000..19b77798 --- /dev/null +++ b/web/default/src/components/risk-acknowledgement-dialog.tsx @@ -0,0 +1,308 @@ +/* +Copyright (C) 2023-2026 QuantumNous + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +For commercial licensing, please contact support@quantumnous.com +*/ +import { useEffect, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { cn } from '@/lib/utils' +import { + AlertDialog, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from '@/components/ui/alert-dialog' +import { Button } from '@/components/ui/button' +import { Checkbox } from '@/components/ui/checkbox' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' + +type RequiredTextPart = { + type: 'input' | 'static' + text: string + placeholder?: string +} + +type NormalizedRequiredTextPart = RequiredTextPart & { + inputIndex?: number +} + +type RiskAcknowledgementDialogProps = { + open: boolean + onOpenChange: (open: boolean) => void + title: string + description?: React.ReactNode + items?: string[] + checklist?: string[] + requiredText?: string + requiredTextParts?: RequiredTextPart[] + inputPrompt?: string + inputPlaceholder?: string + mismatchHint?: string + confirmText?: string + cancelText?: string + destructive?: boolean + isLoading?: boolean + onConfirm: () => void + className?: string +} + +export function RiskAcknowledgementDialog({ + open, + onOpenChange, + title, + description, + items = [], + checklist = [], + requiredText = '', + requiredTextParts = [], + inputPrompt, + inputPlaceholder, + mismatchHint, + confirmText, + cancelText, + destructive = true, + isLoading = false, + onConfirm, + className, +}: RiskAcknowledgementDialogProps) { + const { t } = useTranslation() + const [checkedItems, setCheckedItems] = useState([]) + const [typedText, setTypedText] = useState('') + const [typedTextParts, setTypedTextParts] = useState([]) + + const normalizedRequiredTextParts = useMemo< + NormalizedRequiredTextPart[] + >(() => { + let inputIndex = 0 + return requiredTextParts.map((part) => { + if (part.type === 'input') { + const normalizedPart = { ...part, inputIndex } + inputIndex += 1 + return normalizedPart + } + return part + }) + }, [requiredTextParts]) + + const requiredTextInputCount = useMemo( + () => + normalizedRequiredTextParts.filter((part) => part.type === 'input') + .length, + [normalizedRequiredTextParts] + ) + const hasSegmentedRequiredText = requiredTextInputCount > 0 + const requiredTextToDisplay = hasSegmentedRequiredText + ? normalizedRequiredTextParts.map((part) => part.text).join('') + : requiredText + + useEffect(() => { + if (!open) return + setCheckedItems(Array(checklist.length).fill(false)) + setTypedText('') + setTypedTextParts(Array(requiredTextInputCount).fill('')) + }, [open, checklist.length, requiredTextInputCount]) + + const allChecked = useMemo(() => { + if (checklist.length === 0) return true + return ( + checkedItems.length === checklist.length && + checkedItems.every((checked) => checked) + ) + }, [checkedItems, checklist.length]) + + const typedMatched = useMemo(() => { + if (hasSegmentedRequiredText) { + return normalizedRequiredTextParts.every((part) => { + if (part.type === 'static') return true + return typedTextParts[part.inputIndex ?? 0]?.trim() === part.text.trim() + }) + } + if (!requiredText) return true + return typedText.trim() === requiredText.trim() + }, [ + hasSegmentedRequiredText, + normalizedRequiredTextParts, + requiredText, + typedText, + typedTextParts, + ]) + const hasTypedRequiredText = hasSegmentedRequiredText + ? typedTextParts.some((part) => part.trim() !== '') + : typedText.length > 0 + + const canConfirm = allChecked && typedMatched && !isLoading + + const handleChecklistChange = (index: number, checked: boolean) => { + setCheckedItems((previous) => { + const next = [...previous] + next[index] = checked + return next + }) + } + + const handleTextPartChange = (index: number, value: string) => { + setTypedTextParts((previous) => { + const next = [...previous] + next[index] = value + return next + }) + } + + return ( + + + + {title} + {description ? ( + } + className='mt-1 text-left leading-5' + > + {description} + + ) : null} + + +
+ {items.length > 0 ? ( +
    + {items.map((item) => ( +
  1. {item}
  2. + ))} +
+ ) : null} + + {checklist.length > 0 ? ( +
+ {checklist.map((item, index) => { + const id = `risk-acknowledgement-${index}` + return ( +
+ + handleChecklistChange(index, checked === true) + } + className='mt-0.5' + /> + +
+ ) + })} +
+ ) : null} + + {requiredTextToDisplay ? ( +
+ +
+ {requiredTextToDisplay} +
+ {hasSegmentedRequiredText ? ( +
+ {normalizedRequiredTextParts.map((part, index) => + part.type === 'static' ? ( + + {part.text} + + ) : ( + + handleTextPartChange( + part.inputIndex ?? 0, + event.target.value + ) + } + placeholder={ + part.placeholder ?? + part.text ?? + inputPlaceholder ?? + t('Type the confirmation text here') + } + autoFocus={open && part.inputIndex === 0} + onCopy={(event) => event.preventDefault()} + onCut={(event) => event.preventDefault()} + onPaste={(event) => event.preventDefault()} + onDrop={(event) => event.preventDefault()} + aria-invalid={hasTypedRequiredText && !typedMatched} + className='w-full font-mono sm:w-64' + /> + ) + )} +
+ ) : ( + setTypedText(event.target.value)} + placeholder={ + inputPlaceholder ?? t('Type the confirmation text here') + } + autoFocus={open} + onCopy={(event) => event.preventDefault()} + onCut={(event) => event.preventDefault()} + onPaste={(event) => event.preventDefault()} + onDrop={(event) => event.preventDefault()} + aria-invalid={hasTypedRequiredText && !typedMatched} + /> + )} + {hasTypedRequiredText && !typedMatched ? ( +

+ {mismatchHint ?? + t('The entered text does not match the required text.')} +

+ ) : null} +
+ ) : null} +
+ + + + {cancelText ?? t('Cancel')} + + + +
+
+ ) +} diff --git a/web/default/src/features/subscriptions/components/data-table-row-actions.tsx b/web/default/src/features/subscriptions/components/data-table-row-actions.tsx index 166009b9..ed5b5476 100644 --- a/web/default/src/features/subscriptions/components/data-table-row-actions.tsx +++ b/web/default/src/features/subscriptions/components/data-table-row-actions.tsx @@ -35,7 +35,7 @@ interface DataTableRowActionsProps { export function DataTableRowActions({ row }: DataTableRowActionsProps) { const { t } = useTranslation() - const { setOpen, setCurrentRow } = useSubscriptions() + const { setOpen, setCurrentRow, complianceConfirmed } = useSubscriptions() return ( @@ -46,6 +46,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) { { setCurrentRow(row.original) setOpen('update') @@ -55,6 +56,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) { {t('Edit')} { setCurrentRow(row.original) setOpen('toggle-status') diff --git a/web/default/src/features/subscriptions/components/subscriptions-primary-buttons.tsx b/web/default/src/features/subscriptions/components/subscriptions-primary-buttons.tsx index d5c0fed5..437e7ae9 100644 --- a/web/default/src/features/subscriptions/components/subscriptions-primary-buttons.tsx +++ b/web/default/src/features/subscriptions/components/subscriptions-primary-buttons.tsx @@ -23,10 +23,14 @@ import { useSubscriptions } from './subscriptions-provider' export function SubscriptionsPrimaryButtons() { const { t } = useTranslation() - const { setOpen } = useSubscriptions() + const { setOpen, complianceConfirmed } = useSubscriptions() return (
- diff --git a/web/default/src/features/subscriptions/components/subscriptions-provider.tsx b/web/default/src/features/subscriptions/components/subscriptions-provider.tsx index 6273bab6..454d5c39 100644 --- a/web/default/src/features/subscriptions/components/subscriptions-provider.tsx +++ b/web/default/src/features/subscriptions/components/subscriptions-provider.tsx @@ -18,8 +18,14 @@ For commercial licensing, please contact support@quantumnous.com */ import React, { useState } from 'react' import useDialogState from '@/hooks/use-dialog' +import { + getOptionValue, + useSystemOptions, +} from '@/features/system-settings/hooks/use-system-options' import { type PlanRecord, type SubscriptionsDialogType } from '../types' +const CURRENT_COMPLIANCE_TERMS_VERSION = 'v1' + type SubscriptionsContextType = { open: SubscriptionsDialogType | null setOpen: (str: SubscriptionsDialogType | null) => void @@ -27,6 +33,7 @@ type SubscriptionsContextType = { setCurrentRow: React.Dispatch> refreshTrigger: number triggerRefresh: () => void + complianceConfirmed: boolean } const SubscriptionsContext = @@ -40,6 +47,15 @@ export function SubscriptionsProvider({ const [open, setOpen] = useDialogState(null) const [currentRow, setCurrentRow] = useState(null) const [refreshTrigger, setRefreshTrigger] = useState(0) + const { data } = useSystemOptions() + const complianceOptions = getOptionValue(data?.data, { + 'payment_setting.compliance_confirmed': false, + 'payment_setting.compliance_terms_version': '', + }) + const complianceConfirmed = + complianceOptions['payment_setting.compliance_confirmed'] && + complianceOptions['payment_setting.compliance_terms_version'] === + CURRENT_COMPLIANCE_TERMS_VERSION const triggerRefresh = () => setRefreshTrigger((prev) => prev + 1) @@ -52,6 +68,7 @@ export function SubscriptionsProvider({ setCurrentRow, refreshTrigger, triggerRefresh, + complianceConfirmed, }} > {children} diff --git a/web/default/src/features/subscriptions/index.tsx b/web/default/src/features/subscriptions/index.tsx index 52552d0d..322042ff 100644 --- a/web/default/src/features/subscriptions/index.tsx +++ b/web/default/src/features/subscriptions/index.tsx @@ -22,13 +22,18 @@ import { Alert, AlertDescription } from '@/components/ui/alert' import { SectionPageLayout } from '@/components/layout' import { SubscriptionsDialogs } from './components/subscriptions-dialogs' import { SubscriptionsPrimaryButtons } from './components/subscriptions-primary-buttons' -import { SubscriptionsProvider } from './components/subscriptions-provider' +import { + SubscriptionsProvider, + useSubscriptions, +} from './components/subscriptions-provider' import { SubscriptionsTable } from './components/subscriptions-table' -export function Subscriptions() { +function SubscriptionsContent() { const { t } = useTranslation() + const { complianceConfirmed } = useSubscriptions() + return ( - + <> {t('Subscription Management')} @@ -50,11 +55,28 @@ export function Subscriptions() {
+ {!complianceConfirmed ? ( + + + {t( + 'Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.' + )} + + + ) : null} + + ) +} + +export function Subscriptions() { + return ( + + ) } diff --git a/web/default/src/features/system-settings/api.ts b/web/default/src/features/system-settings/api.ts index 5b5f7cec..86f41792 100644 --- a/web/default/src/features/system-settings/api.ts +++ b/web/default/src/features/system-settings/api.ts @@ -18,6 +18,7 @@ For commercial licensing, please contact support@quantumnous.com */ import { api } from '@/lib/api' import type { + ConfirmPaymentComplianceResponse, DeleteLogsResponse, FetchUpstreamRatiosRequest, SystemOptionsResponse, @@ -37,6 +38,14 @@ export async function updateSystemOption(request: UpdateOptionRequest) { return res.data } +export async function confirmPaymentCompliance() { + const res = await api.post( + '/api/option/payment_compliance', + { confirmed: true } + ) + return res.data +} + export async function deleteLogsBefore(targetTimestamp: number) { const res = await api.delete('/api/log/', { params: { target_timestamp: targetTimestamp }, diff --git a/web/default/src/features/system-settings/billing/index.tsx b/web/default/src/features/system-settings/billing/index.tsx index d0a059db..3b006f77 100644 --- a/web/default/src/features/system-settings/billing/index.tsx +++ b/web/default/src/features/system-settings/billing/index.tsx @@ -66,6 +66,11 @@ const defaultBillingSettings: BillingSettings = { PayMethods: '', 'payment_setting.amount_options': '', 'payment_setting.amount_discount': '', + 'payment_setting.compliance_confirmed': false, + 'payment_setting.compliance_terms_version': '', + 'payment_setting.compliance_confirmed_at': 0, + 'payment_setting.compliance_confirmed_by': 0, + 'payment_setting.compliance_confirmed_ip': '', StripeApiSecret: '', StripeWebhookSecret: '', StripePriceId: '', diff --git a/web/default/src/features/system-settings/billing/section-registry.tsx b/web/default/src/features/system-settings/billing/section-registry.tsx index 059438b8..ee829e23 100644 --- a/web/default/src/features/system-settings/billing/section-registry.tsx +++ b/web/default/src/features/system-settings/billing/section-registry.tsx @@ -71,6 +71,10 @@ const BILLING_SECTIONS = [ settings['quota_setting.enable_free_model_pre_consume'], }, }} + complianceConfirmed={ + (settings['payment_setting.compliance_confirmed'] ?? false) && + settings['payment_setting.compliance_terms_version'] === 'v1' + } /> ), }, @@ -187,6 +191,13 @@ const BILLING_SECTIONS = [ WaffoPancakeUnitPrice: settings.WaffoPancakeUnitPrice ?? 1, WaffoPancakeMinTopUp: settings.WaffoPancakeMinTopUp ?? 1, }} + complianceDefaults={{ + confirmed: settings['payment_setting.compliance_confirmed'] ?? false, + termsVersion: + settings['payment_setting.compliance_terms_version'] ?? '', + confirmedAt: settings['payment_setting.compliance_confirmed_at'] ?? 0, + confirmedBy: settings['payment_setting.compliance_confirmed_by'] ?? 0, + }} /> ), }, diff --git a/web/default/src/features/system-settings/general/quota-settings-section.tsx b/web/default/src/features/system-settings/general/quota-settings-section.tsx index a1a866f3..96a981dc 100644 --- a/web/default/src/features/system-settings/general/quota-settings-section.tsx +++ b/web/default/src/features/system-settings/general/quota-settings-section.tsx @@ -21,6 +21,7 @@ import * as z from 'zod' import type { Resolver } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { useTranslation } from 'react-i18next' +import { Alert, AlertDescription } from '@/components/ui/alert' import { Button } from '@/components/ui/button' import { Form, @@ -57,10 +58,12 @@ type QuotaFormValues = z.infer type QuotaSettingsSectionProps = { defaultValues: QuotaFormValues + complianceConfirmed?: boolean } export function QuotaSettingsSection({ defaultValues, + complianceConfirmed = true, }: QuotaSettingsSectionProps) { const { t } = useTranslation() const updateOption = useUpdateOption() @@ -97,6 +100,16 @@ export function QuotaSettingsSection({ > + {!complianceConfirmed ? ( + + + {t( + 'Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.' + )} + + + ) : null} + diff --git a/web/default/src/features/system-settings/integrations/payment-settings-section.tsx b/web/default/src/features/system-settings/integrations/payment-settings-section.tsx index bf2d29a4..e53721f0 100644 --- a/web/default/src/features/system-settings/integrations/payment-settings-section.tsx +++ b/web/default/src/features/system-settings/integrations/payment-settings-section.tsx @@ -20,8 +20,17 @@ import * as React from 'react' import * as z from 'zod' import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' -import { Code2, Eye } from 'lucide-react' +import { useMutation, useQueryClient } from '@tanstack/react-query' +import { Code2, Eye, ShieldAlert } from 'lucide-react' import { useTranslation } from 'react-i18next' +import { toast } from 'sonner' +import { cn } from '@/lib/utils' +import { + Alert, + AlertAction, + AlertDescription, + AlertTitle, +} from '@/components/ui/alert' import { Button } from '@/components/ui/button' import { Form, @@ -36,6 +45,8 @@ import { Input } from '@/components/ui/input' import { Separator } from '@/components/ui/separator' import { Switch } from '@/components/ui/switch' import { Textarea } from '@/components/ui/textarea' +import { RiskAcknowledgementDialog } from '@/components/risk-acknowledgement-dialog' +import { confirmPaymentCompliance } from '../api' import { SettingsSection } from '../components/settings-section' import { useUpdateOption } from '../hooks/use-update-option' import { AmountDiscountVisualEditor } from './amount-discount-visual-editor' @@ -125,18 +136,30 @@ const paymentSchema = z.object({ type PaymentFormValues = z.infer +const CURRENT_COMPLIANCE_TERMS_VERSION = 'v1' + +type PaymentComplianceDefaults = { + confirmed: boolean + termsVersion: string + confirmedAt: number + confirmedBy: number +} + type PaymentSettingsSectionProps = { defaultValues: PaymentFormValues waffoDefaultValues: WaffoSettingsValues waffoPancakeDefaultValues: WaffoPancakeSettingsValues + complianceDefaults: PaymentComplianceDefaults } export function PaymentSettingsSection({ defaultValues, waffoDefaultValues, waffoPancakeDefaultValues, + complianceDefaults, }: PaymentSettingsSectionProps) { const { t } = useTranslation() + const queryClient = useQueryClient() const updateOption = useUpdateOption() const initialRef = React.useRef(defaultValues) const defaultsSignature = React.useMemo( @@ -151,6 +174,81 @@ export function PaymentSettingsSection({ React.useState(true) const [creemProductsVisualMode, setCreemProductsVisualMode] = React.useState(true) + const [showComplianceDialog, setShowComplianceDialog] = React.useState(false) + + const complianceStatements = React.useMemo( + () => [ + t( + 'You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.' + ), + t( + 'You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.' + ), + t( + 'If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.' + ), + t( + 'You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.' + ), + t( + 'You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.' + ), + t( + 'You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.' + ), + ], + [t] + ) + + const complianceRequiredText = t( + 'I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.' + ) + const complianceRequiredTextParts = React.useMemo( + () => [ + { + type: 'input' as const, + text: t('I have read and understood the above compliance reminder'), + }, + { type: 'static' as const, text: t(',') }, + { + type: 'input' as const, + text: t('acknowledge the related legal risks'), + }, + { type: 'static' as const, text: t(',and ') }, + { + type: 'input' as const, + text: t( + 'confirm that I bear legal responsibility arising from deployment' + ), + }, + { type: 'static' as const, text: t('、') }, + { + type: 'input' as const, + text: t('operation and charging behavior'), + }, + ], + [t] + ) + + const complianceConfirmed = + complianceDefaults.confirmed && + complianceDefaults.termsVersion === CURRENT_COMPLIANCE_TERMS_VERSION + + const confirmComplianceMutation = useMutation({ + mutationFn: confirmPaymentCompliance, + onSuccess: (data) => { + if (data.success) { + toast.success(t('Compliance confirmed successfully')) + setShowComplianceDialog(false) + queryClient.invalidateQueries({ queryKey: ['system-options'] }) + } else { + toast.error(data.message || t('Failed to confirm compliance')) + } + }, + onError: (error: Error) => { + toast.error(error.message || t('Failed to confirm compliance')) + }, + }) const form = useForm({ resolver: zodResolver(paymentSchema), @@ -562,11 +660,77 @@ export function PaymentSettingsSection({ 'Configure recharge pricing and payment gateway integrations' )} > + {!complianceConfirmed ? ( + + + {t('Compliance confirmation required')} + +
+

+ {t( + 'Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.' + )} +

+
    + {complianceStatements.map((statement) => ( +
  1. {statement}
  2. + ))} +
+
+
+ + + +
+ ) : ( + + {t('Compliance confirmed')} + + {t('Confirmed at {{time}} by user #{{userId}}', { + time: complianceDefaults.confirmedAt + ? new Date( + complianceDefaults.confirmedAt * 1000 + ).toLocaleString() + : '-', + userId: complianceDefaults.confirmedBy || '-', + })} + + + )} + + confirmComplianceMutation.mutate()} + /> + {/* eslint-disable react-hooks/refs */}
diff --git a/web/default/src/features/system-settings/types.ts b/web/default/src/features/system-settings/types.ts index 717ba890..f6addabb 100644 --- a/web/default/src/features/system-settings/types.ts +++ b/web/default/src/features/system-settings/types.ts @@ -39,6 +39,17 @@ export type UpdateOptionResponse = { message: string } +export type ConfirmPaymentComplianceResponse = { + success: boolean + message: string + data?: { + confirmed: boolean + terms_version: string + confirmed_at: number + confirmed_by: number + } +} + export type DeleteLogsResponse = { success: boolean message: string @@ -215,6 +226,11 @@ export type BillingSettings = { PayMethods: string 'payment_setting.amount_options': string 'payment_setting.amount_discount': string + 'payment_setting.compliance_confirmed': boolean + 'payment_setting.compliance_terms_version': string + 'payment_setting.compliance_confirmed_at': number + 'payment_setting.compliance_confirmed_by': number + 'payment_setting.compliance_confirmed_ip': string StripeApiSecret: string StripeWebhookSecret: string StripePriceId: string diff --git a/web/default/src/features/wallet/components/affiliate-rewards-card.tsx b/web/default/src/features/wallet/components/affiliate-rewards-card.tsx index 9c59946e..b0865ad3 100644 --- a/web/default/src/features/wallet/components/affiliate-rewards-card.tsx +++ b/web/default/src/features/wallet/components/affiliate-rewards-card.tsx @@ -30,6 +30,7 @@ interface AffiliateRewardsCardProps { user: UserWalletData | null affiliateLink: string onTransfer: () => void + complianceConfirmed?: boolean loading?: boolean } @@ -37,6 +38,7 @@ export function AffiliateRewardsCard({ user, affiliateLink, onTransfer, + complianceConfirmed = true, loading, }: AffiliateRewardsCardProps) { const { t } = useTranslation() @@ -110,6 +112,7 @@ export function AffiliateRewardsCard({ {hasRewards && ( )}
+ {!complianceConfirmed ? ( +

+ {t( + 'Referral reward transfer is disabled until the administrator confirms compliance terms.' + )} +

+ ) : null} ) diff --git a/web/default/src/features/wallet/components/recharge-form-card.tsx b/web/default/src/features/wallet/components/recharge-form-card.tsx index 814e2a71..aa76428f 100644 --- a/web/default/src/features/wallet/components/recharge-form-card.tsx +++ b/web/default/src/features/wallet/components/recharge-form-card.tsx @@ -135,6 +135,7 @@ export function RechargeFormCard({ const hasWaffoPaymentMethods = Array.isArray(waffoPayMethods) && waffoPayMethods.length > 0 const minTopup = getMinTopupAmount(topupInfo) + const redemptionEnabled = topupInfo?.enable_redemption !== false if (loading) { return ( @@ -445,49 +446,59 @@ export function RechargeFormCard({ )} {/* Redemption Code Section */} -
-
- - -
-
- onRedemptionCodeChange(e.target.value)} - placeholder={t('Enter your redemption code')} - className='h-9 min-w-0' - /> - -
- {topupLink && ( -

- {t('Need a redemption code?')}{' '} - +

+ {t('Have a Code?')} + +
+
+ onRedemptionCodeChange(e.target.value)} + placeholder={t('Enter your redemption code')} + className='h-9 min-w-0' + /> + +
+ {topupLink && ( +

+ {t('Need a redemption code?')}{' '} + + {t('Get one here')} + + +

+ )} + + ) : ( + + + {t( + 'Redemption codes are disabled until the administrator confirms compliance terms.' + )} + + + )} ) } diff --git a/web/default/src/features/wallet/index.tsx b/web/default/src/features/wallet/index.tsx index 664ded8b..cf9a1a7a 100644 --- a/web/default/src/features/wallet/index.tsx +++ b/web/default/src/features/wallet/index.tsx @@ -319,6 +319,9 @@ export function Wallet(props: WalletProps) { user={user} affiliateLink={affiliateLink} onTransfer={() => setTransferDialogOpen(true)} + complianceConfirmed={ + topupInfo?.payment_compliance_confirmed !== false + } loading={affiliateLoading} /> diff --git a/web/default/src/features/wallet/types.ts b/web/default/src/features/wallet/types.ts index 6d91fb72..b88befb8 100644 --- a/web/default/src/features/wallet/types.ts +++ b/web/default/src/features/wallet/types.ts @@ -145,6 +145,12 @@ export interface TopupInfo { enable_waffo_pancake_topup?: boolean /** Minimum topup amount for Waffo Pancake */ waffo_pancake_min_topup?: number + /** Whether redemption code usage is enabled */ + enable_redemption?: boolean + /** Whether compliance confirmation has been completed */ + payment_compliance_confirmed?: boolean + /** Current compliance terms version */ + payment_compliance_terms_version?: string } /** diff --git a/web/default/src/i18n/locales/en.json b/web/default/src/i18n/locales/en.json index f64b69ef..7e34ec0a 100644 --- a/web/default/src/i18n/locales/en.json +++ b/web/default/src/i18n/locales/en.json @@ -1756,7 +1756,7 @@ "footer.columns.related.links.oneApi": "One API", "footer.columns.related.title": "Related Projects", "footer.defaultCopyright": "All rights reserved.", - "footer.new\u0061pi.projectAttributionSuffix": "All rights reserved. Designed and developed by the project contributors.", + "footer.newapi.projectAttributionSuffix": "All rights reserved. Designed and developed by the project contributors.", "For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "For channels added after May 10, 2025, no need to remove \".\" from model names during deployment", "For private deployments, format: https://fastgpt.run/api/openapi": "For private deployments, format: https://fastgpt.run/api/openapi", "Force a syntactically valid JSON response": "Force a syntactically valid JSON response", @@ -4423,6 +4423,37 @@ "Zero retention": "Zero retention", "Zhipu": "Zhipu", "Zhipu V4": "Zhipu V4", - "Zoom": "Zoom" + "Zoom": "Zoom", + "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.", + "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.", + "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.": "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.", + "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.", + "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.", + "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.", + "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.", + "Please type the following text to confirm:": "Please type the following text to confirm:", + "Type the confirmation text here": "Type the confirmation text here", + "The entered text does not match the required text.": "The entered text does not match the required text.", + "Compliance confirmation required": "Compliance confirmation required", + "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.": "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.", + "Confirm compliance": "Confirm compliance", + "Compliance confirmed": "Compliance confirmed", + "Confirmed at {{time}} by user #{{userId}}": "Confirmed at {{time}} by user #{{userId}}", + "Confirm compliance terms": "Confirm compliance terms", + "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.", + "Confirm and enable": "Confirm and enable", + "Compliance confirmed successfully": "Compliance confirmed successfully", + "Failed to confirm compliance": "Failed to confirm compliance", + "Redemption codes are disabled until the administrator confirms compliance terms.": "Redemption codes are disabled until the administrator confirms compliance terms.", + "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.": "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.", + "Referral reward transfer is disabled until the administrator confirms compliance terms.": "Referral reward transfer is disabled until the administrator confirms compliance terms.", + "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.", + "I have read and understood the above compliance reminder": "I have read and understood the above compliance reminder", + "acknowledge the related legal risks": "acknowledge the related legal risks", + "confirm that I bear legal responsibility arising from deployment": "confirm that I bear legal responsibility arising from deployment", + "operation and charging behavior": "operation and charging behavior", + ",": ", ", + ",and ": ", and ", + "、": ", " } } diff --git a/web/default/src/i18n/locales/fr.json b/web/default/src/i18n/locales/fr.json index 82f65528..0a682ccb 100644 --- a/web/default/src/i18n/locales/fr.json +++ b/web/default/src/i18n/locales/fr.json @@ -1756,7 +1756,7 @@ "footer.columns.related.links.oneApi": "One API", "footer.columns.related.title": "Projets liés", "footer.defaultCopyright": "Tous droits réservés.", - "footer.new\u0061pi.projectAttributionSuffix": "Tous droits réservés. Conçu et développé par les contributeurs du projet.", + "footer.newapi.projectAttributionSuffix": "Tous droits réservés. Conçu et développé par les contributeurs du projet.", "For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "Pour les canaux ajoutés après le 10 mai 2025, pas besoin de supprimer \".\" des noms de modèles lors du déploiement", "For private deployments, format: https://fastgpt.run/api/openapi": "Pour les déploiements privés, format : https://fastgpt.run/api/openapi", "Force a syntactically valid JSON response": "Imposer une réponse JSON syntaxiquement valide", @@ -4423,6 +4423,37 @@ "Zero retention": "Aucune rétention", "Zhipu": "Zhipu", "Zhipu V4": "Zhipu V4", - "Zoom": "Zoom" + "Zoom": "Zoom", + "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.", + "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.", + "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.": "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.", + "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.", + "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.", + "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.", + "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.", + "Please type the following text to confirm:": "Please type the following text to confirm:", + "Type the confirmation text here": "Type the confirmation text here", + "The entered text does not match the required text.": "The entered text does not match the required text.", + "Compliance confirmation required": "Compliance confirmation required", + "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.": "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.", + "Confirm compliance": "Confirm compliance", + "Compliance confirmed": "Compliance confirmed", + "Confirmed at {{time}} by user #{{userId}}": "Confirmed at {{time}} by user #{{userId}}", + "Confirm compliance terms": "Confirm compliance terms", + "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.", + "Confirm and enable": "Confirm and enable", + "Compliance confirmed successfully": "Compliance confirmed successfully", + "Failed to confirm compliance": "Failed to confirm compliance", + "Redemption codes are disabled until the administrator confirms compliance terms.": "Redemption codes are disabled until the administrator confirms compliance terms.", + "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.": "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.", + "Referral reward transfer is disabled until the administrator confirms compliance terms.": "Referral reward transfer is disabled until the administrator confirms compliance terms.", + "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.", + "I have read and understood the above compliance reminder": "I have read and understood the above compliance reminder", + "acknowledge the related legal risks": "acknowledge the related legal risks", + "confirm that I bear legal responsibility arising from deployment": "confirm that I bear legal responsibility arising from deployment", + "operation and charging behavior": "operation and charging behavior", + ",": ", ", + ",and ": ", and ", + "、": ", " } } diff --git a/web/default/src/i18n/locales/ja.json b/web/default/src/i18n/locales/ja.json index 3f9fa56b..bbacbda5 100644 --- a/web/default/src/i18n/locales/ja.json +++ b/web/default/src/i18n/locales/ja.json @@ -1756,7 +1756,7 @@ "footer.columns.related.links.oneApi": "1つのAPI", "footer.columns.related.title": "関連プロジェクト", "footer.defaultCopyright": "すべての権利を留保します。", - "footer.new\u0061pi.projectAttributionSuffix": "すべての権利を留保します。プロジェクトコントリビューターにより設計・開発されています。", + "footer.newapi.projectAttributionSuffix": "すべての権利を留保します。プロジェクトコントリビューターにより設計・開発されています。", "For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "2025 年 5 月 10 日以降に追加されたチャネルの場合、デプロイ時にモデル名から「.」を削除する必要はありません", "For private deployments, format: https://fastgpt.run/api/openapi": "プライベートデプロイメントの場合、形式: https://fastgpt.run/api/openapi", "Force a syntactically valid JSON response": "構文的に有効な JSON 応答を強制", @@ -4423,6 +4423,37 @@ "Zero retention": "データ保持なし", "Zhipu": "Zhipu", "Zhipu V4": "Zhipu V 4", - "Zoom": "ズーム" + "Zoom": "ズーム", + "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.", + "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.", + "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.": "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.", + "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.", + "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.", + "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.", + "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.", + "Please type the following text to confirm:": "Please type the following text to confirm:", + "Type the confirmation text here": "Type the confirmation text here", + "The entered text does not match the required text.": "The entered text does not match the required text.", + "Compliance confirmation required": "Compliance confirmation required", + "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.": "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.", + "Confirm compliance": "Confirm compliance", + "Compliance confirmed": "Compliance confirmed", + "Confirmed at {{time}} by user #{{userId}}": "Confirmed at {{time}} by user #{{userId}}", + "Confirm compliance terms": "Confirm compliance terms", + "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.", + "Confirm and enable": "Confirm and enable", + "Compliance confirmed successfully": "Compliance confirmed successfully", + "Failed to confirm compliance": "Failed to confirm compliance", + "Redemption codes are disabled until the administrator confirms compliance terms.": "Redemption codes are disabled until the administrator confirms compliance terms.", + "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.": "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.", + "Referral reward transfer is disabled until the administrator confirms compliance terms.": "Referral reward transfer is disabled until the administrator confirms compliance terms.", + "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.", + "I have read and understood the above compliance reminder": "I have read and understood the above compliance reminder", + "acknowledge the related legal risks": "acknowledge the related legal risks", + "confirm that I bear legal responsibility arising from deployment": "confirm that I bear legal responsibility arising from deployment", + "operation and charging behavior": "operation and charging behavior", + ",": ", ", + ",and ": ", and ", + "、": ", " } } diff --git a/web/default/src/i18n/locales/ru.json b/web/default/src/i18n/locales/ru.json index 6f7fe4ae..2022e562 100644 --- a/web/default/src/i18n/locales/ru.json +++ b/web/default/src/i18n/locales/ru.json @@ -1756,7 +1756,7 @@ "footer.columns.related.links.oneApi": "Один API", "footer.columns.related.title": "Связанные проекты", "footer.defaultCopyright": "Все права защищены.", - "footer.new\u0061pi.projectAttributionSuffix": "Все права защищены. Разработано участниками проекта.", + "footer.newapi.projectAttributionSuffix": "Все права защищены. Разработано участниками проекта.", "For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "Для каналов, добавленных после 10 мая 2025 г., не нужно удалять \".\" из имён моделей при развёртывании", "For private deployments, format: https://fastgpt.run/api/openapi": "Для частных развертываний, формат: https://fastgpt.run/api/openapi", "Force a syntactically valid JSON response": "Принудительно возвращать синтаксически корректный JSON", @@ -4423,6 +4423,37 @@ "Zero retention": "Без хранения данных", "Zhipu": "Zhipu", "Zhipu V4": "Zhipu V4", - "Zoom": "Zoom" + "Zoom": "Zoom", + "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.", + "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.", + "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.": "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.", + "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.", + "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.", + "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.", + "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.", + "Please type the following text to confirm:": "Please type the following text to confirm:", + "Type the confirmation text here": "Type the confirmation text here", + "The entered text does not match the required text.": "The entered text does not match the required text.", + "Compliance confirmation required": "Compliance confirmation required", + "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.": "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.", + "Confirm compliance": "Confirm compliance", + "Compliance confirmed": "Compliance confirmed", + "Confirmed at {{time}} by user #{{userId}}": "Confirmed at {{time}} by user #{{userId}}", + "Confirm compliance terms": "Confirm compliance terms", + "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.", + "Confirm and enable": "Confirm and enable", + "Compliance confirmed successfully": "Compliance confirmed successfully", + "Failed to confirm compliance": "Failed to confirm compliance", + "Redemption codes are disabled until the administrator confirms compliance terms.": "Redemption codes are disabled until the administrator confirms compliance terms.", + "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.": "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.", + "Referral reward transfer is disabled until the administrator confirms compliance terms.": "Referral reward transfer is disabled until the administrator confirms compliance terms.", + "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.", + "I have read and understood the above compliance reminder": "I have read and understood the above compliance reminder", + "acknowledge the related legal risks": "acknowledge the related legal risks", + "confirm that I bear legal responsibility arising from deployment": "confirm that I bear legal responsibility arising from deployment", + "operation and charging behavior": "operation and charging behavior", + ",": ", ", + ",and ": ", and ", + "、": ", " } } diff --git a/web/default/src/i18n/locales/vi.json b/web/default/src/i18n/locales/vi.json index 89212546..66f458f3 100644 --- a/web/default/src/i18n/locales/vi.json +++ b/web/default/src/i18n/locales/vi.json @@ -1756,7 +1756,7 @@ "footer.columns.related.links.oneApi": "One API", "footer.columns.related.title": "Các Dự Án Liên Quan", "footer.defaultCopyright": "Bản quyền được bảo lưu.", - "footer.new\u0061pi.projectAttributionSuffix": "Bản quyền được bảo lưu. Được thiết kế và phát triển bởi các cộng tác viên dự án.", + "footer.newapi.projectAttributionSuffix": "Bản quyền được bảo lưu. Được thiết kế và phát triển bởi các cộng tác viên dự án.", "For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "Đối với các kênh được thêm sau ngày 10 tháng 5 năm 2025, không cần loại bỏ \".\" khỏi tên mô hình trong quá trình triển khai", "For private deployments, format: https://fastgpt.run/api/openapi": "Đối với các triển khai riêng tư, định dạng: https://fastgpt.run/api/openapi", "Force a syntactically valid JSON response": "Buộc phản hồi JSON hợp lệ về cú pháp", @@ -4423,6 +4423,37 @@ "Zero retention": "Không lưu dữ liệu", "Zhipu": "Zhipu", "Zhipu V4": "Zhipu V4", - "Zoom": "Zoom" + "Zoom": "Zoom", + "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.", + "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.", + "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.": "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.", + "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.", + "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.", + "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.", + "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.", + "Please type the following text to confirm:": "Please type the following text to confirm:", + "Type the confirmation text here": "Type the confirmation text here", + "The entered text does not match the required text.": "The entered text does not match the required text.", + "Compliance confirmation required": "Compliance confirmation required", + "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.": "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.", + "Confirm compliance": "Confirm compliance", + "Compliance confirmed": "Compliance confirmed", + "Confirmed at {{time}} by user #{{userId}}": "Confirmed at {{time}} by user #{{userId}}", + "Confirm compliance terms": "Confirm compliance terms", + "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.", + "Confirm and enable": "Confirm and enable", + "Compliance confirmed successfully": "Compliance confirmed successfully", + "Failed to confirm compliance": "Failed to confirm compliance", + "Redemption codes are disabled until the administrator confirms compliance terms.": "Redemption codes are disabled until the administrator confirms compliance terms.", + "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.": "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.", + "Referral reward transfer is disabled until the administrator confirms compliance terms.": "Referral reward transfer is disabled until the administrator confirms compliance terms.", + "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.", + "I have read and understood the above compliance reminder": "I have read and understood the above compliance reminder", + "acknowledge the related legal risks": "acknowledge the related legal risks", + "confirm that I bear legal responsibility arising from deployment": "confirm that I bear legal responsibility arising from deployment", + "operation and charging behavior": "operation and charging behavior", + ",": ", ", + ",and ": ", and ", + "、": ", " } } diff --git a/web/default/src/i18n/locales/zh.json b/web/default/src/i18n/locales/zh.json index fd76b1aa..1d2e8675 100644 --- a/web/default/src/i18n/locales/zh.json +++ b/web/default/src/i18n/locales/zh.json @@ -1756,7 +1756,7 @@ "footer.columns.related.links.oneApi": "One API", "footer.columns.related.title": "相关项目", "footer.defaultCopyright": "版权所有。", - "footer.new\u0061pi.projectAttributionSuffix": "版权所有,由项目贡献者设计与开发。", + "footer.newapi.projectAttributionSuffix": "版权所有,由项目贡献者设计与开发。", "For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "对于 2025 年 5 月 10 日之后添加的渠道,在部署时无需从模型名称中移除 \".\"", "For private deployments, format: https://fastgpt.run/api/openapi": "对于私有部署,格式为:https://fastgpt.run/api/openapi", "Force a syntactically valid JSON response": "强制返回语法合法的 JSON", @@ -4423,6 +4423,37 @@ "Zero retention": "零数据保留", "Zhipu": "智谱", "Zhipu V4": "智谱 V4", - "Zoom": "缩放" + "Zoom": "缩放", + "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.": "你已合法取得所接入模型 API、账号、密钥和额度的授权。", + "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.": "你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力,不进行未经授权的转售、倒卖、分销或其他违规商业化使用。", + "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.": "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务。", + "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.": "你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。", + "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.": "你理解并自行承担部署、运营和收费行为产生的法律责任。", + "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.": "你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。", + "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.": "我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任", + "Please type the following text to confirm:": "请输入以下文字以确认:", + "Type the confirmation text here": "请输入确认文案", + "The entered text does not match the required text.": "输入内容与要求文案不一致。", + "Compliance confirmation required": "需要确认合规声明", + "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.": "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。", + "Confirm compliance": "确认合规声明", + "Compliance confirmed": "合规声明已确认", + "Confirmed at {{time}} by user #{{userId}}": "确认时间:{{time}},确认用户:#{{userId}}", + "Confirm compliance terms": "确认合规声明", + "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.": "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。", + "Confirm and enable": "确认并启用", + "Compliance confirmed successfully": "合规声明确认成功", + "Failed to confirm compliance": "确认失败", + "Redemption codes are disabled until the administrator confirms compliance terms.": "兑换码功能已禁用,管理员需先确认合规声明。", + "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.": "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。", + "Referral reward transfer is disabled until the administrator confirms compliance terms.": "邀请奖励划转已禁用,管理员需先确认合规声明。", + "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。", + "I have read and understood the above compliance reminder": "我已阅读并理解上述合规提醒", + "acknowledge the related legal risks": "知悉相关法律风险", + "confirm that I bear legal responsibility arising from deployment": "并确认自行承担部署", + "operation and charging behavior": "运营和收费行为产生的法律责任", + ",": ",", + ",and ": ",", + "、": "、" } }