diff --git a/.gitignore b/.gitignore
index 62444e4..dec0d6c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,12 @@ redis_data/
.gomodcache/
.gocache-temp
.gopath
+
+# Build artifacts
+*.exe
+
+# Test artifacts
+.playwright-cli/
+
+# Misc
+
diff --git a/controller/misc.go b/controller/misc.go
index 6d40272..5f2987f 100644
--- a/controller/misc.go
+++ b/controller/misc.go
@@ -106,6 +106,8 @@ func GetStatus(c *gin.Context) {
"usd_exchange_rate": operation_setting.USDExchangeRate,
"price": operation_setting.Price,
"stripe_unit_price": setting.StripeUnitPrice,
+ "enable_alipay_topup": setting.AlipayEnabled && setting.AlipayAppId != "" && setting.AlipayPrivateKey != "",
+ "enable_wechatpay_topup": setting.WechatPayEnabled && setting.WechatPayAppId != "" && setting.WechatPayMchId != "" && setting.WechatPayApiV3Key != "",
// 面板启用开关
"api_info_enabled": cs.ApiInfoEnabled,
diff --git a/controller/topup.go b/controller/topup.go
index a35de0d..3aece5e 100644
--- a/controller/topup.go
+++ b/controller/topup.go
@@ -87,6 +87,46 @@ func GetTopUpInfo(c *gin.Context) {
}
}
+ // If Alipay is enabled, add it to payment methods
+ if setting.AlipayEnabled && setting.AlipayAppId != "" && setting.AlipayPrivateKey != "" {
+ hasAlipay := false
+ for _, method := range payMethods {
+ if method["type"] == "alipay" {
+ hasAlipay = true
+ break
+ }
+ }
+ if !hasAlipay {
+ alipayMethod := map[string]string{
+ "name": "Alipay (支付宝)",
+ "type": "alipay",
+ "color": "rgba(var(--semi-blue-5), 1)",
+ "min_topup": strconv.Itoa(setting.AlipayMinTopUp),
+ }
+ payMethods = append(payMethods, alipayMethod)
+ }
+ }
+
+ // If WeChat Pay is enabled, add it to payment methods
+ if setting.WechatPayEnabled && setting.WechatPayAppId != "" && setting.WechatPayMchId != "" {
+ hasWechat := false
+ for _, method := range payMethods {
+ if method["type"] == "wxpay" {
+ hasWechat = true
+ break
+ }
+ }
+ if !hasWechat {
+ wechatMethod := map[string]string{
+ "name": "WeChat Pay (微信支付)",
+ "type": "wxpay",
+ "color": "rgba(var(--semi-green-5), 1)",
+ "min_topup": strconv.Itoa(setting.WechatPayMinTopUp),
+ }
+ payMethods = append(payMethods, wechatMethod)
+ }
+ }
+
data := gin.H{
"enable_online_topup": (operation_setting.OnlinePayProvider == "yipay" &&
(operation_setting.YipayRequestURL != "" || operation_setting.PayAddress != "") &&
@@ -97,9 +137,13 @@ func GetTopUpInfo(c *gin.Context) {
operation_setting.PayAddress != "" &&
operation_setting.EpayId != "" &&
operation_setting.EpayKey != ""),
- "enable_stripe_topup": setting.StripeApiSecret != "" && setting.StripeWebhookSecret != "" && setting.StripePriceId != "",
- "enable_creem_topup": setting.CreemApiKey != "" && setting.CreemProducts != "[]",
- "enable_waffo_topup": enableWaffo,
+ "enable_stripe_topup": setting.StripeApiSecret != "" && setting.StripeWebhookSecret != "" && setting.StripePriceId != "",
+ "enable_creem_topup": setting.CreemApiKey != "" && setting.CreemProducts != "[]",
+ "enable_waffo_topup": enableWaffo,
+ "enable_alipay_topup": setting.AlipayEnabled && setting.AlipayAppId != "" && setting.AlipayPrivateKey != "",
+ "enable_wechatpay_topup": setting.WechatPayEnabled && setting.WechatPayAppId != "" && setting.WechatPayMchId != "" && setting.WechatPayApiV3Key != "",
+ "alipay_min_topup": setting.AlipayMinTopUp,
+ "wechatpay_min_topup": setting.WechatPayMinTopUp,
"waffo_pay_methods": func() interface{} {
if enableWaffo {
return setting.GetWaffoPayMethods()
diff --git a/controller/topup_alipay.go b/controller/topup_alipay.go
new file mode 100644
index 0000000..ba0b719
--- /dev/null
+++ b/controller/topup_alipay.go
@@ -0,0 +1,463 @@
+package controller
+
+import (
+ "crypto"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/sha256"
+ "crypto/x509"
+ "encoding/base64"
+ "encoding/pem"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "net/url"
+ "sort"
+ "strings"
+ "time"
+
+ "github.com/QuantumNous/new-api/common"
+ "github.com/QuantumNous/new-api/logger"
+ "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/setting/system_setting"
+
+ "github.com/gin-gonic/gin"
+ "github.com/shopspring/decimal"
+)
+
+const (
+ PaymentMethodAlipay = "alipay"
+)
+
+var alipayAdaptor = &AlipayAdaptor{}
+
+// AlipayPayRequest represents a payment request for Alipay checkout.
+type AlipayPayRequest struct {
+ Amount int64 `json:"amount"`
+ PaymentMethod string `json:"payment_method"`
+}
+
+type AlipayAdaptor struct{}
+
+func getAlipayAppId() string {
+ if setting.AlipaySandbox && setting.AlipaySandboxAppId != "" {
+ return setting.AlipaySandboxAppId
+ }
+ return setting.AlipayAppId
+}
+
+func getAlipayPrivateKey() string {
+ if setting.AlipaySandbox && setting.AlipaySandboxPrivateKey != "" {
+ return setting.AlipaySandboxPrivateKey
+ }
+ return setting.AlipayPrivateKey
+}
+
+func getAlipayPublicKey() string {
+ return setting.AlipayPublicKey
+}
+
+func getAlipayGatewayURL() string {
+ if setting.AlipaySandbox {
+ return "https://openapi-sandbox.dl.alipaydev.com/gateway.do"
+ }
+ return "https://openapi.alipay.com/gateway.do"
+}
+
+func getAlipayMinTopUp() int64 {
+ minTopup := setting.AlipayMinTopUp
+ if operation_setting.GetQuotaDisplayType() == operation_setting.QuotaDisplayTypeTokens {
+ minTopup = minTopup * int(common.QuotaPerUnit)
+ }
+ return int64(minTopup)
+}
+
+func getAlipayPayMoney(amount float64, group string) float64 {
+ originalAmount := amount
+ if operation_setting.GetQuotaDisplayType() == operation_setting.QuotaDisplayTypeTokens {
+ amount = amount / common.QuotaPerUnit
+ }
+ topupGroupRatio := common.GetTopupGroupRatio(group)
+ if topupGroupRatio == 0 {
+ topupGroupRatio = 1
+ }
+ discount := 1.0
+ if ds, ok := operation_setting.GetPaymentSetting().AmountDiscount[int(originalAmount)]; ok {
+ if ds > 0 {
+ discount = ds
+ }
+ }
+ return amount * setting.AlipayUnitPrice * topupGroupRatio * discount
+}
+
+// alipaySign generates RSA2 signature for Alipay params
+func alipaySign(params map[string]string, privateKey string) (string, error) {
+ keys := make([]string, 0, len(params))
+ for k := range params {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
+ var buf strings.Builder
+ for i, k := range keys {
+ v := params[k]
+ if v == "" {
+ continue
+ }
+ if i > 0 {
+ buf.WriteString("&")
+ }
+ buf.WriteString(k)
+ buf.WriteString("=")
+ buf.WriteString(v)
+ }
+
+ signStr := buf.String()
+
+ block, _ := pem.Decode([]byte(privateKey))
+ if block == nil {
+ return "", fmt.Errorf("failed to decode private key PEM")
+ }
+
+ priv, err := x509.ParsePKCS8PrivateKey(block.Bytes)
+ if err != nil {
+ priv, err = x509.ParsePKCS1PrivateKey(block.Bytes)
+ if err != nil {
+ return "", fmt.Errorf("failed to parse private key: %v", err)
+ }
+ }
+
+ rsaKey, ok := priv.(*rsa.PrivateKey)
+ if !ok {
+ return "", fmt.Errorf("private key is not RSA key")
+ }
+
+ hashed := sha256.Sum256([]byte(signStr))
+ signature, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.SHA256, hashed[:])
+ if err != nil {
+ return "", fmt.Errorf("sign failed: %v", err)
+ }
+
+ return base64.StdEncoding.EncodeToString(signature), nil
+}
+
+// alipayVerify verifies Alipay notification signature
+func alipayVerify(params map[string]string, publicKey string) bool {
+ sign, ok := params["sign"]
+ if !ok || sign == "" {
+ return false
+ }
+
+ keys := make([]string, 0, len(params))
+ for k := range params {
+ if k == "sign" || k == "sign_type" {
+ continue
+ }
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
+ var buf strings.Builder
+ for i, k := range keys {
+ v := params[k]
+ if v == "" {
+ continue
+ }
+ if i > 0 {
+ buf.WriteString("&")
+ }
+ buf.WriteString(k)
+ buf.WriteString("=")
+ buf.WriteString(v)
+ }
+
+ signStr := buf.String()
+
+ sigBytes, err := base64.StdEncoding.DecodeString(sign)
+ if err != nil {
+ log.Printf("Alipay verify: base64 decode sign failed: %v", err)
+ return false
+ }
+
+ block, _ := pem.Decode([]byte(publicKey))
+ if block == nil {
+ log.Printf("Alipay verify: failed to decode public key PEM")
+ return false
+ }
+
+ pub, err := x509.ParsePKIXPublicKey(block.Bytes)
+ if err != nil {
+ log.Printf("Alipay verify: failed to parse public key: %v", err)
+ return false
+ }
+
+ rsaPub, ok := pub.(*rsa.PublicKey)
+ if !ok {
+ log.Printf("Alipay verify: public key is not RSA key")
+ return false
+ }
+
+ hashed := sha256.Sum256([]byte(signStr))
+ err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, hashed[:], sigBytes)
+ return err == nil
+}
+
+func (a *AlipayAdaptor) RequestPay(c *gin.Context, req *AlipayPayRequest) {
+ if req.PaymentMethod != PaymentMethodAlipay {
+ c.JSON(200, gin.H{"message": "error", "data": "不支持的支付渠道"})
+ return
+ }
+
+ minTopup := getAlipayMinTopUp()
+ if req.Amount < minTopup {
+ c.JSON(200, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", minTopup)})
+ return
+ }
+ if req.Amount > 100000 {
+ c.JSON(200, gin.H{"message": "error", "data": "充值数量不能大于 100000"})
+ return
+ }
+
+ id := c.GetInt("id")
+ user, err := model.GetUserById(id, false)
+ if err != nil || user == nil {
+ c.JSON(200, gin.H{"message": "error", "data": "用户不存在"})
+ return
+ }
+
+ group, err := model.GetUserGroup(id, true)
+ if err != nil {
+ c.JSON(200, gin.H{"message": "error", "data": "获取用户分组失败"})
+ return
+ }
+
+ payMoney := getAlipayPayMoney(float64(req.Amount), group)
+ if payMoney <= 0.01 {
+ c.JSON(200, gin.H{"message": "error", "data": "充值金额过低"})
+ return
+ }
+
+ tradeNo := fmt.Sprintf("ALI%d%s%d", id, common.GetRandomString(12), time.Now().Unix())
+
+ topUp := &model.TopUp{
+ UserId: id,
+ Amount: req.Amount,
+ Money: payMoney,
+ TradeNo: tradeNo,
+ PaymentMethod: PaymentMethodAlipay,
+ CreateTime: time.Now().Unix(),
+ Status: common.TopUpStatusPending,
+ }
+ err = topUp.Insert()
+ if err != nil {
+ log.Printf("创建Alipay订单失败: %v", err)
+ c.JSON(200, gin.H{"message": "error", "data": "创建订单失败"})
+ return
+ }
+
+ payUrl, err := genAlipayPagePayUrl(tradeNo, payMoney, user.Email)
+ if err != nil {
+ log.Printf("生成Alipay支付链接失败: %v", err)
+ c.JSON(200, gin.H{"message": "error", "data": "拉起支付失败"})
+ return
+ }
+
+ log.Printf("Alipay订单创建成功 - 用户ID: %d, 订单号: %s, 金额: %.2f", id, tradeNo, payMoney)
+
+ c.JSON(200, gin.H{
+ "message": "success",
+ "data": gin.H{
+ "pay_url": payUrl,
+ "trade_no": tradeNo,
+ },
+ })
+}
+
+// genAlipayPagePayUrl generates an Alipay page payment URL
+func genAlipayPagePayUrl(tradeNo string, payMoney float64, email string) (string, error) {
+ appId := getAlipayAppId()
+ privateKey := getAlipayPrivateKey()
+
+ if appId == "" || privateKey == "" {
+ return "", fmt.Errorf("Alipay not configured")
+ }
+
+ callBackAddress := service.GetCallbackAddress()
+ notifyUrl := strings.TrimRight(callBackAddress, "/") + "/api/alipay/notify"
+ if setting.AlipayNotifyUrl != "" {
+ notifyUrl = setting.AlipayNotifyUrl
+ }
+
+ returnUrl := system_setting.ServerAddress + "/console/log"
+ if setting.AlipayReturnUrl != "" {
+ returnUrl = setting.AlipayReturnUrl
+ }
+
+ amountStr := fmt.Sprintf("%.2f", payMoney)
+
+ params := map[string]string{
+ "app_id": appId,
+ "method": "alipay.trade.page.pay",
+ "charset": "utf-8",
+ "sign_type": "RSA2",
+ "timestamp": time.Now().Format("2006-01-02 15:04:05"),
+ "version": "1.0",
+ "notify_url": notifyUrl,
+ "return_url": returnUrl,
+ "biz_content": fmt.Sprintf(`{"out_trade_no":"%s","product_code":"FAST_INSTANT_TRADE_PAY","total_amount":"%s","subject":"TokenFactory TopUp"}`, tradeNo, amountStr),
+ }
+
+ sign, err := alipaySign(params, privateKey)
+ if err != nil {
+ return "", err
+ }
+ params["sign"] = sign
+
+ gatewayUrl := getAlipayGatewayURL()
+ queryParams := url.Values{}
+ for k, v := range params {
+ queryParams.Set(k, v)
+ }
+
+ return gatewayUrl + "?" + queryParams.Encode(), nil
+}
+
+// AlipayNotify handles the Alipay async notification
+func AlipayNotify(c *gin.Context) {
+ bodyBytes, err := io.ReadAll(c.Request.Body)
+ if err != nil {
+ log.Printf("读取Alipay通知失败: %v", err)
+ c.String(http.StatusOK, "failure")
+ return
+ }
+ defer c.Request.Body.Close()
+
+ bodyStr := string(bodyBytes)
+ formValues, err := url.ParseQuery(bodyStr)
+ if err != nil {
+ log.Printf("解析Alipay通知参数失败: %v", err)
+ c.String(http.StatusOK, "failure")
+ return
+ }
+
+ params := make(map[string]string)
+ for k, v := range formValues {
+ if len(v) > 0 {
+ params[k] = v[0]
+ }
+ }
+
+ log.Printf("收到Alipay异步通知: trade_no=%s, out_trade_no=%s, trade_status=%s",
+ params["trade_no"], params["out_trade_no"], params["trade_status"])
+
+ publicKey := getAlipayPublicKey()
+ if publicKey != "" && !alipayVerify(params, publicKey) {
+ log.Printf("Alipay通知签名验证失败: %v", params)
+ c.String(http.StatusOK, "failure")
+ return
+ }
+
+ tradeStatus := params["trade_status"]
+ outTradeNo := params["out_trade_no"]
+
+ if tradeStatus == "TRADE_SUCCESS" || tradeStatus == "TRADE_FINISHED" {
+ LockOrder(outTradeNo)
+ defer UnlockOrder(outTradeNo)
+
+ topUp := model.GetTopUpByTradeNo(outTradeNo)
+ if topUp == nil {
+ log.Printf("Alipay通知未找到订单: %s", outTradeNo)
+ c.String(http.StatusOK, "failure")
+ return
+ }
+
+ if topUp.Status != common.TopUpStatusPending {
+ log.Printf("Alipay订单状态错误: %s, 当前状态: %s", outTradeNo, topUp.Status)
+ c.String(http.StatusOK, "success")
+ return
+ }
+
+ dAmount := decimal.NewFromInt(topUp.Amount)
+ dQuotaPerUnit := decimal.NewFromFloat(common.QuotaPerUnit)
+ quotaToAdd := int(dAmount.Mul(dQuotaPerUnit).IntPart())
+
+ if quotaToAdd <= 0 {
+ log.Printf("Alipay无效的充值额度: %s", outTradeNo)
+ c.String(http.StatusOK, "failure")
+ return
+ }
+
+ topUp.CompleteTime = common.GetTimestamp()
+ topUp.Status = common.TopUpStatusSuccess
+ if err := topUp.Update(); err != nil {
+ log.Printf("Alipay更新订单失败: %v", err)
+ c.String(http.StatusOK, "failure")
+ return
+ }
+
+ if err := model.IncreaseUserQuota(topUp.UserId, quotaToAdd, true); err != nil {
+ log.Printf("Alipay更新用户额度失败: %v", err)
+ c.String(http.StatusOK, "failure")
+ return
+ }
+
+ model.RecordLog(topUp.UserId, model.LogTypeTopup,
+ fmt.Sprintf("使用支付宝充值成功,充值金额: %v,支付金额:%.2f",
+ logger.LogQuota(quotaToAdd), topUp.Money))
+ model.ApplyAffiliateTopupReward(topUp.UserId, quotaToAdd)
+
+ log.Printf("Alipay充值成功 - 订单: %s, 额度: %d", outTradeNo, quotaToAdd)
+ }
+
+ c.String(http.StatusOK, "success")
+}
+
+// AlipayReturn handles the Alipay synchronous return (page redirect)
+func AlipayReturn(c *gin.Context) {
+ params := make(map[string]string)
+ for k, v := range c.Request.URL.Query() {
+ if len(v) > 0 {
+ params[k] = v[0]
+ }
+ }
+
+ log.Printf("Alipay同步返回: out_trade_no=%s, trade_no=%s", params["out_trade_no"], params["trade_no"])
+
+ publicKey := getAlipayPublicKey()
+ if publicKey != "" && !alipayVerify(params, publicKey) {
+ log.Printf("Alipay返回签名验证失败")
+ c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup")
+ return
+ }
+
+ outTradeNo := params["out_trade_no"]
+ if outTradeNo != "" {
+ topUp := model.GetTopUpByTradeNo(outTradeNo)
+ if topUp != nil && topUp.Status == common.TopUpStatusSuccess {
+ c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/log")
+ return
+ }
+ }
+
+ c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup")
+}
+
+// RequestAlipayPay handles the Alipay payment request from frontend
+func RequestAlipayPay(c *gin.Context) {
+ if !setting.AlipayEnabled {
+ c.JSON(200, gin.H{"message": "error", "data": "支付宝支付未启用"})
+ return
+ }
+
+ var req AlipayPayRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ c.JSON(200, gin.H{"message": "error", "data": "参数错误"})
+ return
+ }
+
+ alipayAdaptor.RequestPay(c, &req)
+}
diff --git a/controller/topup_wechat.go b/controller/topup_wechat.go
new file mode 100644
index 0000000..9396894
--- /dev/null
+++ b/controller/topup_wechat.go
@@ -0,0 +1,515 @@
+package controller
+
+import (
+ "crypto"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/sha256"
+ "crypto/x509"
+ "encoding/base64"
+ "encoding/pem"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "strings"
+ "time"
+
+ "github.com/QuantumNous/new-api/common"
+ "github.com/QuantumNous/new-api/logger"
+ "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/gin-gonic/gin"
+ "github.com/shopspring/decimal"
+)
+
+const (
+ PaymentMethodWechatPay = "wxpay"
+)
+
+var wechatPayAdaptor = &WechatPayAdaptor{}
+
+// WechatPayRequest represents a payment request for WeChat Pay.
+type WechatPayRequest struct {
+ Amount int64 `json:"amount"`
+ PaymentMethod string `json:"payment_method"`
+}
+
+type WechatPayAdaptor struct{}
+
+func getWechatPayAppId() string {
+ return setting.WechatPayAppId
+}
+
+func getWechatPayMchId() string {
+ if setting.WechatPaySandbox && setting.WechatPaySandboxMchId != "" {
+ return setting.WechatPaySandboxMchId
+ }
+ return setting.WechatPayMchId
+}
+
+func getWechatPayApiV3Key() string {
+ if setting.WechatPaySandbox && setting.WechatPaySandboxApiV3Key != "" {
+ return setting.WechatPaySandboxApiV3Key
+ }
+ return setting.WechatPayApiV3Key
+}
+
+func getWechatPayPrivateKey() string {
+ if setting.WechatPaySandbox && setting.WechatPaySandboxPrivateKey != "" {
+ return setting.WechatPaySandboxPrivateKey
+ }
+ return setting.WechatPayPrivateKey
+}
+
+func getWechatPayAPIBaseURL() string {
+ if setting.WechatPaySandbox {
+ return "https://apihk.mch.weixin.qq.com/sandboxnew"
+ }
+ return "https://api.mch.weixin.qq.com"
+}
+
+func getWechatPayMinTopUp() int64 {
+ minTopup := setting.WechatPayMinTopUp
+ if operation_setting.GetQuotaDisplayType() == operation_setting.QuotaDisplayTypeTokens {
+ minTopup = minTopup * int(common.QuotaPerUnit)
+ }
+ return int64(minTopup)
+}
+
+func getWechatPayPayMoney(amount float64, group string) float64 {
+ originalAmount := amount
+ if operation_setting.GetQuotaDisplayType() == operation_setting.QuotaDisplayTypeTokens {
+ amount = amount / common.QuotaPerUnit
+ }
+ topupGroupRatio := common.GetTopupGroupRatio(group)
+ if topupGroupRatio == 0 {
+ topupGroupRatio = 1
+ }
+ discount := 1.0
+ if ds, ok := operation_setting.GetPaymentSetting().AmountDiscount[int(originalAmount)]; ok {
+ if ds > 0 {
+ discount = ds
+ }
+ }
+ return amount * setting.WechatPayUnitPrice * topupGroupRatio * discount
+}
+
+// wechatPaySign generates the WeChat Pay v3 signature
+func wechatPaySign(method string, urlPath string, body string, timestamp string, nonce string, privateKeyStr string) (string, error) {
+ message := fmt.Sprintf("%s\n%s\n%s\n%s\n", method, urlPath, timestamp, nonce)
+ if body != "" && body != "{}" {
+ message = fmt.Sprintf("%s\n%s\n%s\n%s\n", method, urlPath, timestamp, nonce) + body
+ } else {
+ message = fmt.Sprintf("%s\n%s\n%s\n%s\n", method, urlPath, timestamp, nonce)
+ }
+
+ block, _ := pem.Decode([]byte(privateKeyStr))
+ if block == nil {
+ return "", fmt.Errorf("failed to decode private key PEM")
+ }
+
+ priv, err := x509.ParsePKCS8PrivateKey(block.Bytes)
+ if err != nil {
+ priv, err = x509.ParsePKCS1PrivateKey(block.Bytes)
+ if err != nil {
+ return "", fmt.Errorf("failed to parse private key: %v", err)
+ }
+ }
+
+ rsaKey, ok := priv.(*rsa.PrivateKey)
+ if !ok {
+ return "", fmt.Errorf("private key is not RSA key")
+ }
+
+ hashed := sha256.Sum256([]byte(message))
+ signature, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.SHA256, hashed[:])
+ if err != nil {
+ return "", fmt.Errorf("sign failed: %v", err)
+ }
+
+ return base64.StdEncoding.EncodeToString(signature), nil
+}
+
+// wechatPayBuildAuthHeader builds the WeChat Pay v3 Authorization header
+func wechatPayBuildAuthHeader(method string, urlPath string, body string) (string, error) {
+ mchId := getWechatPayMchId()
+ privateKey := getWechatPayPrivateKey()
+ certSerial := setting.WechatPayCertSerial
+
+ if mchId == "" || privateKey == "" || certSerial == "" {
+ return "", fmt.Errorf("WeChat Pay not fully configured")
+ }
+
+ timestamp := fmt.Sprintf("%d", time.Now().Unix())
+ nonce := common.GetRandomString(32)
+
+ sign, err := wechatPaySign(method, urlPath, body, timestamp, nonce, privateKey)
+ if err != nil {
+ return "", err
+ }
+
+ auth := fmt.Sprintf(`WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%s",serial_no="%s",signature="%s"`,
+ mchId, nonce, timestamp, certSerial, sign)
+
+ return auth, nil
+}
+
+// wechatPayPost sends a POST request to WeChat Pay API v3
+func wechatPayPost(path string, requestBody interface{}) (map[string]interface{}, error) {
+ jsonBytes, err := common.Marshal(requestBody)
+ if err != nil {
+ return nil, fmt.Errorf("marshal request body failed: %v", err)
+ }
+
+ bodyStr := string(jsonBytes)
+ authHeader, err := wechatPayBuildAuthHeader("POST", path, bodyStr)
+ if err != nil {
+ return nil, err
+ }
+
+ apiBaseURL := getWechatPayAPIBaseURL()
+ req, err := http.NewRequest("POST", apiBaseURL+path, strings.NewReader(bodyStr))
+ if err != nil {
+ return nil, fmt.Errorf("create request failed: %v", err)
+ }
+
+ req.Header.Set("Content-Type", "application/json")
+ req.Header.Set("Authorization", authHeader)
+ req.Header.Set("Accept", "application/json")
+ req.Header.Set("User-Agent", "TokenFactory")
+
+ client := &http.Client{Timeout: 30 * time.Second}
+ resp, err := client.Do(req)
+ if err != nil {
+ return nil, fmt.Errorf("request failed: %v", err)
+ }
+ defer resp.Body.Close()
+
+ respBody, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, fmt.Errorf("read response failed: %v", err)
+ }
+
+ if resp.StatusCode != 200 && resp.StatusCode != 201 && resp.StatusCode != 204 {
+ return nil, fmt.Errorf("WeChat Pay API error: HTTP %d, body: %s", resp.StatusCode, string(respBody))
+ }
+
+ var result map[string]interface{}
+ if len(respBody) > 0 {
+ if err := common.Unmarshal(respBody, &result); err != nil {
+ return nil, fmt.Errorf("parse response failed: %v", err)
+ }
+ }
+
+ return result, nil
+}
+
+// wechatPayDecryptResource decrypts WeChat Pay notification resource
+func wechatPayDecryptResource(ciphertext, associatedData, nonce, apiV3Key string) ([]byte, error) {
+ aesKey, err := base64.StdEncoding.DecodeString(apiV3Key)
+ if err != nil {
+ return nil, fmt.Errorf("decode api v3 key failed: %v", err)
+ }
+
+ cipherData, err := base64.StdEncoding.DecodeString(ciphertext)
+ if err != nil {
+ return nil, fmt.Errorf("decode ciphertext failed: %v", err)
+ }
+
+ nonceBytes := []byte(nonce)
+ adBytes := []byte(associatedData)
+
+ block, err := aes.NewCipher(aesKey)
+ if err != nil {
+ return nil, fmt.Errorf("create cipher failed: %v", err)
+ }
+
+ aesGCM, err := cipher.NewGCM(block)
+ if err != nil {
+ return nil, fmt.Errorf("create GCM failed: %v", err)
+ }
+
+ plaintext, err := aesGCM.Open(nil, nonceBytes, cipherData, adBytes)
+ if err != nil {
+ return nil, fmt.Errorf("decrypt failed: %v", err)
+ }
+
+ return plaintext, nil
+}
+
+func (a *WechatPayAdaptor) RequestPay(c *gin.Context, req *WechatPayRequest) {
+ if req.PaymentMethod != PaymentMethodWechatPay {
+ c.JSON(200, gin.H{"message": "error", "data": "不支持的支付渠道"})
+ return
+ }
+
+ minTopup := getWechatPayMinTopUp()
+ if req.Amount < minTopup {
+ c.JSON(200, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", minTopup)})
+ return
+ }
+ if req.Amount > 100000 {
+ c.JSON(200, gin.H{"message": "error", "data": "充值数量不能大于 100000"})
+ return
+ }
+
+ id := c.GetInt("id")
+ user, err := model.GetUserById(id, false)
+ if err != nil || user == nil {
+ c.JSON(200, gin.H{"message": "error", "data": "用户不存在"})
+ return
+ }
+
+ group, err := model.GetUserGroup(id, true)
+ if err != nil {
+ c.JSON(200, gin.H{"message": "error", "data": "获取用户分组失败"})
+ return
+ }
+
+ payMoney := getWechatPayPayMoney(float64(req.Amount), group)
+ if payMoney <= 0.01 {
+ c.JSON(200, gin.H{"message": "error", "data": "充值金额过低"})
+ return
+ }
+
+ tradeNo := fmt.Sprintf("WX%d%s%d", id, common.GetRandomString(12), time.Now().Unix())
+
+ topUp := &model.TopUp{
+ UserId: id,
+ Amount: req.Amount,
+ Money: payMoney,
+ TradeNo: tradeNo,
+ PaymentMethod: PaymentMethodWechatPay,
+ CreateTime: time.Now().Unix(),
+ Status: common.TopUpStatusPending,
+ }
+ err = topUp.Insert()
+ if err != nil {
+ log.Printf("创建WeChat Pay订单失败: %v", err)
+ c.JSON(200, gin.H{"message": "error", "data": "创建订单失败"})
+ return
+ }
+
+ // Create WeChat Pay order via API v3
+ callBackAddress := service.GetCallbackAddress()
+ notifyUrl := strings.TrimRight(callBackAddress, "/") + "/api/wechatpay/notify"
+ if setting.WechatPayNotifyUrl != "" {
+ notifyUrl = setting.WechatPayNotifyUrl
+ }
+
+ amountFen := int64(payMoney * 100) // Convert yuan to fen
+
+ log.Printf("WeChat Pay创建订单 - 用户: %d, 订单: %s, 金额: %.2f元 (%d分)", id, tradeNo, payMoney, amountFen)
+
+ payParams := map[string]interface{}{
+ "mchid": getWechatPayMchId(),
+ "out_trade_no": tradeNo,
+ "appid": getWechatPayAppId(),
+ "description": "TokenFactory TopUp",
+ "notify_url": notifyUrl,
+ "amount": map[string]interface{}{
+ "total": amountFen,
+ "currency": "CNY",
+ },
+ "payer": map[string]interface{}{
+ "openid": "", // Will be obtained from WeChat OAuth
+ },
+ "scene_info": map[string]interface{}{
+ "payer_client_ip": c.ClientIP(),
+ },
+ }
+
+ // For H5 payment
+ payParams["scene_info"].(map[string]interface{})["h5_info"] = map[string]interface{}{
+ "type": "Wap",
+ }
+
+ result, err := wechatPayPost("/v3/pay/transactions/h5", payParams)
+ if err != nil {
+ log.Printf("WeChat Pay创建订单失败: %v", err)
+ c.JSON(200, gin.H{"message": "error", "data": "拉起支付失败"})
+ return
+ }
+
+ h5Url := ""
+ if result != nil {
+ if urlStr, ok := result["h5_url"].(string); ok {
+ h5Url = urlStr
+ }
+ }
+
+ log.Printf("WeChat Pay订单创建成功 - 用户ID: %d, 订单号: %s, 金额: %.2f", id, tradeNo, payMoney)
+
+ c.JSON(200, gin.H{
+ "message": "success",
+ "data": gin.H{
+ "pay_url": h5Url,
+ "trade_no": tradeNo,
+ },
+ })
+}
+
+// WechatPayNotify handles the WeChat Pay payment notification
+func WechatPayNotify(c *gin.Context) {
+ bodyBytes, err := io.ReadAll(c.Request.Body)
+ if err != nil {
+ log.Printf("读取WeChat Pay通知失败: %v", err)
+ c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "read body failed"})
+ return
+ }
+ defer c.Request.Body.Close()
+
+ log.Printf("收到WeChat Pay通知: %s", string(bodyBytes))
+
+ var notifyData struct {
+ Id string `json:"id"`
+ CreateTime string `json:"create_time"`
+ EventType string `json:"event_type"`
+ ResourceType string `json:"resource_type"`
+ Resource struct {
+ Algorithm string `json:"algorithm"`
+ Ciphertext string `json:"ciphertext"`
+ AssociatedData string `json:"associated_data"`
+ Nonce string `json:"nonce"`
+ OriginalType string `json:"original_type"`
+ } `json:"resource"`
+ Summary string `json:"summary"`
+ }
+
+ if err := common.Unmarshal(bodyBytes, ¬ifyData); err != nil {
+ log.Printf("解析WeChat Pay通知失败: %v", err)
+ c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "parse failed"})
+ return
+ }
+
+ apiV3Key := getWechatPayApiV3Key()
+ if apiV3Key == "" {
+ log.Printf("WeChat Pay API v3 key not configured")
+ c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "not configured"})
+ return
+ }
+
+ // Decrypt the resource
+ plaintext, err := wechatPayDecryptResource(
+ notifyData.Resource.Ciphertext,
+ notifyData.Resource.AssociatedData,
+ notifyData.Resource.Nonce,
+ apiV3Key,
+ )
+ if err != nil {
+ log.Printf("WeChat Pay通知解密失败: %v", err)
+ c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "decrypt failed"})
+ return
+ }
+
+ log.Printf("WeChat Pay通知解密成功: %s", string(plaintext))
+
+ var paymentResult struct {
+ AppId string `json:"appid"`
+ MchId string `json:"mchid"`
+ OutTradeNo string `json:"out_trade_no"`
+ TransactionId string `json:"transaction_id"`
+ TradeType string `json:"trade_type"`
+ TradeState string `json:"trade_state"`
+ TradeStateDesc string `json:"trade_state_desc"`
+ BankType string `json:"bank_type"`
+ Attach string `json:"attach"`
+ SuccessTime string `json:"success_time"`
+ Payer struct {
+ OpenId string `json:"openid"`
+ } `json:"payer"`
+ Amount struct {
+ Total int `json:"total"`
+ PayerTotal int `json:"payer_total"`
+ Currency string `json:"currency"`
+ PayerCurrency string `json:"payer_currency"`
+ } `json:"amount"`
+ }
+
+ if err := common.Unmarshal(plaintext, &paymentResult); err != nil {
+ log.Printf("解析WeChat Pay支付结果失败: %v", err)
+ c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "parse result failed"})
+ return
+ }
+
+ tradeNo := paymentResult.OutTradeNo
+ log.Printf("WeChat Pay支付结果: out_trade_no=%s, transaction_id=%s, trade_state=%s",
+ tradeNo, paymentResult.TransactionId, paymentResult.TradeState)
+
+ if paymentResult.TradeState != "SUCCESS" {
+ log.Printf("WeChat Pay支付未成功: %s, trade_state=%s", tradeNo, paymentResult.TradeState)
+ c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": "ok"})
+ return
+ }
+
+ LockOrder(tradeNo)
+ defer UnlockOrder(tradeNo)
+
+ topUp := model.GetTopUpByTradeNo(tradeNo)
+ if topUp == nil {
+ log.Printf("WeChat Pay通知未找到订单: %s", tradeNo)
+ c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "order not found"})
+ return
+ }
+
+ if topUp.Status != common.TopUpStatusPending {
+ log.Printf("WeChat Pay订单状态错误: %s, 当前状态: %s", tradeNo, topUp.Status)
+ c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": "ok"})
+ return
+ }
+
+ dAmount := decimal.NewFromInt(topUp.Amount)
+ dQuotaPerUnit := decimal.NewFromFloat(common.QuotaPerUnit)
+ quotaToAdd := int(dAmount.Mul(dQuotaPerUnit).IntPart())
+
+ if quotaToAdd <= 0 {
+ log.Printf("WeChat Pay无效的充值额度: %s", tradeNo)
+ c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "invalid quota"})
+ return
+ }
+
+ topUp.CompleteTime = common.GetTimestamp()
+ topUp.Status = common.TopUpStatusSuccess
+ if err := topUp.Update(); err != nil {
+ log.Printf("WeChat Pay更新订单失败: %v", err)
+ c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "update failed"})
+ return
+ }
+
+ if err := model.IncreaseUserQuota(topUp.UserId, quotaToAdd, true); err != nil {
+ log.Printf("WeChat Pay更新用户额度失败: %v", err)
+ c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "quota update failed"})
+ return
+ }
+
+ model.RecordLog(topUp.UserId, model.LogTypeTopup,
+ fmt.Sprintf("使用微信支付充值成功,充值金额: %v,支付金额:%.2f",
+ logger.LogQuota(quotaToAdd), topUp.Money))
+ model.ApplyAffiliateTopupReward(topUp.UserId, quotaToAdd)
+
+ log.Printf("WeChat Pay充值成功 - 订单: %s, 额度: %d", tradeNo, quotaToAdd)
+
+ c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": "ok"})
+}
+
+// RequestWechatPay handles the WeChat Pay payment request from frontend
+func RequestWechatPay(c *gin.Context) {
+ if !setting.WechatPayEnabled {
+ c.JSON(200, gin.H{"message": "error", "data": "微信支付未启用"})
+ return
+ }
+
+ var req WechatPayRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ c.JSON(200, gin.H{"message": "error", "data": "参数错误"})
+ return
+ }
+
+ wechatPayAdaptor.RequestPay(c, &req)
+}
diff --git a/model/option.go b/model/option.go
index d60d0cb..f65a4ff 100644
--- a/model/option.go
+++ b/model/option.go
@@ -1,4 +1,4 @@
-package model
+package model
import (
"strconv"
@@ -131,6 +131,31 @@ func InitOptionMap() {
common.OptionMap["WaffoUnitPrice"] = strconv.FormatFloat(setting.WaffoUnitPrice, 'f', -1, 64)
common.OptionMap["WaffoMinTopUp"] = strconv.Itoa(setting.WaffoMinTopUp)
common.OptionMap["WaffoPayMethods"] = setting.WaffoPayMethods2JsonString()
+ common.OptionMap["AlipayEnabled"] = strconv.FormatBool(setting.AlipayEnabled)
+ common.OptionMap["AlipayAppId"] = setting.AlipayAppId
+ common.OptionMap["AlipayPrivateKey"] = setting.AlipayPrivateKey
+ common.OptionMap["AlipayPublicKey"] = setting.AlipayPublicKey
+ common.OptionMap["AlipayNotifyUrl"] = setting.AlipayNotifyUrl
+ common.OptionMap["AlipayReturnUrl"] = setting.AlipayReturnUrl
+ common.OptionMap["AlipayUnitPrice"] = strconv.FormatFloat(setting.AlipayUnitPrice, 'f', -1, 64)
+ common.OptionMap["AlipayMinTopUp"] = strconv.Itoa(setting.AlipayMinTopUp)
+ common.OptionMap["AlipaySandbox"] = strconv.FormatBool(setting.AlipaySandbox)
+ common.OptionMap["AlipaySandboxAppId"] = setting.AlipaySandboxAppId
+ common.OptionMap["AlipaySandboxPrivateKey"] = setting.AlipaySandboxPrivateKey
+ common.OptionMap["WechatPayEnabled"] = strconv.FormatBool(setting.WechatPayEnabled)
+ common.OptionMap["WechatPayAppId"] = setting.WechatPayAppId
+ common.OptionMap["WechatPayMchId"] = setting.WechatPayMchId
+ common.OptionMap["WechatPayApiV3Key"] = setting.WechatPayApiV3Key
+ common.OptionMap["WechatPayPrivateKey"] = setting.WechatPayPrivateKey
+ common.OptionMap["WechatPayCertSerial"] = setting.WechatPayCertSerial
+ common.OptionMap["WechatPayNotifyUrl"] = setting.WechatPayNotifyUrl
+ common.OptionMap["WechatPayReturnUrl"] = setting.WechatPayReturnUrl
+ common.OptionMap["WechatPayUnitPrice"] = strconv.FormatFloat(setting.WechatPayUnitPrice, 'f', -1, 64)
+ common.OptionMap["WechatPayMinTopUp"] = strconv.Itoa(setting.WechatPayMinTopUp)
+ common.OptionMap["WechatPaySandbox"] = strconv.FormatBool(setting.WechatPaySandbox)
+ common.OptionMap["WechatPaySandboxMchId"] = setting.WechatPaySandboxMchId
+ common.OptionMap["WechatPaySandboxApiV3Key"] = setting.WechatPaySandboxApiV3Key
+ common.OptionMap["WechatPaySandboxPrivateKey"] = setting.WechatPaySandboxPrivateKey
common.OptionMap["TopupGroupRatio"] = common.TopupGroupRatio2JSONString()
common.OptionMap["Chats"] = setting.Chats2JsonString()
common.OptionMap["AutoGroups"] = setting.AutoGroups2JsonString()
@@ -501,6 +526,56 @@ func updateOptionMap(key string, value string) (err error) {
case "WaffoApiKey":
setting.WaffoApiKey = value
case "WaffoPrivateKey":
+ case "AlipayEnabled":
+ setting.AlipayEnabled = value == "true"
+ case "AlipayAppId":
+ setting.AlipayAppId = value
+ case "AlipayPrivateKey":
+ setting.AlipayPrivateKey = value
+ case "AlipayPublicKey":
+ setting.AlipayPublicKey = value
+ case "AlipayNotifyUrl":
+ setting.AlipayNotifyUrl = value
+ case "AlipayReturnUrl":
+ setting.AlipayReturnUrl = value
+ case "AlipayUnitPrice":
+ setting.AlipayUnitPrice, _ = strconv.ParseFloat(value, 64)
+ case "AlipayMinTopUp":
+ setting.AlipayMinTopUp, _ = strconv.Atoi(value)
+ case "AlipaySandbox":
+ setting.AlipaySandbox = value == "true"
+ case "AlipaySandboxAppId":
+ setting.AlipaySandboxAppId = value
+ case "AlipaySandboxPrivateKey":
+ setting.AlipaySandboxPrivateKey = value
+ case "WechatPayEnabled":
+ setting.WechatPayEnabled = value == "true"
+ case "WechatPayAppId":
+ setting.WechatPayAppId = value
+ case "WechatPayMchId":
+ setting.WechatPayMchId = value
+ case "WechatPayApiV3Key":
+ setting.WechatPayApiV3Key = value
+ case "WechatPayPrivateKey":
+ setting.WechatPayPrivateKey = value
+ case "WechatPayCertSerial":
+ setting.WechatPayCertSerial = value
+ case "WechatPayNotifyUrl":
+ setting.WechatPayNotifyUrl = value
+ case "WechatPayReturnUrl":
+ setting.WechatPayReturnUrl = value
+ case "WechatPayUnitPrice":
+ setting.WechatPayUnitPrice, _ = strconv.ParseFloat(value, 64)
+ case "WechatPayMinTopUp":
+ setting.WechatPayMinTopUp, _ = strconv.Atoi(value)
+ case "WechatPaySandbox":
+ setting.WechatPaySandbox = value == "true"
+ case "WechatPaySandboxMchId":
+ setting.WechatPaySandboxMchId = value
+ case "WechatPaySandboxApiV3Key":
+ setting.WechatPaySandboxApiV3Key = value
+ case "WechatPaySandboxPrivateKey":
+ setting.WechatPaySandboxPrivateKey = value
setting.WaffoPrivateKey = value
case "WaffoPublicCert":
setting.WaffoPublicCert = value
diff --git a/router/api-router.go b/router/api-router.go
index 021d351..837bcdd 100644
--- a/router/api-router.go
+++ b/router/api-router.go
@@ -1,4 +1,4 @@
-package router
+package router
import (
"github.com/QuantumNous/new-api/controller"
@@ -104,6 +104,10 @@ func SetApiRouter(router *gin.Engine) {
apiRouter.POST("/creem/webhook", controller.CreemWebhook)
apiRouter.POST("/waffo/webhook", controller.WaffoWebhook)
+ apiRouter.POST("/alipay/notify", controller.AlipayNotify)
+ apiRouter.GET("/alipay/return", controller.AlipayReturn)
+ apiRouter.POST("/alipay/return", controller.AlipayReturn)
+ apiRouter.POST("/wechatpay/notify", controller.WechatPayNotify)
// Universal secure verification routes
apiRouter.POST("/verify", middleware.UserAuth(), middleware.CriticalRateLimit(), controller.UniversalVerify)
@@ -165,6 +169,8 @@ func SetApiRouter(router *gin.Engine) {
selfRoute.POST("/waffo/pay", middleware.CriticalRateLimit(), controller.RequestWaffoPay)
selfRoute.POST("/aff_transfer", controller.TransferAffQuota)
selfRoute.GET("/aff_invitees", controller.GetAffInvitees)
+ selfRoute.POST("/alipay/pay", middleware.CriticalRateLimit(), controller.RequestAlipayPay)
+ selfRoute.POST("/wechatpay/pay", middleware.CriticalRateLimit(), controller.RequestWechatPay)
selfRoute.PUT("/setting", controller.UpdateUserSetting)
selfRoute.POST("/supplier/application", controller.SubmitSupplierApplication)
selfRoute.GET("/supplier/application/self", controller.GetMySupplierApplication)
diff --git a/setting/payment_alipay.go b/setting/payment_alipay.go
new file mode 100644
index 0000000..08a2868
--- /dev/null
+++ b/setting/payment_alipay.go
@@ -0,0 +1,16 @@
+package setting
+
+// Alipay direct integration (via Alipay+ / international Alipay API)
+var (
+ AlipayEnabled bool
+ AlipayAppId string
+ AlipayPrivateKey string
+ AlipayPublicKey string
+ AlipayNotifyUrl string
+ AlipayReturnUrl string
+ AlipayUnitPrice float64 = 1.0
+ AlipayMinTopUp int = 1
+ AlipaySandbox bool
+ AlipaySandboxAppId string
+ AlipaySandboxPrivateKey string
+)
diff --git a/setting/payment_wechat.go b/setting/payment_wechat.go
new file mode 100644
index 0000000..af6bb44
--- /dev/null
+++ b/setting/payment_wechat.go
@@ -0,0 +1,19 @@
+package setting
+
+// WeChat Pay direct integration (via WeChat Pay APIv3)
+var (
+ WechatPayEnabled bool
+ WechatPayAppId string
+ WechatPayMchId string
+ WechatPayApiV3Key string
+ WechatPayPrivateKey string
+ WechatPayCertSerial string
+ WechatPayNotifyUrl string
+ WechatPayReturnUrl string
+ WechatPayUnitPrice float64 = 1.0
+ WechatPayMinTopUp int = 1
+ WechatPaySandbox bool
+ WechatPaySandboxMchId string
+ WechatPaySandboxApiV3Key string
+ WechatPaySandboxPrivateKey string
+)
diff --git a/web/src/components/settings/PaymentSetting.jsx b/web/src/components/settings/PaymentSetting.jsx
index 1716527..92a11d3 100644
--- a/web/src/components/settings/PaymentSetting.jsx
+++ b/web/src/components/settings/PaymentSetting.jsx
@@ -1,4 +1,4 @@
-/*
+/*
Copyright (C) 2025 QuantumNous
This program is free software: you can redistribute it and/or modify
@@ -17,104 +17,132 @@ along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
-import React, { useEffect, useState } from 'react';
-import { Card, Spin } 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 { API, showError, toBoolean } from '../../helpers';
-import { useTranslation } from 'react-i18next';
+import React, { useEffect, useState } from "react";
+import { Card, Spin } 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 SettingsPaymentGatewayAlipay from "../../pages/Setting/Payment/SettingsPaymentGatewayAlipay";
+import SettingsPaymentGatewayWechat from "../../pages/Setting/Payment/SettingsPaymentGatewayWechat";
+import { API, showError, toBoolean } from "../../helpers";
+import { useTranslation } from "react-i18next";
/** 系统设置中的支付 Tab:拉取 option 并渲染各支付子表单。 */
const PaymentSetting = () => {
const { t } = useTranslation();
let [inputs, setInputs] = useState({
- ServerAddress: '',
- PayAddress: '',
- OnlinePayProvider: 'yipay',
- EpayId: '',
- EpayKey: '',
- YipayAppSecret: '',
- YipayMchNo: '',
- YipayAppId: '',
- YipayNotifyUrl: '',
- YipayReturnUrl: '',
- YipayChannelExtra: '',
- YipayRequestURL: '',
+ ServerAddress: "",
+ PayAddress: "",
+ OnlinePayProvider: "yipay",
+ EpayId: "",
+ EpayKey: "",
+ YipayAppSecret: "",
+ YipayMchNo: "",
+ YipayAppId: "",
+ YipayNotifyUrl: "",
+ YipayReturnUrl: "",
+ YipayChannelExtra: "",
+ YipayRequestURL: "",
Price: 7.3,
MinTopUp: 1,
- TopupGroupRatio: '',
- CustomCallbackAddress: '',
- PayMethods: '',
- AmountOptions: '',
- AmountDiscount: '',
+ TopupGroupRatio: "",
+ CustomCallbackAddress: "",
+ PayMethods: "",
+ AmountOptions: "",
+ AmountDiscount: "",
- StripeApiSecret: '',
- StripeWebhookSecret: '',
- StripePriceId: '',
+ StripeApiSecret: "",
+ StripeWebhookSecret: "",
+ StripePriceId: "",
StripeUnitPrice: 8.0,
StripeMinTopUp: 1,
StripePromotionCodesEnabled: false,
+
+ AlipayEnabled: false,
+ AlipayAppId: "",
+ AlipayPrivateKey: "",
+ AlipayPublicKey: "",
+ AlipayNotifyUrl: "",
+ AlipayReturnUrl: "",
+ AlipayUnitPrice: 1.0,
+ AlipayMinTopUp: 1,
+ AlipaySandbox: false,
+ AlipaySandboxAppId: "",
+ AlipaySandboxPrivateKey: "",
+ WechatPayEnabled: false,
+ WechatPayAppId: "",
+ WechatPayMchId: "",
+ WechatPayApiV3Key: "",
+ WechatPayPrivateKey: "",
+ WechatPayCertSerial: "",
+ WechatPayNotifyUrl: "",
+ WechatPayReturnUrl: "",
+ WechatPayUnitPrice: 1.0,
+ WechatPayMinTopUp: 1,
+ WechatPaySandbox: false,
+ WechatPaySandboxMchId: "",
+ WechatPaySandboxApiV3Key: "",
+ WechatPaySandboxPrivateKey: "",
});
let [loading, setLoading] = useState(false);
const getOptions = async () => {
- const res = await API.get('/api/option/');
+ const res = await API.get("/api/option/");
const { success, message, data } = res.data;
if (success) {
let newInputs = {};
data.forEach((item) => {
switch (item.key) {
- case 'TopupGroupRatio':
+ case "TopupGroupRatio":
try {
newInputs[item.key] = JSON.stringify(
JSON.parse(item.value),
null,
- 2,
+ 2
);
} catch (error) {
newInputs[item.key] = item.value;
}
break;
- case 'payment_setting.amount_options':
+ case "payment_setting.amount_options":
try {
- newInputs['AmountOptions'] = JSON.stringify(
+ newInputs["AmountOptions"] = JSON.stringify(
JSON.parse(item.value),
null,
- 2,
+ 2
);
} catch (error) {
- newInputs['AmountOptions'] = item.value;
+ newInputs["AmountOptions"] = item.value;
}
break;
- case 'payment_setting.amount_discount':
+ case "payment_setting.amount_discount":
try {
- newInputs['AmountDiscount'] = JSON.stringify(
+ newInputs["AmountDiscount"] = JSON.stringify(
JSON.parse(item.value),
null,
- 2,
+ 2
);
} catch (error) {
- newInputs['AmountDiscount'] = item.value;
+ newInputs["AmountDiscount"] = item.value;
}
break;
- case 'Price':
- case 'MinTopUp':
- case 'StripeUnitPrice':
- case 'StripeMinTopUp':
+ case "Price":
+ case "MinTopUp":
+ case "StripeUnitPrice":
+ case "StripeMinTopUp":
newInputs[item.key] = parseFloat(item.value);
break;
- case 'YipayAppSecret':
+ case "YipayAppSecret":
newInputs[item.key] =
item.value != null && item.value !== undefined
? String(item.value)
- : '';
+ : "";
break;
default:
- if (item.key.endsWith('Enabled')) {
+ if (item.key.endsWith("Enabled")) {
newInputs[item.key] = toBoolean(item.value);
} else {
newInputs[item.key] = item.value;
@@ -134,7 +162,7 @@ const PaymentSetting = () => {
setLoading(true);
await getOptions();
} catch (error) {
- showError(t('刷新失败'));
+ showError(t("刷新失败"));
} finally {
setLoading(false);
}
@@ -146,22 +174,28 @@ const PaymentSetting = () => {
return (
<>
-
-
+
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+
>
);
diff --git a/web/src/components/topup/index.jsx b/web/src/components/topup/index.jsx
index ac8072e..d37e537 100644
--- a/web/src/components/topup/index.jsx
+++ b/web/src/components/topup/index.jsx
@@ -1,4 +1,4 @@
-
+
import React, { useEffect, useState, useContext } from 'react';
import { useSearchParams } from 'react-router-dom';
import {
@@ -58,6 +58,8 @@ const TopUp = () => {
// Waffo 相关状态
const [enableWaffoTopUp, setEnableWaffoTopUp] = useState(false);
+ const [enableAlipayTopUp, setEnableAlipayTopUp] = useState(false);
+ const [enableWechatPayTopUp, setEnableWechatPayTopUp] = useState(false);
const [waffoPayMethods, setWaffoPayMethods] = useState([]);
const [waffoMinTopUp, setWaffoMinTopUp] = useState(1);
@@ -147,6 +149,10 @@ const TopUp = () => {
showError(t('管理员未开启Stripe充值!'));
return;
}
+ } else if (payment === 'alipay') {
+ // Alipay handles its own check via backend
+ } else if (payment === 'wxpay') {
+ // WeChat Pay handles its own check via backend
} else {
if (!enableOnlineTopUp) {
showError(t('管理员未开启在线充值!'));
@@ -159,6 +165,10 @@ const TopUp = () => {
try {
if (payment === 'stripe') {
await getStripeAmount();
+ } else if (payment === 'alipay') {
+ // Alipay goes directly to payment without amount query
+ } else if (payment === 'wxpay') {
+ // WeChat Pay goes directly to payment without amount query
} else {
await getAmount();
}
@@ -201,7 +211,19 @@ const TopUp = () => {
amount: parseInt(topUpCount),
payment_method: 'stripe',
});
- } else {
+ } else if (payWay === 'alipay') {
+ // Alipay 支付请求
+ res = await API.post('/api/user/alipay/pay', {
+ amount: parseInt(topUpCount),
+ payment_method: 'alipay',
+ });
+ } else if (payWay === 'wxpay') {
+ // WeChat Pay 支付请求
+ res = await API.post('/api/user/wechatpay/pay', {
+ amount: parseInt(topUpCount),
+ payment_method: 'wxpay',
+ });
+ } else {
// 普通支付请求
res = await API.post('/api/user/pay', {
amount: parseInt(topUpCount),
@@ -215,7 +237,10 @@ const TopUp = () => {
if (payWay === 'stripe') {
// Stripe 支付回调处理
window.open(data.pay_link, '_blank');
- } else {
+ } else if (payWay === 'alipay' || payWay === 'wxpay') {
+ // Alipay / WeChat Pay 返回 pay_url,直接打开新窗口跳转
+ window.open(data.pay_url, '_blank');
+ } else {
// 普通支付表单提交
let params = data;
let url = res.data.url;
@@ -513,6 +538,10 @@ const TopUp = () => {
setEnableStripeTopUp(enableStripeTopUp);
setEnableCreemTopUp(enableCreemTopUp);
const enableWaffoTopUp = data.enable_waffo_topup || false;
+ const enableAlipayTopUp = data.enable_alipay_topup || false;
+ setEnableAlipayTopUp(enableAlipayTopUp);
+ const enableWechatPayTopUp = data.enable_wechatpay_topup || false;
+ setEnableWechatPayTopUp(enableWechatPayTopUp);
setEnableWaffoTopUp(enableWaffoTopUp);
setWaffoPayMethods(data.waffo_pay_methods || []);
setWaffoMinTopUp(data.waffo_min_topup || 1);
@@ -607,7 +636,10 @@ const TopUp = () => {
statusState.status.recharge_display_currency ||
'USD',
);
-
+ const enableAlipayTopUp = statusState.status.enable_alipay_topup || false;
+ setEnableAlipayTopUp(enableAlipayTopUp);
+ const enableWechatPayTopUp = statusState.status.enable_wechatpay_topup || false;
+ setEnableWechatPayTopUp(enableWechatPayTopUp);
setStatusLoading(false);
}
}, [statusState?.status]);
@@ -1005,3 +1037,9 @@ const TopUp = () => {
};
export default TopUp;
+
+
+
+
+
+
diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json
index 16f27b6..f607464 100644
--- a/web/src/i18n/locales/en.json
+++ b/web/src/i18n/locales/en.json
@@ -5587,6 +5587,92 @@
"默认 ap-guangzhou,可改为 ap-beijing 等": "Default ap-guangzhou; can be changed to ap-beijing, etc.",
"(共 {{total}} 个)": "({{total}} total)",
"(共 {{total}} 个,省略 {{omit}} 个)": "({{total}} total, {{omit}} omitted)",
- "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(Currently supports Yipay configuration; by default, the server address above is used as the callback URL!)"
+ "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(Currently supports Yipay configuration; by default, the server address above is used as the callback URL!)",
+ "热度配置": "Heat Configuration",
+ "模型热度排行配置": "Model Heat Ranking Config",
+ "热度分 = (手动基数 + 实际调用) x 权重": "Heat Score = (Manual Base + Actual Calls) x Weight",
+ "预估热度分": "Estimated Heat Score",
+ "统计周期": "Statistics Period",
+ "统计周期设置": "Statistics Period Setting",
+ "统计周期已更新,重新加载数据中...": "Statistics period updated, reloading data...",
+ "权重配置": "Weight Configuration",
+ "手动调整": "Manual Adjustment",
+ "实际调用": "Actual Calls",
+ "全部历史": "All History",
+ "近 7 天": "Last 7 Days",
+ "近 30 天": "Last 30 Days",
+ "模型 / 渠道": "Model / Channel",
+ "搜索渠道": "Search Channel",
+ "不修改": "No Change",
+ "批量保存成功": "Batch saved successfully",
+ "批量保存失败": "Batch save failed",
+ "序号": "No.",
+ "热门": "Hot",
+ "操作记录": "Operation Records",
+ "系统操作日志审计": "System Operation Log Audit",
+ "操作详情": "Operation Details",
+ "操作描述": "Operation Description",
+ "操作者": "Operator",
+ "资源ID": "Resource ID",
+ "资源类型": "Resource Type",
+ "资源名称": "Resource Name",
+ "IP地址": "IP Address",
+ "兑换码": "Redemption Code",
+ "角色菜单权限配置": "Role Menu Permission Config",
+ "重置当前角色": "Reset Current Role",
+ "已重置角色配置": "Role configuration has been reset",
+ "供应商申请": "Supplier Application",
+ "供应商-申请": "Supplier - Application",
+ "供应商-渠道管理": "Supplier - Channel Management",
+ "供应商渠道管理": "Supplier Channel Management",
+ "供应商-定价设置": "Supplier - Pricing Settings",
+ "供应商定价设置": "Supplier Pricing Settings",
+ "供应商-数据看板": "Supplier - Dashboard",
+ "供应商数据看板": "Supplier Dashboard",
+ "支付宝 (Alipay) 设置": "Alipay Settings",
+ "支付宝 App ID": "Alipay App ID",
+ "支付宝开放平台应用的 APPID": "APPID of the Alipay Open Platform application",
+ "商户私钥": "Merchant Private Key",
+ "应用私钥,PKCS8 格式的 RSA2 私钥": "Application private key, RSA2 private key in PKCS8 format",
+ "支付宝公钥": "Alipay Public Key",
+ "支付宝开放平台获取的支付宝公钥": "Alipay public key obtained from Alipay Open Platform",
+ "自定义异步通知地址": "Custom Async Notify URL",
+ "自定义同步跳转地址": "Custom Sync Return URL",
+ "单价(元/额度)": "Unit Price (CNY/Quota)",
+ "例如:1,就是1元/额度": "e.g. 1 means 1 CNY per quota unit",
+ "最低充值额度": "Minimum Top-Up Amount",
+ "例如:1": "e.g. 1",
+ "沙箱模式": "Sandbox Mode",
+ "沙箱 App ID": "Sandbox App ID",
+ "沙箱环境的 APPID": "APPID for sandbox environment",
+ "沙箱商户私钥": "Sandbox Merchant Private Key",
+ "沙箱环境的应用私钥": "Application private key for sandbox environment",
+ "沙箱环境": "Sandbox Environment",
+ "支付宝开放平台": "Alipay Open Platform",
+ "启用支付宝支付": "Enable Alipay Payment",
+ "更新支付宝设置": "Update Alipay Settings",
+ "请确保已在支付宝开放平台配置 RSA2 密钥,并设置签约产品为「电脑网站支付」。": "Please ensure RSA2 keys are configured on Alipay Open Platform and the signed product is set to \"Website Payment\".",
+ "异步通知地址 (Notify URL)": "Async Notify URL",
+ "启用微信支付": "Enable WeChat Pay",
+ "微信支付 (WeChat Pay) 设置": "WeChat Pay Settings",
+ "微信公众号/小程序 AppID": "WeChat Official Account/Mini Program AppID",
+ "微信开放平台的 AppID": "AppID from WeChat Open Platform",
+ "商户号 (MchID)": "Merchant ID (MchID)",
+ "微信支付商户号": "WeChat Pay Merchant ID",
+ "APIv3 密钥": "APIv3 Key",
+ "APIv3 密钥,32位字符": "APIv3 key, 32 characters",
+ "商户 API 证书的私钥 (PKCS8 PEM)": "Merchant API Certificate Private Key (PKCS8 PEM)",
+ "证书序列号": "Certificate Serial Number",
+ "商户 API 证书序列号": "Merchant API Certificate Serial Number",
+ "微信支付商户平台": "WeChat Pay Merchant Platform",
+ "API v3 文档": "API v3 Documentation",
+ "更新微信支付设置": "Update WeChat Pay Settings",
+ "沙箱商户号": "Sandbox Merchant ID",
+ "沙箱环境的商户号": "Merchant ID for sandbox environment",
+ "沙箱 APIv3 密钥": "Sandbox APIv3 Key",
+ "沙箱环境 APIv3 密钥": "APIv3 key for sandbox environment",
+ "沙箱环境商户私钥": "Sandbox Merchant Private Key",
+ "请确保已配置 APIv3 密钥,并下载商户证书序列号。": "Please ensure the APIv3 key is configured and the merchant certificate serial number is downloaded.",
+ "可选,留空则使用默认地址": "Optional, leave blank to use default URL"
}
-}
+}
\ No newline at end of file
diff --git a/web/src/i18n/locales/fr.json b/web/src/i18n/locales/fr.json
index 23e0b78..34a9927 100644
--- a/web/src/i18n/locales/fr.json
+++ b/web/src/i18n/locales/fr.json
@@ -5497,6 +5497,93 @@
"默认 ap-guangzhou,可改为 ap-beijing 等": "La valeur par défaut est ap-guangzhou, qui peut être modifiée en ap-beijing etc.",
"(共 {{total}} 个)": "(Total {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(Total des éléments {{total}}, éléments {{omit}} omis)",
- "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(Prend actuellement en charge la configuration Yipay/Yipay et utilise l'adresse du serveur ci-dessus comme adresse de rappel par défaut !)"
+ "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(Prend actuellement en charge la configuration Yipay/Yipay et utilise l'adresse du serveur ci-dessus comme adresse de rappel par défaut !)",
+ "热度配置": "Configuration de chaleur",
+ "模型热度排行配置": "Configuration du classement de chaleur des modeles",
+ "热度分 = (手动基数 + 实际调用) x 权重": "Score de chaleur = (Base manuelle + Appels reels) x Poids",
+ "预估热度分": "Score de chaleur estime",
+ "统计周期": "Periode statistique",
+ "统计周期设置": "Parametre de periode statistique",
+ "统计周期已更新,重新加载数据中...": "Periode mise a jour, rechargement des donnees...",
+ "权重配置": "Configuration des poids",
+ "手动调整": "Ajustement manuel",
+ "实际调用": "Appels reels",
+ "全部历史": "Tout historique",
+ "近 7 天": "7 derniers jours",
+ "近 30 天": "30 derniers jours",
+ "模型 / 渠道": "Modele / Canal",
+ "搜索渠道": "Rechercher canal",
+ "不修改": "Ne pas modifier",
+ "批量保存成功": "Enregistrement par lot reussi",
+ "批量保存失败": "Echec enregistrement par lot",
+ "序号": "No.",
+ "热门": "Populaire",
+ "操作记录": "Registre des operations",
+ "系统操作日志审计": "Audit des journaux d'operations systeme",
+ "操作详情": "Details de l'operation",
+ "操作描述": "Description de l'operation",
+ "操作者": "Operateur",
+ "资源ID": "ID de ressource",
+ "资源类型": "Type de ressource",
+ "资源名称": "Nom de la ressource",
+ "IP地址": "Adresse IP",
+ "兑换码": "Code d'echange",
+ "角色菜单权限配置": "Configuration des permissions du menu de role",
+ "重置当前角色": "Reinitialiser le role actuel",
+ "已重置角色配置": "La configuration du role a ete reinitialisee",
+ "成本价": "Prix de revient",
+ "供应商申请": "Demande de fournisseur",
+ "供应商-申请": "Fournisseur - Demande",
+ "供应商-渠道管理": "Fournisseur - Gestion des canaux",
+ "供应商渠道管理": "Gestion des canaux du fournisseur",
+ "供应商-定价设置": "Fournisseur - Parametres de prix",
+ "供应商定价设置": "Parametres de prix du fournisseur",
+ "供应商-数据看板": "Fournisseur - Tableau de bord",
+ "供应商数据看板": "Tableau de bord du fournisseur",
+ "支付宝 (Alipay) 设置": "Alipay Settings",
+ "支付宝 App ID": "Alipay App ID",
+ "支付宝开放平台应用的 APPID": "APPID of the Alipay Open Platform application",
+ "商户私钥": "Merchant Private Key",
+ "应用私钥,PKCS8 格式的 RSA2 私钥": "Application private key, RSA2 private key in PKCS8 format",
+ "支付宝公钥": "Alipay Public Key",
+ "支付宝开放平台获取的支付宝公钥": "Alipay public key obtained from Alipay Open Platform",
+ "自定义异步通知地址": "Custom Async Notify URL",
+ "自定义同步跳转地址": "Custom Sync Return URL",
+ "单价(元/额度)": "Unit Price (CNY/Quota)",
+ "例如:1,就是1元/额度": "e.g. 1 means 1 CNY per quota unit",
+ "最低充值额度": "Minimum Top-Up Amount",
+ "例如:1": "e.g. 1",
+ "沙箱模式": "Sandbox Mode",
+ "沙箱 App ID": "Sandbox App ID",
+ "沙箱环境的 APPID": "APPID for sandbox environment",
+ "沙箱商户私钥": "Sandbox Merchant Private Key",
+ "沙箱环境的应用私钥": "Application private key for sandbox environment",
+ "沙箱环境": "Sandbox Environment",
+ "支付宝开放平台": "Alipay Open Platform",
+ "启用支付宝支付": "Enable Alipay Payment",
+ "更新支付宝设置": "Update Alipay Settings",
+ "请确保已在支付宝开放平台配置 RSA2 密钥,并设置签约产品为「电脑网站支付」。": "Please ensure RSA2 keys are configured on Alipay Open Platform and the signed product is set to \"Website Payment\".",
+ "异步通知地址 (Notify URL)": "Async Notify URL",
+ "启用微信支付": "Enable WeChat Pay",
+ "微信支付 (WeChat Pay) 设置": "WeChat Pay Settings",
+ "微信公众号/小程序 AppID": "WeChat Official Account/Mini Program AppID",
+ "微信开放平台的 AppID": "AppID from WeChat Open Platform",
+ "商户号 (MchID)": "Merchant ID (MchID)",
+ "微信支付商户号": "WeChat Pay Merchant ID",
+ "APIv3 密钥": "APIv3 Key",
+ "APIv3 密钥,32位字符": "APIv3 key, 32 characters",
+ "商户 API 证书的私钥 (PKCS8 PEM)": "Merchant API Certificate Private Key (PKCS8 PEM)",
+ "证书序列号": "Certificate Serial Number",
+ "商户 API 证书序列号": "Merchant API Certificate Serial Number",
+ "微信支付商户平台": "WeChat Pay Merchant Platform",
+ "API v3 文档": "API v3 Documentation",
+ "更新微信支付设置": "Update WeChat Pay Settings",
+ "沙箱商户号": "Sandbox Merchant ID",
+ "沙箱环境的商户号": "Merchant ID for sandbox environment",
+ "沙箱 APIv3 密钥": "Sandbox APIv3 Key",
+ "沙箱环境 APIv3 密钥": "APIv3 key for sandbox environment",
+ "沙箱环境商户私钥": "Sandbox Merchant Private Key",
+ "请确保已配置 APIv3 密钥,并下载商户证书序列号。": "Please ensure the APIv3 key is configured and the merchant certificate serial number is downloaded.",
+ "可选,留空则使用默认地址": "Optional, leave blank to use default URL"
}
-}
+}
\ No newline at end of file
diff --git a/web/src/i18n/locales/id.json b/web/src/i18n/locales/id.json
index 6e5e0f3..cfee086 100644
--- a/web/src/i18n/locales/id.json
+++ b/web/src/i18n/locales/id.json
@@ -5497,6 +5497,93 @@
"默认 ap-guangzhou,可改为 ap-beijing 等": "Standarnya adalah ap-guangzhou, yang dapat diubah menjadi ap-beijing dll.",
"(共 {{total}} 个)": "(Jumlah {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(Total item {{total}}, item {{omit}} dihilangkan)",
- "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(Saat ini mendukung konfigurasi Yipay/Yipay, dan menggunakan alamat server di atas sebagai alamat panggilan balik secara default!)"
+ "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(Saat ini mendukung konfigurasi Yipay/Yipay, dan menggunakan alamat server di atas sebagai alamat panggilan balik secara default!)",
+ "热度配置": "Konfigurasi Panas",
+ "模型热度排行配置": "Konfigurasi Peringkat Panas Model",
+ "热度分 = (手动基数 + 实际调用) x 权重": "Skor Panas = (Dasar Manual + Panggilan Aktual) x Bobot",
+ "预估热度分": "Perkiraan Skor Panas",
+ "统计周期": "Periode Statistik",
+ "统计周期设置": "Pengaturan Periode Statistik",
+ "统计周期已更新,重新加载数据中...": "Periode diperbarui, memuat ulang data...",
+ "权重配置": "Konfigurasi Bobot",
+ "手动调整": "Penyesuaian Manual",
+ "实际调用": "Panggilan Aktual",
+ "全部历史": "Semua Riwayat",
+ "近 7 天": "7 Hari Terakhir",
+ "近 30 天": "30 Hari Terakhir",
+ "模型 / 渠道": "Model / Saluran",
+ "搜索渠道": "Cari Saluran",
+ "不修改": "Tidak Berubah",
+ "批量保存成功": "Penyimpanan massal berhasil",
+ "批量保存失败": "Penyimpanan massal gagal",
+ "序号": "No.",
+ "热门": "Populer",
+ "操作记录": "Catatan Operasi",
+ "系统操作日志审计": "Audit Log Operasi Sistem",
+ "操作详情": "Detail Operasi",
+ "操作描述": "Deskripsi Operasi",
+ "操作者": "Operator",
+ "资源ID": "ID Sumber Daya",
+ "资源类型": "Tipe Sumber Daya",
+ "资源名称": "Nama Sumber Daya",
+ "IP地址": "Alamat IP",
+ "兑换码": "Kode Penukaran",
+ "角色菜单权限配置": "Konfigurasi Izin Menu Peran",
+ "重置当前角色": "Reset Peran Saat Ini",
+ "已重置角色配置": "Konfigurasi peran telah direset",
+ "成本价": "Harga Pokok",
+ "供应商申请": "Aplikasi Pemasok",
+ "供应商-申请": "Pemasok - Aplikasi",
+ "供应商-渠道管理": "Pemasok - Manajemen Saluran",
+ "供应商渠道管理": "Manajemen Saluran Pemasok",
+ "供应商-定价设置": "Pemasok - Pengaturan Harga",
+ "供应商定价设置": "Pengaturan Harga Pemasok",
+ "供应商-数据看板": "Pemasok - Dashboard",
+ "供应商数据看板": "Dashboard Pemasok",
+ "支付宝 (Alipay) 设置": "Alipay Settings",
+ "支付宝 App ID": "Alipay App ID",
+ "支付宝开放平台应用的 APPID": "APPID of the Alipay Open Platform application",
+ "商户私钥": "Merchant Private Key",
+ "应用私钥,PKCS8 格式的 RSA2 私钥": "Application private key, RSA2 private key in PKCS8 format",
+ "支付宝公钥": "Alipay Public Key",
+ "支付宝开放平台获取的支付宝公钥": "Alipay public key obtained from Alipay Open Platform",
+ "自定义异步通知地址": "Custom Async Notify URL",
+ "自定义同步跳转地址": "Custom Sync Return URL",
+ "单价(元/额度)": "Unit Price (CNY/Quota)",
+ "例如:1,就是1元/额度": "e.g. 1 means 1 CNY per quota unit",
+ "最低充值额度": "Minimum Top-Up Amount",
+ "例如:1": "e.g. 1",
+ "沙箱模式": "Sandbox Mode",
+ "沙箱 App ID": "Sandbox App ID",
+ "沙箱环境的 APPID": "APPID for sandbox environment",
+ "沙箱商户私钥": "Sandbox Merchant Private Key",
+ "沙箱环境的应用私钥": "Application private key for sandbox environment",
+ "沙箱环境": "Sandbox Environment",
+ "支付宝开放平台": "Alipay Open Platform",
+ "启用支付宝支付": "Enable Alipay Payment",
+ "更新支付宝设置": "Update Alipay Settings",
+ "请确保已在支付宝开放平台配置 RSA2 密钥,并设置签约产品为「电脑网站支付」。": "Please ensure RSA2 keys are configured on Alipay Open Platform and the signed product is set to \"Website Payment\".",
+ "异步通知地址 (Notify URL)": "Async Notify URL",
+ "启用微信支付": "Enable WeChat Pay",
+ "微信支付 (WeChat Pay) 设置": "WeChat Pay Settings",
+ "微信公众号/小程序 AppID": "WeChat Official Account/Mini Program AppID",
+ "微信开放平台的 AppID": "AppID from WeChat Open Platform",
+ "商户号 (MchID)": "Merchant ID (MchID)",
+ "微信支付商户号": "WeChat Pay Merchant ID",
+ "APIv3 密钥": "APIv3 Key",
+ "APIv3 密钥,32位字符": "APIv3 key, 32 characters",
+ "商户 API 证书的私钥 (PKCS8 PEM)": "Merchant API Certificate Private Key (PKCS8 PEM)",
+ "证书序列号": "Certificate Serial Number",
+ "商户 API 证书序列号": "Merchant API Certificate Serial Number",
+ "微信支付商户平台": "WeChat Pay Merchant Platform",
+ "API v3 文档": "API v3 Documentation",
+ "更新微信支付设置": "Update WeChat Pay Settings",
+ "沙箱商户号": "Sandbox Merchant ID",
+ "沙箱环境的商户号": "Merchant ID for sandbox environment",
+ "沙箱 APIv3 密钥": "Sandbox APIv3 Key",
+ "沙箱环境 APIv3 密钥": "APIv3 key for sandbox environment",
+ "沙箱环境商户私钥": "Sandbox Merchant Private Key",
+ "请确保已配置 APIv3 密钥,并下载商户证书序列号。": "Please ensure the APIv3 key is configured and the merchant certificate serial number is downloaded.",
+ "可选,留空则使用默认地址": "Optional, leave blank to use default URL"
}
-}
+}
\ No newline at end of file
diff --git a/web/src/i18n/locales/ja.json b/web/src/i18n/locales/ja.json
index 0cccbb1..0b2c823 100644
--- a/web/src/i18n/locales/ja.json
+++ b/web/src/i18n/locales/ja.json
@@ -5497,6 +5497,93 @@
"默认 ap-guangzhou,可改为 ap-beijing 等": "デフォルトは ap-guangzhou ですが、ap-beijing などに変更できます。",
"(共 {{total}} 个)": "(合計 {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(合計{{total}}項目、{{omit}}項目は省略)",
- "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(現在 Yipay/Yipay 構成をサポートしており、デフォルトで上記のサーバー アドレスをコールバック アドレスとして使用します。)"
+ "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(現在 Yipay/Yipay 構成をサポートしており、デフォルトで上記のサーバー アドレスをコールバック アドレスとして使用します。)",
+ "热度配置": "ヒート設定",
+ "模型热度排行配置": "モデルヒートランキング設定",
+ "热度分 = (手动基数 + 实际调用) x 权重": "ヒートスコア = (手動ベース + 實際呼出) x 重み",
+ "预估热度分": "推定ヒートスコア",
+ "统计周期": "統計期間",
+ "统计周期设置": "統計期間設定",
+ "统计周期已更新,重新加载数据中...": "統計期間が更新されました。データを再読込中...",
+ "权重配置": "重み設定",
+ "手动调整": "手動調整",
+ "实际调用": "實際呼出",
+ "全部历史": "全履歴",
+ "近 7 天": "最近7日間",
+ "近 30 天": "最近30日間",
+ "模型 / 渠道": "モデル / チャンネル",
+ "搜索渠道": "チャンネル検索",
+ "不修改": "変更しない",
+ "批量保存成功": "一括保存成功",
+ "批量保存失败": "一括保存失敗",
+ "序号": "番号",
+ "热门": "人気",
+ "操作记录": "操作記録",
+ "系统操作日志审计": "システム操作ログ監査",
+ "操作详情": "操作詳細",
+ "操作描述": "操作説明",
+ "操作者": "操作者",
+ "资源ID": "リソースID",
+ "资源类型": "リソースタイプ",
+ "资源名称": "リソース名",
+ "IP地址": "IPアドレス",
+ "兑换码": "交換コード",
+ "角色菜单权限配置": "ロールメニュー権限設定",
+ "重置当前角色": "現在のロールをリセット",
+ "已重置角色配置": "ロール設定をリセットしました",
+ "成本价": "原価",
+ "供应商申请": "サプライヤー申請",
+ "供应商-申请": "サプライヤー - 申請",
+ "供应商-渠道管理": "サプライヤー - チャンネル管理",
+ "供应商渠道管理": "サプライヤーチャンネル管理",
+ "供应商-定价设置": "サプライヤー - 価格設定",
+ "供应商定价设置": "サプライヤー価格設定",
+ "供应商-数据看板": "サプライヤー - ダッシュボード",
+ "供应商数据看板": "サプライヤーダッシュボード",
+ "支付宝 (Alipay) 设置": "Alipay Settings",
+ "支付宝 App ID": "Alipay App ID",
+ "支付宝开放平台应用的 APPID": "APPID of the Alipay Open Platform application",
+ "商户私钥": "Merchant Private Key",
+ "应用私钥,PKCS8 格式的 RSA2 私钥": "Application private key, RSA2 private key in PKCS8 format",
+ "支付宝公钥": "Alipay Public Key",
+ "支付宝开放平台获取的支付宝公钥": "Alipay public key obtained from Alipay Open Platform",
+ "自定义异步通知地址": "Custom Async Notify URL",
+ "自定义同步跳转地址": "Custom Sync Return URL",
+ "单价(元/额度)": "Unit Price (CNY/Quota)",
+ "例如:1,就是1元/额度": "e.g. 1 means 1 CNY per quota unit",
+ "最低充值额度": "Minimum Top-Up Amount",
+ "例如:1": "e.g. 1",
+ "沙箱模式": "Sandbox Mode",
+ "沙箱 App ID": "Sandbox App ID",
+ "沙箱环境的 APPID": "APPID for sandbox environment",
+ "沙箱商户私钥": "Sandbox Merchant Private Key",
+ "沙箱环境的应用私钥": "Application private key for sandbox environment",
+ "沙箱环境": "Sandbox Environment",
+ "支付宝开放平台": "Alipay Open Platform",
+ "启用支付宝支付": "Enable Alipay Payment",
+ "更新支付宝设置": "Update Alipay Settings",
+ "请确保已在支付宝开放平台配置 RSA2 密钥,并设置签约产品为「电脑网站支付」。": "Please ensure RSA2 keys are configured on Alipay Open Platform and the signed product is set to \"Website Payment\".",
+ "异步通知地址 (Notify URL)": "Async Notify URL",
+ "启用微信支付": "Enable WeChat Pay",
+ "微信支付 (WeChat Pay) 设置": "WeChat Pay Settings",
+ "微信公众号/小程序 AppID": "WeChat Official Account/Mini Program AppID",
+ "微信开放平台的 AppID": "AppID from WeChat Open Platform",
+ "商户号 (MchID)": "Merchant ID (MchID)",
+ "微信支付商户号": "WeChat Pay Merchant ID",
+ "APIv3 密钥": "APIv3 Key",
+ "APIv3 密钥,32位字符": "APIv3 key, 32 characters",
+ "商户 API 证书的私钥 (PKCS8 PEM)": "Merchant API Certificate Private Key (PKCS8 PEM)",
+ "证书序列号": "Certificate Serial Number",
+ "商户 API 证书序列号": "Merchant API Certificate Serial Number",
+ "微信支付商户平台": "WeChat Pay Merchant Platform",
+ "API v3 文档": "API v3 Documentation",
+ "更新微信支付设置": "Update WeChat Pay Settings",
+ "沙箱商户号": "Sandbox Merchant ID",
+ "沙箱环境的商户号": "Merchant ID for sandbox environment",
+ "沙箱 APIv3 密钥": "Sandbox APIv3 Key",
+ "沙箱环境 APIv3 密钥": "APIv3 key for sandbox environment",
+ "沙箱环境商户私钥": "Sandbox Merchant Private Key",
+ "请确保已配置 APIv3 密钥,并下载商户证书序列号。": "Please ensure the APIv3 key is configured and the merchant certificate serial number is downloaded.",
+ "可选,留空则使用默认地址": "Optional, leave blank to use default URL"
}
-}
+}
\ No newline at end of file
diff --git a/web/src/i18n/locales/ms.json b/web/src/i18n/locales/ms.json
index 41e19b7..bf866d8 100644
--- a/web/src/i18n/locales/ms.json
+++ b/web/src/i18n/locales/ms.json
@@ -5497,6 +5497,93 @@
"默认 ap-guangzhou,可改为 ap-beijing 等": "Lalai ialah ap-guangzhou, yang boleh ditukar kepada ap-beijing dsb.",
"(共 {{total}} 个)": "(Jumlah {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(Jumlah {{total}} item, ditinggalkan {{omit}} item)",
- "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(Pada masa ini menyokong konfigurasi Yipay/Yipay, dan menggunakan alamat pelayan di atas sebagai alamat panggilan balik secara lalai!)"
+ "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(Pada masa ini menyokong konfigurasi Yipay/Yipay, dan menggunakan alamat pelayan di atas sebagai alamat panggilan balik secara lalai!)",
+ "热度配置": "Konfigurasi Haba",
+ "模型热度排行配置": "Konfigurasi Kedudukan Haba Model",
+ "热度分 = (手动基数 + 实际调用) x 权重": "Skor Haba = (Asas Manual + Panggilan Sebenar) x Berat",
+ "预估热度分": "Anggaran Skor Haba",
+ "统计周期": "Tempoh Statistik",
+ "统计周期设置": "Tetapan Tempoh Statistik",
+ "统计周期已更新,重新加载数据中...": "Tempoh dikemas kini, memuat semula data...",
+ "权重配置": "Konfigurasi Berat",
+ "手动调整": "Pelarasan Manual",
+ "实际调用": "Panggilan Sebenar",
+ "全部历史": "Semua Sejarah",
+ "近 7 天": "7 Hari Terakhir",
+ "近 30 天": "30 Hari Terakhir",
+ "模型 / 渠道": "Model / Saluran",
+ "搜索渠道": "Cari Saluran",
+ "不修改": "Tiada Perubahan",
+ "批量保存成功": "Simpan pukal berjaya",
+ "批量保存失败": "Simpan pukal gagal",
+ "序号": "No.",
+ "热门": "Popular",
+ "操作记录": "Rekod Operasi",
+ "系统操作日志审计": "Audit Log Operasi Sistem",
+ "操作详情": "Butiran Operasi",
+ "操作描述": "Penerangan Operasi",
+ "操作者": "Pengendali",
+ "资源ID": "ID Sumber",
+ "资源类型": "Jenis Sumber",
+ "资源名称": "Nama Sumber",
+ "IP地址": "Alamat IP",
+ "兑换码": "Kod Tebusan",
+ "角色菜单权限配置": "Konfigurasi Kebenaran Menu Peranan",
+ "重置当前角色": "Tetapkan Semula Peranan Semasa",
+ "已重置角色配置": "Konfigurasi peranan telah ditetapkan semula",
+ "成本价": "Kos",
+ "供应商申请": "Permohonan Pembekal",
+ "供应商-申请": "Pembekal - Permohonan",
+ "供应商-渠道管理": "Pembekal - Pengurusan Saluran",
+ "供应商渠道管理": "Pengurusan Saluran Pembekal",
+ "供应商-定价设置": "Pembekal - Tetapan Harga",
+ "供应商定价设置": "Tetapan Harga Pembekal",
+ "供应商-数据看板": "Pembekal - Papan Pemuka",
+ "供应商数据看板": "Papan Pemuka Pembekal",
+ "支付宝 (Alipay) 设置": "Alipay Settings",
+ "支付宝 App ID": "Alipay App ID",
+ "支付宝开放平台应用的 APPID": "APPID of the Alipay Open Platform application",
+ "商户私钥": "Merchant Private Key",
+ "应用私钥,PKCS8 格式的 RSA2 私钥": "Application private key, RSA2 private key in PKCS8 format",
+ "支付宝公钥": "Alipay Public Key",
+ "支付宝开放平台获取的支付宝公钥": "Alipay public key obtained from Alipay Open Platform",
+ "自定义异步通知地址": "Custom Async Notify URL",
+ "自定义同步跳转地址": "Custom Sync Return URL",
+ "单价(元/额度)": "Unit Price (CNY/Quota)",
+ "例如:1,就是1元/额度": "e.g. 1 means 1 CNY per quota unit",
+ "最低充值额度": "Minimum Top-Up Amount",
+ "例如:1": "e.g. 1",
+ "沙箱模式": "Sandbox Mode",
+ "沙箱 App ID": "Sandbox App ID",
+ "沙箱环境的 APPID": "APPID for sandbox environment",
+ "沙箱商户私钥": "Sandbox Merchant Private Key",
+ "沙箱环境的应用私钥": "Application private key for sandbox environment",
+ "沙箱环境": "Sandbox Environment",
+ "支付宝开放平台": "Alipay Open Platform",
+ "启用支付宝支付": "Enable Alipay Payment",
+ "更新支付宝设置": "Update Alipay Settings",
+ "请确保已在支付宝开放平台配置 RSA2 密钥,并设置签约产品为「电脑网站支付」。": "Please ensure RSA2 keys are configured on Alipay Open Platform and the signed product is set to \"Website Payment\".",
+ "异步通知地址 (Notify URL)": "Async Notify URL",
+ "启用微信支付": "Enable WeChat Pay",
+ "微信支付 (WeChat Pay) 设置": "WeChat Pay Settings",
+ "微信公众号/小程序 AppID": "WeChat Official Account/Mini Program AppID",
+ "微信开放平台的 AppID": "AppID from WeChat Open Platform",
+ "商户号 (MchID)": "Merchant ID (MchID)",
+ "微信支付商户号": "WeChat Pay Merchant ID",
+ "APIv3 密钥": "APIv3 Key",
+ "APIv3 密钥,32位字符": "APIv3 key, 32 characters",
+ "商户 API 证书的私钥 (PKCS8 PEM)": "Merchant API Certificate Private Key (PKCS8 PEM)",
+ "证书序列号": "Certificate Serial Number",
+ "商户 API 证书序列号": "Merchant API Certificate Serial Number",
+ "微信支付商户平台": "WeChat Pay Merchant Platform",
+ "API v3 文档": "API v3 Documentation",
+ "更新微信支付设置": "Update WeChat Pay Settings",
+ "沙箱商户号": "Sandbox Merchant ID",
+ "沙箱环境的商户号": "Merchant ID for sandbox environment",
+ "沙箱 APIv3 密钥": "Sandbox APIv3 Key",
+ "沙箱环境 APIv3 密钥": "APIv3 key for sandbox environment",
+ "沙箱环境商户私钥": "Sandbox Merchant Private Key",
+ "请确保已配置 APIv3 密钥,并下载商户证书序列号。": "Please ensure the APIv3 key is configured and the merchant certificate serial number is downloaded.",
+ "可选,留空则使用默认地址": "Optional, leave blank to use default URL"
}
-}
+}
\ No newline at end of file
diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json
index 3f98963..ecbf543 100644
--- a/web/src/i18n/locales/ru.json
+++ b/web/src/i18n/locales/ru.json
@@ -5497,6 +5497,93 @@
"默认 ap-guangzhou,可改为 ap-beijing 等": "По умолчанию используется ap-guangzhou, который можно изменить на ap-beijing и т. д.",
"(共 {{total}} 个)": "(Всего {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(Всего элементов {{total}}, пропущены элементы {{omit}})",
- "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(В настоящее время поддерживается конфигурация Yipay/Yipay и по умолчанию используется указанный выше адрес сервера в качестве адреса обратного вызова!)"
+ "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(В настоящее время поддерживается конфигурация Yipay/Yipay и по умолчанию используется указанный выше адрес сервера в качестве адреса обратного вызова!)",
+ "热度配置": "Конфигурация тепла",
+ "模型热度排行配置": "Конфигурация рейтинга тепла моделей",
+ "热度分 = (手动基数 + 实际调用) x 权重": "Оценка тепла = (Ручная база + Факт. вызовы) x Вес",
+ "预估热度分": "Предполагаемая оценка тепла",
+ "统计周期": "Период статистики",
+ "统计周期设置": "Настройка периода статистики",
+ "统计周期已更新,重新加载数据中...": "Период обновлён, перезагрузка данных...",
+ "权重配置": "Настройка веса",
+ "手动调整": "Ручная настройка",
+ "实际调用": "Фактические вызовы",
+ "全部历史": "Вся история",
+ "近 7 天": "Последние 7 дней",
+ "近 30 天": "Последние 30 дней",
+ "模型 / 渠道": "Модель / Канал",
+ "搜索渠道": "Поиск канала",
+ "不修改": "Не изменять",
+ "批量保存成功": "Массовое сохранение успешно",
+ "批量保存失败": "Ошибка массового сохранения",
+ "序号": "No.",
+ "热门": "Популярное",
+ "操作记录": "Записи операций",
+ "系统操作日志审计": "Аудит журнала операций системы",
+ "操作详情": "Детали операции",
+ "操作描述": "Описание операции",
+ "操作者": "Оператор",
+ "资源ID": "ID ресурса",
+ "资源类型": "Тип ресурса",
+ "资源名称": "Имя ресурса",
+ "IP地址": "IP-адрес",
+ "兑换码": "Код обмена",
+ "角色菜单权限配置": "�������������� ������ ������ ������",
+ "重置当前角色": "Сбросить текущую роль",
+ "已重置角色配置": "Конфигурация роли была сброшена",
+ "成本价": "Себестоимость",
+ "供应商申请": "Заявка поставщика",
+ "供应商-申请": "Поставщик - Заявка",
+ "供应商-渠道管理": "Поставщик - Управление каналами",
+ "供应商渠道管理": "Управление каналами поставщика",
+ "供应商-定价设置": "Поставщик - Настройка цен",
+ "供应商定价设置": "Настройка цен поставщика",
+ "供应商-数据看板": "Поставщик - Панель управления",
+ "供应商数据看板": "Панель управления поставщика",
+ "支付宝 (Alipay) 设置": "Alipay Settings",
+ "支付宝 App ID": "Alipay App ID",
+ "支付宝开放平台应用的 APPID": "APPID of the Alipay Open Platform application",
+ "商户私钥": "Merchant Private Key",
+ "应用私钥,PKCS8 格式的 RSA2 私钥": "Application private key, RSA2 private key in PKCS8 format",
+ "支付宝公钥": "Alipay Public Key",
+ "支付宝开放平台获取的支付宝公钥": "Alipay public key obtained from Alipay Open Platform",
+ "自定义异步通知地址": "Custom Async Notify URL",
+ "自定义同步跳转地址": "Custom Sync Return URL",
+ "单价(元/额度)": "Unit Price (CNY/Quota)",
+ "例如:1,就是1元/额度": "e.g. 1 means 1 CNY per quota unit",
+ "最低充值额度": "Minimum Top-Up Amount",
+ "例如:1": "e.g. 1",
+ "沙箱模式": "Sandbox Mode",
+ "沙箱 App ID": "Sandbox App ID",
+ "沙箱环境的 APPID": "APPID for sandbox environment",
+ "沙箱商户私钥": "Sandbox Merchant Private Key",
+ "沙箱环境的应用私钥": "Application private key for sandbox environment",
+ "沙箱环境": "Sandbox Environment",
+ "支付宝开放平台": "Alipay Open Platform",
+ "启用支付宝支付": "Enable Alipay Payment",
+ "更新支付宝设置": "Update Alipay Settings",
+ "请确保已在支付宝开放平台配置 RSA2 密钥,并设置签约产品为「电脑网站支付」。": "Please ensure RSA2 keys are configured on Alipay Open Platform and the signed product is set to \"Website Payment\".",
+ "异步通知地址 (Notify URL)": "Async Notify URL",
+ "启用微信支付": "Enable WeChat Pay",
+ "微信支付 (WeChat Pay) 设置": "WeChat Pay Settings",
+ "微信公众号/小程序 AppID": "WeChat Official Account/Mini Program AppID",
+ "微信开放平台的 AppID": "AppID from WeChat Open Platform",
+ "商户号 (MchID)": "Merchant ID (MchID)",
+ "微信支付商户号": "WeChat Pay Merchant ID",
+ "APIv3 密钥": "APIv3 Key",
+ "APIv3 密钥,32位字符": "APIv3 key, 32 characters",
+ "商户 API 证书的私钥 (PKCS8 PEM)": "Merchant API Certificate Private Key (PKCS8 PEM)",
+ "证书序列号": "Certificate Serial Number",
+ "商户 API 证书序列号": "Merchant API Certificate Serial Number",
+ "微信支付商户平台": "WeChat Pay Merchant Platform",
+ "API v3 文档": "API v3 Documentation",
+ "更新微信支付设置": "Update WeChat Pay Settings",
+ "沙箱商户号": "Sandbox Merchant ID",
+ "沙箱环境的商户号": "Merchant ID for sandbox environment",
+ "沙箱 APIv3 密钥": "Sandbox APIv3 Key",
+ "沙箱环境 APIv3 密钥": "APIv3 key for sandbox environment",
+ "沙箱环境商户私钥": "Sandbox Merchant Private Key",
+ "请确保已配置 APIv3 密钥,并下载商户证书序列号。": "Please ensure the APIv3 key is configured and the merchant certificate serial number is downloaded.",
+ "可选,留空则使用默认地址": "Optional, leave blank to use default URL"
}
-}
+}
\ No newline at end of file
diff --git a/web/src/i18n/locales/sw.json b/web/src/i18n/locales/sw.json
index b248c28..97cbccf 100644
--- a/web/src/i18n/locales/sw.json
+++ b/web/src/i18n/locales/sw.json
@@ -5497,6 +5497,93 @@
"默认 ap-guangzhou,可改为 ap-beijing 等": "Chaguo-msingi ni ap-guangzhou, ambayo inaweza kubadilishwa kuwa ap-beijing n.k.",
"(共 {{total}} 个)": "(Jumla {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(Jumla ya vipengee {{total}}, vilivyoachwa {{omit}})",
- "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(Kwa sasa inaauni usanidi wa Yipay/Yipay, na hutumia anwani ya seva iliyo hapo juu kama anwani ya kupiga simu kwa chaguomsingi!)"
+ "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(Kwa sasa inaauni usanidi wa Yipay/Yipay, na hutumia anwani ya seva iliyo hapo juu kama anwani ya kupiga simu kwa chaguomsingi!)",
+ "热度配置": "Usanidi wa Joto",
+ "模型热度排行配置": "Usanidi wa Kiwango cha Joto cha Mfano",
+ "热度分 = (手动基数 + 实际调用) x 权重": "Alama ya Joto = (Msingi wa Mikono + Simu Halisi) x Uzito",
+ "预估热度分": "Alama ya Joto Inayokadiriwa",
+ "统计周期": "Kipindi cha Takwimu",
+ "统计周期设置": "Mpangilio wa Kipindi cha Takwimu",
+ "统计周期已更新,重新加载数据中...": "Kipindi kimesasishwa, inapakia upya data...",
+ "权重配置": "Usanidi wa Uzito",
+ "手动调整": "Marekebisho ya Mikono",
+ "实际调用": "Simu Halisi",
+ "全部历史": "Historia Yote",
+ "近 7 天": "Siku 7 Zilizopita",
+ "近 30 天": "Siku 30 Zilizopita",
+ "模型 / 渠道": "Mfano / Chaneli",
+ "搜索渠道": "Tafuta Chaneli",
+ "不修改": "Usibadilishe",
+ "批量保存成功": "Uhifadhi wa kundi umefaulu",
+ "批量保存失败": "Uhifadhi wa kundi umeshindwa",
+ "序号": "Nambari",
+ "热门": "Maarufu",
+ "操作记录": "Rekodi za Uendeshaji",
+ "系统操作日志审计": "Ukaguzi wa Kumbukumbu za Uendeshaji wa Mfumo",
+ "操作详情": "Maelezo ya Uendeshaji",
+ "操作描述": "Maelezo ya Uendeshaji",
+ "操作者": "Mwendeshaji",
+ "资源ID": "Kitambulisho cha Rasilimali",
+ "资源类型": "Aina ya Rasilimali",
+ "资源名称": "Jina la Rasilimali",
+ "IP地址": "Anwani ya IP",
+ "兑换码": "Msimbo wa Ukombozi",
+ "角色菜单权限配置": "Usanidi wa Ruhusa za Menyu ya Wajibu",
+ "重置当前角色": "Weka upya Wajibu wa Sasa",
+ "已重置角色配置": "Usanidi wa wajibu umewekwa upya",
+ "成本价": "Bei ya Gharama",
+ "供应商申请": "Maombi ya Msambazaji",
+ "供应商-申请": "Msambazaji - Maombi",
+ "供应商-渠道管理": "Msambazaji - Usimamizi wa Chaneli",
+ "供应商渠道管理": "Usimamizi wa Chaneli za Msambazaji",
+ "供应商-定价设置": "Msambazaji - Mipangilio ya Bei",
+ "供应商定价设置": "Mipangilio ya Bei ya Msambazaji",
+ "供应商-数据看板": "Msambazaji - Dashibodi",
+ "供应商数据看板": "Dashibodi ya Msambazaji",
+ "支付宝 (Alipay) 设置": "Alipay Settings",
+ "支付宝 App ID": "Alipay App ID",
+ "支付宝开放平台应用的 APPID": "APPID of the Alipay Open Platform application",
+ "商户私钥": "Merchant Private Key",
+ "应用私钥,PKCS8 格式的 RSA2 私钥": "Application private key, RSA2 private key in PKCS8 format",
+ "支付宝公钥": "Alipay Public Key",
+ "支付宝开放平台获取的支付宝公钥": "Alipay public key obtained from Alipay Open Platform",
+ "自定义异步通知地址": "Custom Async Notify URL",
+ "自定义同步跳转地址": "Custom Sync Return URL",
+ "单价(元/额度)": "Unit Price (CNY/Quota)",
+ "例如:1,就是1元/额度": "e.g. 1 means 1 CNY per quota unit",
+ "最低充值额度": "Minimum Top-Up Amount",
+ "例如:1": "e.g. 1",
+ "沙箱模式": "Sandbox Mode",
+ "沙箱 App ID": "Sandbox App ID",
+ "沙箱环境的 APPID": "APPID for sandbox environment",
+ "沙箱商户私钥": "Sandbox Merchant Private Key",
+ "沙箱环境的应用私钥": "Application private key for sandbox environment",
+ "沙箱环境": "Sandbox Environment",
+ "支付宝开放平台": "Alipay Open Platform",
+ "启用支付宝支付": "Enable Alipay Payment",
+ "更新支付宝设置": "Update Alipay Settings",
+ "请确保已在支付宝开放平台配置 RSA2 密钥,并设置签约产品为「电脑网站支付」。": "Please ensure RSA2 keys are configured on Alipay Open Platform and the signed product is set to \"Website Payment\".",
+ "异步通知地址 (Notify URL)": "Async Notify URL",
+ "启用微信支付": "Enable WeChat Pay",
+ "微信支付 (WeChat Pay) 设置": "WeChat Pay Settings",
+ "微信公众号/小程序 AppID": "WeChat Official Account/Mini Program AppID",
+ "微信开放平台的 AppID": "AppID from WeChat Open Platform",
+ "商户号 (MchID)": "Merchant ID (MchID)",
+ "微信支付商户号": "WeChat Pay Merchant ID",
+ "APIv3 密钥": "APIv3 Key",
+ "APIv3 密钥,32位字符": "APIv3 key, 32 characters",
+ "商户 API 证书的私钥 (PKCS8 PEM)": "Merchant API Certificate Private Key (PKCS8 PEM)",
+ "证书序列号": "Certificate Serial Number",
+ "商户 API 证书序列号": "Merchant API Certificate Serial Number",
+ "微信支付商户平台": "WeChat Pay Merchant Platform",
+ "API v3 文档": "API v3 Documentation",
+ "更新微信支付设置": "Update WeChat Pay Settings",
+ "沙箱商户号": "Sandbox Merchant ID",
+ "沙箱环境的商户号": "Merchant ID for sandbox environment",
+ "沙箱 APIv3 密钥": "Sandbox APIv3 Key",
+ "沙箱环境 APIv3 密钥": "APIv3 key for sandbox environment",
+ "沙箱环境商户私钥": "Sandbox Merchant Private Key",
+ "请确保已配置 APIv3 密钥,并下载商户证书序列号。": "Please ensure the APIv3 key is configured and the merchant certificate serial number is downloaded.",
+ "可选,留空则使用默认地址": "Optional, leave blank to use default URL"
}
-}
+}
\ No newline at end of file
diff --git a/web/src/i18n/locales/th.json b/web/src/i18n/locales/th.json
index a2e9ac1..5da7d98 100644
--- a/web/src/i18n/locales/th.json
+++ b/web/src/i18n/locales/th.json
@@ -5497,6 +5497,93 @@
"默认 ap-guangzhou,可改为 ap-beijing 等": "ค่าเริ่มต้นคือ ap-guangzhou ซึ่งสามารถเปลี่ยนเป็น ap-beijing เป็นต้น",
"(共 {{total}} 个)": "(รวม {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(ทั้งหมด {{total}} รายการ ละเว้น {{omit}} รายการ)",
- "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(ปัจจุบันรองรับการกำหนดค่า Yipay/Yipay และใช้ที่อยู่เซิร์ฟเวอร์ด้านบนเป็นที่อยู่โทรกลับตามค่าเริ่มต้น!)"
+ "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(ปัจจุบันรองรับการกำหนดค่า Yipay/Yipay และใช้ที่อยู่เซิร์ฟเวอร์ด้านบนเป็นที่อยู่โทรกลับตามค่าเริ่มต้น!)",
+ "热度配置": "การตั้งค่าความร้อน",
+ "模型热度排行配置": "การตั้งค่าการจัดอันดับความร้อนของโมเดล",
+ "热度分 = (手动基数 + 实际调用) x 权重": "คะแนนความร้อน = (ฐาน manual + การเรียกจริง) x น้ำหนัก",
+ "预估热度分": "คะแนนความร้อนโดยประมาณ",
+ "统计周期": "รอบระยะเวลาสถิติ",
+ "统计周期设置": "การตั้งค่ารอบระยะเวลาสถิติ",
+ "统计周期已更新,重新加载数据中...": "อัปเดตรอบระยะเวลาแล้ว กำลังโหลดข้อมูลใหม่...",
+ "权重配置": "การตั้งค่าน้ำหนัก",
+ "手动调整": "การปรับด้วยตนเอง",
+ "实际调用": "การเรียกจริง",
+ "全部历史": "ประวัติทั้งหมด",
+ "近 7 天": "7 วันที่ผ่านมา",
+ "近 30 天": "30 วันที่ผ่านมา",
+ "模型 / 渠道": "โมเดล / ช่องทาง",
+ "搜索渠道": "ค้นหาช่องทาง",
+ "不修改": "ไม่เปลี่ยนแปลง",
+ "批量保存成功": "บันทึกเป็นกลุ่มสำเร็จ",
+ "批量保存失败": "บันทึกเป็นกลุ่มล้มเหลว",
+ "序号": "ลำดับ",
+ "热门": "ยอดนิยม",
+ "操作记录": "บันทึกการดำเนินการ",
+ "系统操作日志审计": "การตรวจสอบบันทึกการดำเนินงานของระบบ",
+ "操作详情": "รายละเอียดการดำเนินการ",
+ "操作描述": "คำอธิบายการดำเนินการ",
+ "操作者": "ผู้ดำเนินการ",
+ "资源ID": "ID ทรัพยากร",
+ "资源类型": "ประเภททรัพยากร",
+ "资源名称": "ชื่อทรัพยากร",
+ "IP地址": "ที่อยู่ IP",
+ "兑换码": "รหัสแลก",
+ "角色菜单权限配置": "การกำหนดค่าสิทธิ์เมนูบทบาท",
+ "重置当前角色": "รีเซ็ตบทบาทปัจจุบัน",
+ "已重置角色配置": "รีเซ็ตการกำหนดค่าบทบาทแล้ว",
+ "成本价": "ราคาทุน",
+ "供应商申请": "คําขอผู้จัดจําหน่าย",
+ "供应商-申请": "ผู้จัดจําหน่าย - คําขอ",
+ "供应商-渠道管理": "ผู้จัดจําหน่าย - การจัดการช่องทาง",
+ "供应商渠道管理": "การจัดการช่องทางผู้จัดจําหน่าย",
+ "供应商-定价设置": "ผู้จัดจําหน่าย - การตั้งราคา",
+ "供应商定价设置": "การตั้งราคาผู้จัดจําหน่าย",
+ "供应商-数据看板": "ผู้จัดจําหน่าย - แดชบอร์ด",
+ "供应商数据看板": "แดชบอร์ดผู้จัดจําหน่าย",
+ "支付宝 (Alipay) 设置": "Alipay Settings",
+ "支付宝 App ID": "Alipay App ID",
+ "支付宝开放平台应用的 APPID": "APPID of the Alipay Open Platform application",
+ "商户私钥": "Merchant Private Key",
+ "应用私钥,PKCS8 格式的 RSA2 私钥": "Application private key, RSA2 private key in PKCS8 format",
+ "支付宝公钥": "Alipay Public Key",
+ "支付宝开放平台获取的支付宝公钥": "Alipay public key obtained from Alipay Open Platform",
+ "自定义异步通知地址": "Custom Async Notify URL",
+ "自定义同步跳转地址": "Custom Sync Return URL",
+ "单价(元/额度)": "Unit Price (CNY/Quota)",
+ "例如:1,就是1元/额度": "e.g. 1 means 1 CNY per quota unit",
+ "最低充值额度": "Minimum Top-Up Amount",
+ "例如:1": "e.g. 1",
+ "沙箱模式": "Sandbox Mode",
+ "沙箱 App ID": "Sandbox App ID",
+ "沙箱环境的 APPID": "APPID for sandbox environment",
+ "沙箱商户私钥": "Sandbox Merchant Private Key",
+ "沙箱环境的应用私钥": "Application private key for sandbox environment",
+ "沙箱环境": "Sandbox Environment",
+ "支付宝开放平台": "Alipay Open Platform",
+ "启用支付宝支付": "Enable Alipay Payment",
+ "更新支付宝设置": "Update Alipay Settings",
+ "请确保已在支付宝开放平台配置 RSA2 密钥,并设置签约产品为「电脑网站支付」。": "Please ensure RSA2 keys are configured on Alipay Open Platform and the signed product is set to \"Website Payment\".",
+ "异步通知地址 (Notify URL)": "Async Notify URL",
+ "启用微信支付": "Enable WeChat Pay",
+ "微信支付 (WeChat Pay) 设置": "WeChat Pay Settings",
+ "微信公众号/小程序 AppID": "WeChat Official Account/Mini Program AppID",
+ "微信开放平台的 AppID": "AppID from WeChat Open Platform",
+ "商户号 (MchID)": "Merchant ID (MchID)",
+ "微信支付商户号": "WeChat Pay Merchant ID",
+ "APIv3 密钥": "APIv3 Key",
+ "APIv3 密钥,32位字符": "APIv3 key, 32 characters",
+ "商户 API 证书的私钥 (PKCS8 PEM)": "Merchant API Certificate Private Key (PKCS8 PEM)",
+ "证书序列号": "Certificate Serial Number",
+ "商户 API 证书序列号": "Merchant API Certificate Serial Number",
+ "微信支付商户平台": "WeChat Pay Merchant Platform",
+ "API v3 文档": "API v3 Documentation",
+ "更新微信支付设置": "Update WeChat Pay Settings",
+ "沙箱商户号": "Sandbox Merchant ID",
+ "沙箱环境的商户号": "Merchant ID for sandbox environment",
+ "沙箱 APIv3 密钥": "Sandbox APIv3 Key",
+ "沙箱环境 APIv3 密钥": "APIv3 key for sandbox environment",
+ "沙箱环境商户私钥": "Sandbox Merchant Private Key",
+ "请确保已配置 APIv3 密钥,并下载商户证书序列号。": "Please ensure the APIv3 key is configured and the merchant certificate serial number is downloaded.",
+ "可选,留空则使用默认地址": "Optional, leave blank to use default URL"
}
-}
+}
\ No newline at end of file
diff --git a/web/src/i18n/locales/vi.json b/web/src/i18n/locales/vi.json
index e1a6ec3..690f91f 100644
--- a/web/src/i18n/locales/vi.json
+++ b/web/src/i18n/locales/vi.json
@@ -5497,6 +5497,93 @@
"默认 ap-guangzhou,可改为 ap-beijing 等": "Mặc định là ap-guangzhou, có thể thay đổi thành ap-beijing, v.v.",
"(共 {{total}} 个)": "(Tổng cộng {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(Tổng số mục {{total}}, đã bỏ qua mục {{omit}})",
- "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(Hiện hỗ trợ cấu hình Yipay/Yipay và sử dụng địa chỉ máy chủ ở trên làm địa chỉ gọi lại theo mặc định!)"
+ "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(Hiện hỗ trợ cấu hình Yipay/Yipay và sử dụng địa chỉ máy chủ ở trên làm địa chỉ gọi lại theo mặc định!)",
+ "热度配置": "Cau hinh nhiet",
+ "模型热度排行配置": "Cau hinh xep hang nhiet mo hinh",
+ "热度分 = (手动基数 + 实际调用) x 权重": "Diem nhiet = (Co so thu cong + Cuoc goi thuc te) x Trong so",
+ "预估热度分": "Diem nhiet uoc tinh",
+ "统计周期": "Chu ky thong ke",
+ "统计周期设置": "Cai dat chu ky thong ke",
+ "统计周期已更新,重新加载数据中...": "Chu ky da cap nhat, dang tai lai du lieu...",
+ "权重配置": "Cau hinh trong so",
+ "手动调整": "Dieu chinh thu cong",
+ "实际调用": "Cuoc goi thuc te",
+ "全部历史": "Tat ca lich su",
+ "近 7 天": "7 ngay qua",
+ "近 30 天": "30 ngay qua",
+ "模型 / 渠道": "Mo hinh / Kenh",
+ "搜索渠道": "Tim kenh",
+ "不修改": "Khong thay doi",
+ "批量保存成功": "Luu hang loat thanh cong",
+ "批量保存失败": "Luu hang loat that bai",
+ "序号": "STT",
+ "热门": "Pho bien",
+ "操作记录": "Nhat ky thao tac",
+ "系统操作日志审计": "Kiem tra nhat ky thao tac he thong",
+ "操作详情": "Chi tiet thao tac",
+ "操作描述": "Mo ta thao tac",
+ "操作者": "Nguoi thao tac",
+ "资源ID": "ID tai nguyen",
+ "资源类型": "Loai tai nguyen",
+ "资源名称": "Ten tai nguyen",
+ "IP地址": "Dia chi IP",
+ "兑换码": "Ma doi thuong",
+ "角色菜单权限配置": "Cau hinh quyen menu vai tro",
+ "重置当前角色": "Dat lai vai tro hien tai",
+ "已重置角色配置": "Cau hinh vai tro da duoc dat lai",
+ "成本价": "Gia von",
+ "供应商申请": "Don xin nha cung cap",
+ "供应商-申请": "Nha cung cap - Don xin",
+ "供应商-渠道管理": "Nha cung cap - Quan ly kenh",
+ "供应商渠道管理": "Quan ly kenh nha cung cap",
+ "供应商-定价设置": "Nha cung cap - Cai dat gia",
+ "供应商定价设置": "Cai dat gia nha cung cap",
+ "供应商-数据看板": "Nha cung cap - Bang dieu khien",
+ "供应商数据看板": "Bang dieu khien nha cung cap",
+ "支付宝 (Alipay) 设置": "Alipay Settings",
+ "支付宝 App ID": "Alipay App ID",
+ "支付宝开放平台应用的 APPID": "APPID of the Alipay Open Platform application",
+ "商户私钥": "Merchant Private Key",
+ "应用私钥,PKCS8 格式的 RSA2 私钥": "Application private key, RSA2 private key in PKCS8 format",
+ "支付宝公钥": "Alipay Public Key",
+ "支付宝开放平台获取的支付宝公钥": "Alipay public key obtained from Alipay Open Platform",
+ "自定义异步通知地址": "Custom Async Notify URL",
+ "自定义同步跳转地址": "Custom Sync Return URL",
+ "单价(元/额度)": "Unit Price (CNY/Quota)",
+ "例如:1,就是1元/额度": "e.g. 1 means 1 CNY per quota unit",
+ "最低充值额度": "Minimum Top-Up Amount",
+ "例如:1": "e.g. 1",
+ "沙箱模式": "Sandbox Mode",
+ "沙箱 App ID": "Sandbox App ID",
+ "沙箱环境的 APPID": "APPID for sandbox environment",
+ "沙箱商户私钥": "Sandbox Merchant Private Key",
+ "沙箱环境的应用私钥": "Application private key for sandbox environment",
+ "沙箱环境": "Sandbox Environment",
+ "支付宝开放平台": "Alipay Open Platform",
+ "启用支付宝支付": "Enable Alipay Payment",
+ "更新支付宝设置": "Update Alipay Settings",
+ "请确保已在支付宝开放平台配置 RSA2 密钥,并设置签约产品为「电脑网站支付」。": "Please ensure RSA2 keys are configured on Alipay Open Platform and the signed product is set to \"Website Payment\".",
+ "异步通知地址 (Notify URL)": "Async Notify URL",
+ "启用微信支付": "Enable WeChat Pay",
+ "微信支付 (WeChat Pay) 设置": "WeChat Pay Settings",
+ "微信公众号/小程序 AppID": "WeChat Official Account/Mini Program AppID",
+ "微信开放平台的 AppID": "AppID from WeChat Open Platform",
+ "商户号 (MchID)": "Merchant ID (MchID)",
+ "微信支付商户号": "WeChat Pay Merchant ID",
+ "APIv3 密钥": "APIv3 Key",
+ "APIv3 密钥,32位字符": "APIv3 key, 32 characters",
+ "商户 API 证书的私钥 (PKCS8 PEM)": "Merchant API Certificate Private Key (PKCS8 PEM)",
+ "证书序列号": "Certificate Serial Number",
+ "商户 API 证书序列号": "Merchant API Certificate Serial Number",
+ "微信支付商户平台": "WeChat Pay Merchant Platform",
+ "API v3 文档": "API v3 Documentation",
+ "更新微信支付设置": "Update WeChat Pay Settings",
+ "沙箱商户号": "Sandbox Merchant ID",
+ "沙箱环境的商户号": "Merchant ID for sandbox environment",
+ "沙箱 APIv3 密钥": "Sandbox APIv3 Key",
+ "沙箱环境 APIv3 密钥": "APIv3 key for sandbox environment",
+ "沙箱环境商户私钥": "Sandbox Merchant Private Key",
+ "请确保已配置 APIv3 密钥,并下载商户证书序列号。": "Please ensure the APIv3 key is configured and the merchant certificate serial number is downloaded.",
+ "可选,留空则使用默认地址": "Optional, leave blank to use default URL"
}
-}
+}
\ No newline at end of file
diff --git a/web/src/i18n/locales/zh-CN.json b/web/src/i18n/locales/zh-CN.json
index 847d9b1..46e15cf 100644
--- a/web/src/i18n/locales/zh-CN.json
+++ b/web/src/i18n/locales/zh-CN.json
@@ -5445,6 +5445,93 @@
"(共 {{total}} 个)": "(共 {{total}} 个)",
"(共 {{total}} 个,省略 {{omit}} 个)": "(共 {{total}} 个,省略 {{omit}} 个)",
"(含上次测试结果)": "(含上次测试结果)",
- "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)"
+ "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)",
+ "热度配置": "热度配置",
+ "模型热度排行配置": "模型热度排行配置",
+ "热度分 = (手动基数 + 实际调用) x 权重": "热度分 = (手动基数 + 实际调用) x 权重",
+ "预估热度分": "预估热度分",
+ "统计周期": "统计周期",
+ "统计周期设置": "统计周期设置",
+ "统计周期已更新,重新加载数据中...": "统计周期已更新,重新加载数据中...",
+ "权重配置": "权重配置",
+ "手动调整": "手动调整",
+ "实际调用": "实际调用",
+ "全部历史": "全部历史",
+ "近 7 天": "近 7 天",
+ "近 30 天": "近 30 天",
+ "模型 / 渠道": "模型 / 渠道",
+ "搜索渠道": "搜索渠道",
+ "不修改": "不修改",
+ "批量保存成功": "批量保存成功",
+ "批量保存失败": "批量保存失败",
+ "序号": "序号",
+ "热门": "热门",
+ "操作记录": "操作记录",
+ "系统操作日志审计": "系统操作日志审计",
+ "操作详情": "操作详情",
+ "操作描述": "操作描述",
+ "操作者": "操作者",
+ "资源ID": "资源ID",
+ "资源类型": "资源类型",
+ "资源名称": "资源名称",
+ "IP地址": "IP地址",
+ "兑换码": "兑换码",
+ "角色菜单权限配置": "角色菜单权限配置",
+ "重置当前角色": "重置当前角色",
+ "已重置角色配置": "已重置角色配置",
+ "成本价": "成本价",
+ "供应商申请": "供应商申请",
+ "供应商-申请": "供应商-申请",
+ "供应商-渠道管理": "供应商-渠道管理",
+ "供应商渠道管理": "供应商渠道管理",
+ "供应商-定价设置": "供应商-定价设置",
+ "供应商定价设置": "供应商定价设置",
+ "供应商-数据看板": "供应商-数据看板",
+ "供应商数据看板": "供应商数据看板",
+ "支付宝 (Alipay) 设置": "支付宝 (Alipay) 设置",
+ "支付宝 App ID": "支付宝 App ID",
+ "支付宝开放平台应用的 APPID": "支付宝开放平台应用的 APPID",
+ "商户私钥": "商户私钥",
+ "应用私钥,PKCS8 格式的 RSA2 私钥": "应用私钥,PKCS8 格式的 RSA2 私钥",
+ "支付宝公钥": "支付宝公钥",
+ "支付宝开放平台获取的支付宝公钥": "支付宝开放平台获取的支付宝公钥",
+ "自定义异步通知地址": "自定义异步通知地址",
+ "自定义同步跳转地址": "自定义同步跳转地址",
+ "单价(元/额度)": "单价(元/额度)",
+ "例如:1,就是1元/额度": "例如:1,就是1元/额度",
+ "最低充值额度": "最低充值额度",
+ "例如:1": "例如:1",
+ "沙箱模式": "沙箱模式",
+ "沙箱 App ID": "沙箱 App ID",
+ "沙箱环境的 APPID": "沙箱环境的 APPID",
+ "沙箱商户私钥": "沙箱商户私钥",
+ "沙箱环境的应用私钥": "沙箱环境的应用私钥",
+ "沙箱环境": "沙箱环境",
+ "支付宝开放平台": "支付宝开放平台",
+ "启用支付宝支付": "启用支付宝支付",
+ "更新支付宝设置": "更新支付宝设置",
+ "请确保已在支付宝开放平台配置 RSA2 密钥,并设置签约产品为「电脑网站支付」。": "请确保已在支付宝开放平台配置 RSA2 密钥,并设置签约产品为「电脑网站支付」。",
+ "异步通知地址 (Notify URL)": "异步通知地址 (Notify URL)",
+ "启用微信支付": "启用微信支付",
+ "微信支付 (WeChat Pay) 设置": "微信支付 (WeChat Pay) 设置",
+ "微信公众号/小程序 AppID": "微信公众号/小程序 AppID",
+ "微信开放平台的 AppID": "微信开放平台的 AppID",
+ "商户号 (MchID)": "商户号 (MchID)",
+ "微信支付商户号": "微信支付商户号",
+ "APIv3 密钥": "APIv3 密钥",
+ "APIv3 密钥,32位字符": "APIv3 密钥,32位字符",
+ "商户 API 证书的私钥 (PKCS8 PEM)": "商户 API 证书的私钥 (PKCS8 PEM)",
+ "证书序列号": "证书序列号",
+ "商户 API 证书序列号": "商户 API 证书序列号",
+ "微信支付商户平台": "微信支付商户平台",
+ "API v3 文档": "API v3 文档",
+ "更新微信支付设置": "更新微信支付设置",
+ "沙箱商户号": "沙箱商户号",
+ "沙箱环境的商户号": "沙箱环境的商户号",
+ "沙箱 APIv3 密钥": "沙箱 APIv3 密钥",
+ "沙箱环境 APIv3 密钥": "沙箱环境 APIv3 密钥",
+ "沙箱环境商户私钥": "沙箱环境商户私钥",
+ "请确保已配置 APIv3 密钥,并下载商户证书序列号。": "请确保已配置 APIv3 密钥,并下载商户证书序列号。",
+ "可选,留空则使用默认地址": "可选,留空则使用默认地址"
}
-}
+}
\ No newline at end of file
diff --git a/web/src/i18n/locales/zh-TW.json b/web/src/i18n/locales/zh-TW.json
index f3225fc..06bcb80 100644
--- a/web/src/i18n/locales/zh-TW.json
+++ b/web/src/i18n/locales/zh-TW.json
@@ -5496,6 +5496,93 @@
"默认 ap-guangzhou,可改为 ap-beijing 等": "預設 ap-guangzhou,可改為 ap-beijing 等",
"(共 {{total}} 个)": "(共 {{total}} 個)",
"(共 {{total}} 个,省略 {{omit}} 个)": "(共 {{total}} 個,省略 {{omit}} 個)",
- "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(目前支援易付款/Yipay 配置,預設使用上方伺服器位址作為回呼位址!)"
+ "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(目前支援易付款/Yipay 配置,預設使用上方伺服器位址作為回呼位址!)",
+ "热度配置": "熱度配置",
+ "模型热度排行配置": "模型熱度排行配置",
+ "热度分 = (手动基数 + 实际调用) x 权重": "熱度分 = (手動基數 + 實際調用) x 權重",
+ "预估热度分": "預估熱度分",
+ "统计周期": "統計週期",
+ "统计周期设置": "統計週期設置",
+ "统计周期已更新,重新加载数据中...": "統計週期已更新,重新加載數據中...",
+ "权重配置": "權重配置",
+ "手动调整": "手動調整",
+ "实际调用": "實際調用",
+ "全部历史": "全部歷史",
+ "近 7 天": "近 7 天",
+ "近 30 天": "近 30 天",
+ "模型 / 渠道": "模型 / 渠道",
+ "搜索渠道": "搜索渠道",
+ "不修改": "不修改",
+ "批量保存成功": "批量保存成功",
+ "批量保存失败": "批量保存失敗",
+ "序号": "序號",
+ "热门": "熱門",
+ "操作记录": "操作記錄",
+ "系统操作日志审计": "系統操作日誌審計",
+ "操作详情": "操作詳情",
+ "操作描述": "操作描述",
+ "操作者": "操作者",
+ "资源ID": "資源ID",
+ "资源类型": "資源類型",
+ "资源名称": "資源名稱",
+ "IP地址": "IP地址",
+ "兑换码": "兌換碼",
+ "角色菜单权限配置": "角色菜單權限配置",
+ "重置当前角色": "重置當前角色",
+ "已重置角色配置": "已重置角色配置",
+ "成本价": "成本價",
+ "供应商申请": "供應商申請",
+ "供应商-申请": "供應商-申請",
+ "供应商-渠道管理": "供應商-渠道管理",
+ "供应商渠道管理": "供應商渠道管理",
+ "供应商-定价设置": "供應商-定價設置",
+ "供应商定价设置": "供應商定價設置",
+ "供应商-数据看板": "供應商-數據看板",
+ "供应商数据看板": "供應商數據看板",
+ "支付宝 (Alipay) 设置": "Alipay Settings",
+ "支付宝 App ID": "Alipay App ID",
+ "支付宝开放平台应用的 APPID": "APPID of the Alipay Open Platform application",
+ "商户私钥": "Merchant Private Key",
+ "应用私钥,PKCS8 格式的 RSA2 私钥": "Application private key, RSA2 private key in PKCS8 format",
+ "支付宝公钥": "Alipay Public Key",
+ "支付宝开放平台获取的支付宝公钥": "Alipay public key obtained from Alipay Open Platform",
+ "自定义异步通知地址": "Custom Async Notify URL",
+ "自定义同步跳转地址": "Custom Sync Return URL",
+ "单价(元/额度)": "Unit Price (CNY/Quota)",
+ "例如:1,就是1元/额度": "e.g. 1 means 1 CNY per quota unit",
+ "最低充值额度": "Minimum Top-Up Amount",
+ "例如:1": "e.g. 1",
+ "沙箱模式": "Sandbox Mode",
+ "沙箱 App ID": "Sandbox App ID",
+ "沙箱环境的 APPID": "APPID for sandbox environment",
+ "沙箱商户私钥": "Sandbox Merchant Private Key",
+ "沙箱环境的应用私钥": "Application private key for sandbox environment",
+ "沙箱环境": "Sandbox Environment",
+ "支付宝开放平台": "Alipay Open Platform",
+ "启用支付宝支付": "Enable Alipay Payment",
+ "更新支付宝设置": "Update Alipay Settings",
+ "请确保已在支付宝开放平台配置 RSA2 密钥,并设置签约产品为「电脑网站支付」。": "Please ensure RSA2 keys are configured on Alipay Open Platform and the signed product is set to \"Website Payment\".",
+ "异步通知地址 (Notify URL)": "Async Notify URL",
+ "启用微信支付": "Enable WeChat Pay",
+ "微信支付 (WeChat Pay) 设置": "WeChat Pay Settings",
+ "微信公众号/小程序 AppID": "WeChat Official Account/Mini Program AppID",
+ "微信开放平台的 AppID": "AppID from WeChat Open Platform",
+ "商户号 (MchID)": "Merchant ID (MchID)",
+ "微信支付商户号": "WeChat Pay Merchant ID",
+ "APIv3 密钥": "APIv3 Key",
+ "APIv3 密钥,32位字符": "APIv3 key, 32 characters",
+ "商户 API 证书的私钥 (PKCS8 PEM)": "Merchant API Certificate Private Key (PKCS8 PEM)",
+ "证书序列号": "Certificate Serial Number",
+ "商户 API 证书序列号": "Merchant API Certificate Serial Number",
+ "微信支付商户平台": "WeChat Pay Merchant Platform",
+ "API v3 文档": "API v3 Documentation",
+ "更新微信支付设置": "Update WeChat Pay Settings",
+ "沙箱商户号": "Sandbox Merchant ID",
+ "沙箱环境的商户号": "Merchant ID for sandbox environment",
+ "沙箱 APIv3 密钥": "Sandbox APIv3 Key",
+ "沙箱环境 APIv3 密钥": "APIv3 key for sandbox environment",
+ "沙箱环境商户私钥": "Sandbox Merchant Private Key",
+ "请确保已配置 APIv3 密钥,并下载商户证书序列号。": "Please ensure the APIv3 key is configured and the merchant certificate serial number is downloaded.",
+ "可选,留空则使用默认地址": "Optional, leave blank to use default URL"
}
}
\ No newline at end of file
diff --git a/web/src/pages/Setting/Operation/SettingsSidebarModulesAdmin.jsx b/web/src/pages/Setting/Operation/SettingsSidebarModulesAdmin.jsx
index ce336fe..8dbfc07 100644
--- a/web/src/pages/Setting/Operation/SettingsSidebarModulesAdmin.jsx
+++ b/web/src/pages/Setting/Operation/SettingsSidebarModulesAdmin.jsx
@@ -336,9 +336,9 @@ export default function SettingsSidebarModulesAdmin(props) {
value={selectedRole}
onChange={(e) => setSelectedRole(e.target.value)}
>
- {USER_ROLE_NAMES[USER_ROLES.USER]}
- {USER_ROLE_NAMES[USER_ROLES.ADMIN]}
- {USER_ROLE_NAMES[USER_ROLES.ROOT]}
+ {t(USER_ROLE_NAMES[USER_ROLES.USER])}
+ {t(USER_ROLE_NAMES[USER_ROLES.ADMIN])}
+ {t(USER_ROLE_NAMES[USER_ROLES.ROOT])}
diff --git a/web/src/pages/Setting/Payment/SettingsPaymentGatewayAlipay.jsx b/web/src/pages/Setting/Payment/SettingsPaymentGatewayAlipay.jsx
new file mode 100644
index 0000000..745b95a
--- /dev/null
+++ b/web/src/pages/Setting/Payment/SettingsPaymentGatewayAlipay.jsx
@@ -0,0 +1,308 @@
+import React, { useEffect, useState, useRef } from "react";
+import {
+ Banner,
+ Button,
+ Form,
+ Row,
+ Col,
+ Typography,
+ Spin,
+} from "@douyinfe/semi-ui";
+const { Text } = Typography;
+import {
+ API,
+ removeTrailingSlash,
+ showError,
+ showSuccess,
+} from "../../../helpers";
+import { useTranslation } from "react-i18next";
+
+export default function SettingsPaymentGatewayAlipay(props) {
+ const { t } = useTranslation();
+ const [loading, setLoading] = useState(false);
+ const [inputs, setInputs] = useState({
+ AlipayEnabled: false,
+ AlipayAppId: "",
+ AlipayPrivateKey: "",
+ AlipayPublicKey: "",
+ AlipayNotifyUrl: "",
+ AlipayReturnUrl: "",
+ AlipayUnitPrice: 1.0,
+ AlipayMinTopUp: 1,
+ AlipaySandbox: false,
+ AlipaySandboxAppId: "",
+ AlipaySandboxPrivateKey: "",
+ });
+ const [originInputs, setOriginInputs] = useState({});
+ const formApiRef = useRef(null);
+
+ useEffect(() => {
+ if (props.options && formApiRef.current) {
+ const currentInputs = {
+ AlipayEnabled: props.options.AlipayEnabled || false,
+ AlipayAppId: props.options.AlipayAppId || "",
+ AlipayPrivateKey: props.options.AlipayPrivateKey || "",
+ AlipayPublicKey: props.options.AlipayPublicKey || "",
+ AlipayNotifyUrl: props.options.AlipayNotifyUrl || "",
+ AlipayReturnUrl: props.options.AlipayReturnUrl || "",
+ AlipayUnitPrice:
+ props.options.AlipayUnitPrice !== undefined
+ ? parseFloat(props.options.AlipayUnitPrice)
+ : 1.0,
+ AlipayMinTopUp:
+ props.options.AlipayMinTopUp !== undefined
+ ? parseInt(props.options.AlipayMinTopUp)
+ : 1,
+ AlipaySandbox: props.options.AlipaySandbox || false,
+ AlipaySandboxAppId: props.options.AlipaySandboxAppId || "",
+ AlipaySandboxPrivateKey: props.options.AlipaySandboxPrivateKey || "",
+ };
+ setInputs(currentInputs);
+ setOriginInputs({ ...currentInputs });
+ formApiRef.current.setValues(currentInputs);
+ }
+ }, [props.options]);
+
+ const handleFormChange = (values) => {
+ setInputs(values);
+ };
+
+ const submitAlipaySetting = async () => {
+ if (props.options.ServerAddress === "") {
+ showError(t("请先填写服务器地址"));
+ return;
+ }
+
+ setLoading(true);
+ try {
+ const options = [];
+
+ options.push({
+ key: "AlipayEnabled",
+ value: inputs.AlipayEnabled ? "true" : "false",
+ });
+ if (inputs.AlipayAppId !== "") {
+ options.push({ key: "AlipayAppId", value: inputs.AlipayAppId });
+ }
+ if (inputs.AlipayPrivateKey !== "") {
+ options.push({
+ key: "AlipayPrivateKey",
+ value: inputs.AlipayPrivateKey,
+ });
+ }
+ if (inputs.AlipayPublicKey !== "") {
+ options.push({
+ key: "AlipayPublicKey",
+ value: inputs.AlipayPublicKey,
+ });
+ }
+ if (inputs.AlipayNotifyUrl !== "") {
+ options.push({
+ key: "AlipayNotifyUrl",
+ value: inputs.AlipayNotifyUrl,
+ });
+ }
+ if (inputs.AlipayReturnUrl !== "") {
+ options.push({
+ key: "AlipayReturnUrl",
+ value: inputs.AlipayReturnUrl,
+ });
+ }
+ options.push({
+ key: "AlipayUnitPrice",
+ value: inputs.AlipayUnitPrice.toString(),
+ });
+ options.push({
+ key: "AlipayMinTopUp",
+ value: inputs.AlipayMinTopUp.toString(),
+ });
+ options.push({
+ key: "AlipaySandbox",
+ value: inputs.AlipaySandbox ? "true" : "false",
+ });
+ if (inputs.AlipaySandboxAppId !== "") {
+ options.push({
+ key: "AlipaySandboxAppId",
+ value: inputs.AlipaySandboxAppId,
+ });
+ }
+ if (inputs.AlipaySandboxPrivateKey !== "") {
+ options.push({
+ key: "AlipaySandboxPrivateKey",
+ value: inputs.AlipaySandboxPrivateKey,
+ });
+ }
+
+ const requestQueue = options.map((opt) =>
+ API.put("/api/option/", {
+ key: opt.key,
+ value: opt.value,
+ })
+ );
+
+ const results = await Promise.all(requestQueue);
+ const errorResults = results.filter((res) => !res.data.success);
+ if (errorResults.length > 0) {
+ errorResults.forEach((res) => {
+ showError(res.data.message);
+ });
+ } else {
+ showSuccess(t("更新成功"));
+ setOriginInputs({ ...inputs });
+ props.refresh?.();
+ }
+ } catch (error) {
+ showError(t("更新失败"));
+ }
+ setLoading(false);
+ };
+
+ return (
+
+
+
+ {t(
+ "支付宝支付集成说明:请先在支付宝开放平台创建应用并获取以下配置信息。"
+ )}
+
+
+ {t("支付宝开放平台")}
+
+ {" | "}
+
+ {t("沙箱环境")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/web/src/pages/Setting/Payment/SettingsPaymentGatewayWechat.jsx b/web/src/pages/Setting/Payment/SettingsPaymentGatewayWechat.jsx
new file mode 100644
index 0000000..5a37216
--- /dev/null
+++ b/web/src/pages/Setting/Payment/SettingsPaymentGatewayWechat.jsx
@@ -0,0 +1,351 @@
+import React, { useEffect, useState, useRef } from "react";
+import {
+ Banner,
+ Button,
+ Form,
+ Row,
+ Col,
+ Typography,
+ Spin,
+} from "@douyinfe/semi-ui";
+const { Text } = Typography;
+import {
+ API,
+ removeTrailingSlash,
+ showError,
+ showSuccess,
+} from "../../../helpers";
+import { useTranslation } from "react-i18next";
+
+export default function SettingsPaymentGatewayWechat(props) {
+ const { t } = useTranslation();
+ const [loading, setLoading] = useState(false);
+ const [inputs, setInputs] = useState({
+ WechatPayEnabled: false,
+ WechatPayAppId: "",
+ WechatPayMchId: "",
+ WechatPayApiV3Key: "",
+ WechatPayPrivateKey: "",
+ WechatPayCertSerial: "",
+ WechatPayNotifyUrl: "",
+ WechatPayReturnUrl: "",
+ WechatPayUnitPrice: 1.0,
+ WechatPayMinTopUp: 1,
+ WechatPaySandbox: false,
+ WechatPaySandboxMchId: "",
+ WechatPaySandboxApiV3Key: "",
+ WechatPaySandboxPrivateKey: "",
+ });
+ const [originInputs, setOriginInputs] = useState({});
+ const formApiRef = useRef(null);
+
+ useEffect(() => {
+ if (props.options && formApiRef.current) {
+ const currentInputs = {
+ WechatPayEnabled: props.options.WechatPayEnabled || false,
+ WechatPayAppId: props.options.WechatPayAppId || "",
+ WechatPayMchId: props.options.WechatPayMchId || "",
+ WechatPayApiV3Key: props.options.WechatPayApiV3Key || "",
+ WechatPayPrivateKey: props.options.WechatPayPrivateKey || "",
+ WechatPayCertSerial: props.options.WechatPayCertSerial || "",
+ WechatPayNotifyUrl: props.options.WechatPayNotifyUrl || "",
+ WechatPayReturnUrl: props.options.WechatPayReturnUrl || "",
+ WechatPayUnitPrice:
+ props.options.WechatPayUnitPrice !== undefined
+ ? parseFloat(props.options.WechatPayUnitPrice)
+ : 1.0,
+ WechatPayMinTopUp:
+ props.options.WechatPayMinTopUp !== undefined
+ ? parseInt(props.options.WechatPayMinTopUp)
+ : 1,
+ WechatPaySandbox: props.options.WechatPaySandbox || false,
+ WechatPaySandboxMchId: props.options.WechatPaySandboxMchId || "",
+ WechatPaySandboxApiV3Key:
+ props.options.WechatPaySandboxApiV3Key || "",
+ WechatPaySandboxPrivateKey:
+ props.options.WechatPaySandboxPrivateKey || "",
+ };
+ setInputs(currentInputs);
+ setOriginInputs({ ...currentInputs });
+ formApiRef.current.setValues(currentInputs);
+ }
+ }, [props.options]);
+
+ const handleFormChange = (values) => {
+ setInputs(values);
+ };
+
+ const submitWechatPaySetting = async () => {
+ if (props.options.ServerAddress === "") {
+ showError(t("请先填写服务器地址"));
+ return;
+ }
+
+ setLoading(true);
+ try {
+ const options = [];
+
+ options.push({
+ key: "WechatPayEnabled",
+ value: inputs.WechatPayEnabled ? "true" : "false",
+ });
+ if (inputs.WechatPayAppId !== "") {
+ options.push({ key: "WechatPayAppId", value: inputs.WechatPayAppId });
+ }
+ if (inputs.WechatPayMchId !== "") {
+ options.push({ key: "WechatPayMchId", value: inputs.WechatPayMchId });
+ }
+ if (inputs.WechatPayApiV3Key !== "") {
+ options.push({
+ key: "WechatPayApiV3Key",
+ value: inputs.WechatPayApiV3Key,
+ });
+ }
+ if (inputs.WechatPayPrivateKey !== "") {
+ options.push({
+ key: "WechatPayPrivateKey",
+ value: inputs.WechatPayPrivateKey,
+ });
+ }
+ if (inputs.WechatPayCertSerial !== "") {
+ options.push({
+ key: "WechatPayCertSerial",
+ value: inputs.WechatPayCertSerial,
+ });
+ }
+ if (inputs.WechatPayNotifyUrl !== "") {
+ options.push({
+ key: "WechatPayNotifyUrl",
+ value: inputs.WechatPayNotifyUrl,
+ });
+ }
+ if (inputs.WechatPayReturnUrl !== "") {
+ options.push({
+ key: "WechatPayReturnUrl",
+ value: inputs.WechatPayReturnUrl,
+ });
+ }
+ options.push({
+ key: "WechatPayUnitPrice",
+ value: inputs.WechatPayUnitPrice.toString(),
+ });
+ options.push({
+ key: "WechatPayMinTopUp",
+ value: inputs.WechatPayMinTopUp.toString(),
+ });
+ options.push({
+ key: "WechatPaySandbox",
+ value: inputs.WechatPaySandbox ? "true" : "false",
+ });
+ if (inputs.WechatPaySandboxMchId !== "") {
+ options.push({
+ key: "WechatPaySandboxMchId",
+ value: inputs.WechatPaySandboxMchId,
+ });
+ }
+ if (inputs.WechatPaySandboxApiV3Key !== "") {
+ options.push({
+ key: "WechatPaySandboxApiV3Key",
+ value: inputs.WechatPaySandboxApiV3Key,
+ });
+ }
+ if (inputs.WechatPaySandboxPrivateKey !== "") {
+ options.push({
+ key: "WechatPaySandboxPrivateKey",
+ value: inputs.WechatPaySandboxPrivateKey,
+ });
+ }
+
+ const requestQueue = options.map((opt) =>
+ API.put("/api/option/", {
+ key: opt.key,
+ value: opt.value,
+ })
+ );
+
+ const results = await Promise.all(requestQueue);
+ const errorResults = results.filter((res) => !res.data.success);
+ if (errorResults.length > 0) {
+ errorResults.forEach((res) => {
+ showError(res.data.message);
+ });
+ } else {
+ showSuccess(t("更新成功"));
+ setOriginInputs({ ...inputs });
+ props.refresh?.();
+ }
+ } catch (error) {
+ showError(t("更新失败"));
+ }
+ setLoading(false);
+ };
+
+ return (
+
+
+
+ {t(
+ "微信支付集成说明:请先在微信支付商户平台申请接入,获取以下配置信息。"
+ )}
+
+
+ {t("微信支付商户平台")}
+
+ {" | "}
+
+ {t("API v3 文档")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}