feat: add Alipay and WeChat Pay payment gateway support

- Add Alipay payment backend: RSA2 signing/verification, async notify, sync return
- Add WeChat Pay backend: APIv3 signing, AES-GCM decryption, H5 pay, async notify
- Add payment setting UI components for Alipay and WeChat Pay
- Register config keys (24 items) in OptionMap for both gateways
- Add API routes: /api/user/alipay/pay, /api/user/wechatpay/pay,
  /api/alipay/notify, /api/alipay/return, /api/wechatpay/notify
- Add enable_alipay_topup/enable_wechatpay_topup to /api/status endpoint
- Add i18n translations for all new UI strings (11 locales)
- Fix: data is not defined ReferenceError in topup/index.jsx useEffect
- Fix: unused imports in controller/topup_wechat.go
- Update .gitignore for build artifacts
This commit is contained in:
xiezhouwei 2026-06-08 09:01:33 +08:00
parent b63f75ade4
commit 6fc114ec32
25 changed files with 2924 additions and 88 deletions

9
.gitignore vendored
View File

@ -35,3 +35,12 @@ redis_data/
.gomodcache/ .gomodcache/
.gocache-temp .gocache-temp
.gopath .gopath
# Build artifacts
*.exe
# Test artifacts
.playwright-cli/
# Misc

View File

@ -106,6 +106,8 @@ func GetStatus(c *gin.Context) {
"usd_exchange_rate": operation_setting.USDExchangeRate, "usd_exchange_rate": operation_setting.USDExchangeRate,
"price": operation_setting.Price, "price": operation_setting.Price,
"stripe_unit_price": setting.StripeUnitPrice, "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, "api_info_enabled": cs.ApiInfoEnabled,

View File

@ -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{ data := gin.H{
"enable_online_topup": (operation_setting.OnlinePayProvider == "yipay" && "enable_online_topup": (operation_setting.OnlinePayProvider == "yipay" &&
(operation_setting.YipayRequestURL != "" || operation_setting.PayAddress != "") && (operation_setting.YipayRequestURL != "" || operation_setting.PayAddress != "") &&
@ -97,9 +137,13 @@ func GetTopUpInfo(c *gin.Context) {
operation_setting.PayAddress != "" && operation_setting.PayAddress != "" &&
operation_setting.EpayId != "" && operation_setting.EpayId != "" &&
operation_setting.EpayKey != ""), operation_setting.EpayKey != ""),
"enable_stripe_topup": setting.StripeApiSecret != "" && setting.StripeWebhookSecret != "" && setting.StripePriceId != "", "enable_stripe_topup": setting.StripeApiSecret != "" && setting.StripeWebhookSecret != "" && setting.StripePriceId != "",
"enable_creem_topup": setting.CreemApiKey != "" && setting.CreemProducts != "[]", "enable_creem_topup": setting.CreemApiKey != "" && setting.CreemProducts != "[]",
"enable_waffo_topup": enableWaffo, "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{} { "waffo_pay_methods": func() interface{} {
if enableWaffo { if enableWaffo {
return setting.GetWaffoPayMethods() return setting.GetWaffoPayMethods()

463
controller/topup_alipay.go Normal file
View File

@ -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)
}

515
controller/topup_wechat.go Normal file
View File

@ -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, &notifyData); 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)
}

View File

@ -1,4 +1,4 @@
package model package model
import ( import (
"strconv" "strconv"
@ -131,6 +131,31 @@ func InitOptionMap() {
common.OptionMap["WaffoUnitPrice"] = strconv.FormatFloat(setting.WaffoUnitPrice, 'f', -1, 64) common.OptionMap["WaffoUnitPrice"] = strconv.FormatFloat(setting.WaffoUnitPrice, 'f', -1, 64)
common.OptionMap["WaffoMinTopUp"] = strconv.Itoa(setting.WaffoMinTopUp) common.OptionMap["WaffoMinTopUp"] = strconv.Itoa(setting.WaffoMinTopUp)
common.OptionMap["WaffoPayMethods"] = setting.WaffoPayMethods2JsonString() 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["TopupGroupRatio"] = common.TopupGroupRatio2JSONString()
common.OptionMap["Chats"] = setting.Chats2JsonString() common.OptionMap["Chats"] = setting.Chats2JsonString()
common.OptionMap["AutoGroups"] = setting.AutoGroups2JsonString() common.OptionMap["AutoGroups"] = setting.AutoGroups2JsonString()
@ -501,6 +526,56 @@ func updateOptionMap(key string, value string) (err error) {
case "WaffoApiKey": case "WaffoApiKey":
setting.WaffoApiKey = value setting.WaffoApiKey = value
case "WaffoPrivateKey": 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 setting.WaffoPrivateKey = value
case "WaffoPublicCert": case "WaffoPublicCert":
setting.WaffoPublicCert = value setting.WaffoPublicCert = value

View File

@ -1,4 +1,4 @@
package router package router
import ( import (
"github.com/QuantumNous/new-api/controller" "github.com/QuantumNous/new-api/controller"
@ -104,6 +104,10 @@ func SetApiRouter(router *gin.Engine) {
apiRouter.POST("/creem/webhook", controller.CreemWebhook) apiRouter.POST("/creem/webhook", controller.CreemWebhook)
apiRouter.POST("/waffo/webhook", controller.WaffoWebhook) 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 // Universal secure verification routes
apiRouter.POST("/verify", middleware.UserAuth(), middleware.CriticalRateLimit(), controller.UniversalVerify) 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("/waffo/pay", middleware.CriticalRateLimit(), controller.RequestWaffoPay)
selfRoute.POST("/aff_transfer", controller.TransferAffQuota) selfRoute.POST("/aff_transfer", controller.TransferAffQuota)
selfRoute.GET("/aff_invitees", controller.GetAffInvitees) 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.PUT("/setting", controller.UpdateUserSetting)
selfRoute.POST("/supplier/application", controller.SubmitSupplierApplication) selfRoute.POST("/supplier/application", controller.SubmitSupplierApplication)
selfRoute.GET("/supplier/application/self", controller.GetMySupplierApplication) selfRoute.GET("/supplier/application/self", controller.GetMySupplierApplication)

16
setting/payment_alipay.go Normal file
View File

@ -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
)

19
setting/payment_wechat.go Normal file
View File

@ -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
)

View File

@ -1,4 +1,4 @@
/* /*
Copyright (C) 2025 QuantumNous Copyright (C) 2025 QuantumNous
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
@ -17,104 +17,132 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com For commercial licensing, please contact support@quantumnous.com
*/ */
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from "react";
import { Card, Spin } from '@douyinfe/semi-ui'; import { Card, Spin } from "@douyinfe/semi-ui";
import SettingsGeneralPayment from '../../pages/Setting/Payment/SettingsGeneralPayment'; import SettingsGeneralPayment from "../../pages/Setting/Payment/SettingsGeneralPayment";
import SettingsPaymentGateway from '../../pages/Setting/Payment/SettingsPaymentGateway'; import SettingsPaymentGateway from "../../pages/Setting/Payment/SettingsPaymentGateway";
import SettingsPaymentGatewayStripe from '../../pages/Setting/Payment/SettingsPaymentGatewayStripe'; import SettingsPaymentGatewayStripe from "../../pages/Setting/Payment/SettingsPaymentGatewayStripe";
import SettingsPaymentGatewayCreem from '../../pages/Setting/Payment/SettingsPaymentGatewayCreem'; import SettingsPaymentGatewayCreem from "../../pages/Setting/Payment/SettingsPaymentGatewayCreem";
import SettingsPaymentGatewayWaffo from '../../pages/Setting/Payment/SettingsPaymentGatewayWaffo'; import SettingsPaymentGatewayWaffo from "../../pages/Setting/Payment/SettingsPaymentGatewayWaffo";
import { API, showError, toBoolean } from '../../helpers'; import SettingsPaymentGatewayAlipay from "../../pages/Setting/Payment/SettingsPaymentGatewayAlipay";
import { useTranslation } from 'react-i18next'; import SettingsPaymentGatewayWechat from "../../pages/Setting/Payment/SettingsPaymentGatewayWechat";
import { API, showError, toBoolean } from "../../helpers";
import { useTranslation } from "react-i18next";
/** 系统设置中的支付 Tab拉取 option 并渲染各支付子表单。 */ /** 系统设置中的支付 Tab拉取 option 并渲染各支付子表单。 */
const PaymentSetting = () => { const PaymentSetting = () => {
const { t } = useTranslation(); const { t } = useTranslation();
let [inputs, setInputs] = useState({ let [inputs, setInputs] = useState({
ServerAddress: '', ServerAddress: "",
PayAddress: '', PayAddress: "",
OnlinePayProvider: 'yipay', OnlinePayProvider: "yipay",
EpayId: '', EpayId: "",
EpayKey: '', EpayKey: "",
YipayAppSecret: '', YipayAppSecret: "",
YipayMchNo: '', YipayMchNo: "",
YipayAppId: '', YipayAppId: "",
YipayNotifyUrl: '', YipayNotifyUrl: "",
YipayReturnUrl: '', YipayReturnUrl: "",
YipayChannelExtra: '', YipayChannelExtra: "",
YipayRequestURL: '', YipayRequestURL: "",
Price: 7.3, Price: 7.3,
MinTopUp: 1, MinTopUp: 1,
TopupGroupRatio: '', TopupGroupRatio: "",
CustomCallbackAddress: '', CustomCallbackAddress: "",
PayMethods: '', PayMethods: "",
AmountOptions: '', AmountOptions: "",
AmountDiscount: '', AmountDiscount: "",
StripeApiSecret: '', StripeApiSecret: "",
StripeWebhookSecret: '', StripeWebhookSecret: "",
StripePriceId: '', StripePriceId: "",
StripeUnitPrice: 8.0, StripeUnitPrice: 8.0,
StripeMinTopUp: 1, StripeMinTopUp: 1,
StripePromotionCodesEnabled: false, 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); let [loading, setLoading] = useState(false);
const getOptions = async () => { const getOptions = async () => {
const res = await API.get('/api/option/'); const res = await API.get("/api/option/");
const { success, message, data } = res.data; const { success, message, data } = res.data;
if (success) { if (success) {
let newInputs = {}; let newInputs = {};
data.forEach((item) => { data.forEach((item) => {
switch (item.key) { switch (item.key) {
case 'TopupGroupRatio': case "TopupGroupRatio":
try { try {
newInputs[item.key] = JSON.stringify( newInputs[item.key] = JSON.stringify(
JSON.parse(item.value), JSON.parse(item.value),
null, null,
2, 2
); );
} catch (error) { } catch (error) {
newInputs[item.key] = item.value; newInputs[item.key] = item.value;
} }
break; break;
case 'payment_setting.amount_options': case "payment_setting.amount_options":
try { try {
newInputs['AmountOptions'] = JSON.stringify( newInputs["AmountOptions"] = JSON.stringify(
JSON.parse(item.value), JSON.parse(item.value),
null, null,
2, 2
); );
} catch (error) { } catch (error) {
newInputs['AmountOptions'] = item.value; newInputs["AmountOptions"] = item.value;
} }
break; break;
case 'payment_setting.amount_discount': case "payment_setting.amount_discount":
try { try {
newInputs['AmountDiscount'] = JSON.stringify( newInputs["AmountDiscount"] = JSON.stringify(
JSON.parse(item.value), JSON.parse(item.value),
null, null,
2, 2
); );
} catch (error) { } catch (error) {
newInputs['AmountDiscount'] = item.value; newInputs["AmountDiscount"] = item.value;
} }
break; break;
case 'Price': case "Price":
case 'MinTopUp': case "MinTopUp":
case 'StripeUnitPrice': case "StripeUnitPrice":
case 'StripeMinTopUp': case "StripeMinTopUp":
newInputs[item.key] = parseFloat(item.value); newInputs[item.key] = parseFloat(item.value);
break; break;
case 'YipayAppSecret': case "YipayAppSecret":
newInputs[item.key] = newInputs[item.key] =
item.value != null && item.value !== undefined item.value != null && item.value !== undefined
? String(item.value) ? String(item.value)
: ''; : "";
break; break;
default: default:
if (item.key.endsWith('Enabled')) { if (item.key.endsWith("Enabled")) {
newInputs[item.key] = toBoolean(item.value); newInputs[item.key] = toBoolean(item.value);
} else { } else {
newInputs[item.key] = item.value; newInputs[item.key] = item.value;
@ -134,7 +162,7 @@ const PaymentSetting = () => {
setLoading(true); setLoading(true);
await getOptions(); await getOptions();
} catch (error) { } catch (error) {
showError(t('刷新失败')); showError(t("刷新失败"));
} finally { } finally {
setLoading(false); setLoading(false);
} }
@ -146,22 +174,28 @@ const PaymentSetting = () => {
return ( return (
<> <>
<Spin spinning={loading} size='large'> <Spin spinning={loading} size="large">
<Card style={{ marginTop: '10px' }}> <Card style={{ marginTop: "10px" }}>
<SettingsGeneralPayment options={inputs} refresh={onRefresh} /> <SettingsGeneralPayment options={inputs} refresh={onRefresh} />
</Card> </Card>
<Card style={{ marginTop: '10px' }}> <Card style={{ marginTop: "10px" }}>
<SettingsPaymentGateway options={inputs} refresh={onRefresh} /> <SettingsPaymentGateway options={inputs} refresh={onRefresh} />
</Card> </Card>
<Card style={{ marginTop: '10px' }}> <Card style={{ marginTop: "10px" }}>
<SettingsPaymentGatewayStripe options={inputs} refresh={onRefresh} /> <SettingsPaymentGatewayStripe options={inputs} refresh={onRefresh} />
</Card> </Card>
<Card style={{ marginTop: '10px' }}> <Card style={{ marginTop: "10px" }}>
<SettingsPaymentGatewayCreem options={inputs} refresh={onRefresh} /> <SettingsPaymentGatewayCreem options={inputs} refresh={onRefresh} />
</Card> </Card>
<Card style={{ marginTop: '10px' }}> <Card style={{ marginTop: "10px" }}>
<SettingsPaymentGatewayWaffo options={inputs} refresh={onRefresh} /> <SettingsPaymentGatewayWaffo options={inputs} refresh={onRefresh} />
</Card> </Card>
<Card style={{ marginTop: "10px" }}>
<SettingsPaymentGatewayAlipay options={inputs} refresh={onRefresh} />
</Card>
<Card style={{ marginTop: "10px" }}>
<SettingsPaymentGatewayWechat options={inputs} refresh={onRefresh} />
</Card>
</Spin> </Spin>
</> </>
); );

View File

@ -1,4 +1,4 @@

import React, { useEffect, useState, useContext } from 'react'; import React, { useEffect, useState, useContext } from 'react';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { import {
@ -58,6 +58,8 @@ const TopUp = () => {
// Waffo // Waffo
const [enableWaffoTopUp, setEnableWaffoTopUp] = useState(false); const [enableWaffoTopUp, setEnableWaffoTopUp] = useState(false);
const [enableAlipayTopUp, setEnableAlipayTopUp] = useState(false);
const [enableWechatPayTopUp, setEnableWechatPayTopUp] = useState(false);
const [waffoPayMethods, setWaffoPayMethods] = useState([]); const [waffoPayMethods, setWaffoPayMethods] = useState([]);
const [waffoMinTopUp, setWaffoMinTopUp] = useState(1); const [waffoMinTopUp, setWaffoMinTopUp] = useState(1);
@ -147,6 +149,10 @@ const TopUp = () => {
showError(t('管理员未开启Stripe充值')); showError(t('管理员未开启Stripe充值'));
return; 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 { } else {
if (!enableOnlineTopUp) { if (!enableOnlineTopUp) {
showError(t('管理员未开启在线充值!')); showError(t('管理员未开启在线充值!'));
@ -159,6 +165,10 @@ const TopUp = () => {
try { try {
if (payment === 'stripe') { if (payment === 'stripe') {
await getStripeAmount(); 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 { } else {
await getAmount(); await getAmount();
} }
@ -201,7 +211,19 @@ const TopUp = () => {
amount: parseInt(topUpCount), amount: parseInt(topUpCount),
payment_method: 'stripe', 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', { res = await API.post('/api/user/pay', {
amount: parseInt(topUpCount), amount: parseInt(topUpCount),
@ -215,7 +237,10 @@ const TopUp = () => {
if (payWay === 'stripe') { if (payWay === 'stripe') {
// Stripe // Stripe
window.open(data.pay_link, '_blank'); 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 params = data;
let url = res.data.url; let url = res.data.url;
@ -513,6 +538,10 @@ const TopUp = () => {
setEnableStripeTopUp(enableStripeTopUp); setEnableStripeTopUp(enableStripeTopUp);
setEnableCreemTopUp(enableCreemTopUp); setEnableCreemTopUp(enableCreemTopUp);
const enableWaffoTopUp = data.enable_waffo_topup || false; 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); setEnableWaffoTopUp(enableWaffoTopUp);
setWaffoPayMethods(data.waffo_pay_methods || []); setWaffoPayMethods(data.waffo_pay_methods || []);
setWaffoMinTopUp(data.waffo_min_topup || 1); setWaffoMinTopUp(data.waffo_min_topup || 1);
@ -607,7 +636,10 @@ const TopUp = () => {
statusState.status.recharge_display_currency || statusState.status.recharge_display_currency ||
'USD', 'USD',
); );
const enableAlipayTopUp = statusState.status.enable_alipay_topup || false;
setEnableAlipayTopUp(enableAlipayTopUp);
const enableWechatPayTopUp = statusState.status.enable_wechatpay_topup || false;
setEnableWechatPayTopUp(enableWechatPayTopUp);
setStatusLoading(false); setStatusLoading(false);
} }
}, [statusState?.status]); }, [statusState?.status]);
@ -1005,3 +1037,9 @@ const TopUp = () => {
}; };
export default TopUp; export default TopUp;

View File

@ -5587,6 +5587,92 @@
"默认 ap-guangzhou可改为 ap-beijing 等": "Default ap-guangzhou; can be changed to ap-beijing, etc.", "默认 ap-guangzhou可改为 ap-beijing 等": "Default ap-guangzhou; can be changed to ap-beijing, etc.",
"(共 {{total}} 个)": "({{total}} total)", "(共 {{total}} 个)": "({{total}} total)",
"(共 {{total}} 个,省略 {{omit}} 个)": "({{total}} total, {{omit}} omitted)", "(共 {{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"
} }
} }

View File

@ -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.", "默认 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}} 个)": "(Total {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(Total des éléments {{total}}, éléments {{omit}} omis)", "(共 {{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"
} }
} }

View File

@ -5497,6 +5497,93 @@
"默认 ap-guangzhou可改为 ap-beijing 等": "Standarnya adalah ap-guangzhou, yang dapat diubah menjadi ap-beijing dll.", "默认 ap-guangzhou可改为 ap-beijing 等": "Standarnya adalah ap-guangzhou, yang dapat diubah menjadi ap-beijing dll.",
"(共 {{total}} 个)": "(Jumlah {{total}})", "(共 {{total}} 个)": "(Jumlah {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(Total item {{total}}, item {{omit}} dihilangkan)", "(共 {{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"
} }
} }

View File

@ -5497,6 +5497,93 @@
"默认 ap-guangzhou可改为 ap-beijing 等": "デフォルトは ap-guangzhou ですが、ap-beijing などに変更できます。", "默认 ap-guangzhou可改为 ap-beijing 等": "デフォルトは ap-guangzhou ですが、ap-beijing などに変更できます。",
"(共 {{total}} 个)": "(合計 {{total}})", "(共 {{total}} 个)": "(合計 {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(合計{{total}}項目、{{omit}}項目は省略)", "(共 {{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"
} }
} }

View File

@ -5497,6 +5497,93 @@
"默认 ap-guangzhou可改为 ap-beijing 等": "Lalai ialah ap-guangzhou, yang boleh ditukar kepada ap-beijing dsb.", "默认 ap-guangzhou可改为 ap-beijing 等": "Lalai ialah ap-guangzhou, yang boleh ditukar kepada ap-beijing dsb.",
"(共 {{total}} 个)": "(Jumlah {{total}})", "(共 {{total}} 个)": "(Jumlah {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(Jumlah {{total}} item, ditinggalkan {{omit}} item)", "(共 {{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"
} }
} }

View File

@ -5497,6 +5497,93 @@
"默认 ap-guangzhou可改为 ap-beijing 等": "По умолчанию используется ap-guangzhou, который можно изменить на ap-beijing и т. д.", "默认 ap-guangzhou可改为 ap-beijing 等": "По умолчанию используется ap-guangzhou, который можно изменить на ap-beijing и т. д.",
"(共 {{total}} 个)": "(Всего {{total}})", "(共 {{total}} 个)": "(Всего {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(Всего элементов {{total}}, пропущены элементы {{omit}})", "(共 {{total}} 个,省略 {{omit}} 个)": "(Всего элементов {{total}}, пропущены элементы {{omit}})",
"(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(В настоящее время поддерживается конфигурация Yipay/Yipay и по умолчанию используется указанный выше адрес сервера в качестве адреса обратного вызова!)" "(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)": "(В настоящее время поддерживается конфигурация Yipay/Yipay и по умолчанию используется указанный выше адрес сервера в качестве адреса обратного вызова!)",
"热度配置": "Конфигурация тепла",
"模型热度排行配置": "Конфигурация рейтинга тепла моделей",
"热度分 = (手动基数 + 实际调用) x 权重": "Оценка тепла = (Ручная база + Факт. вызовы) x Вес",
"预估热度分": "Предполагаемая оценка тепла",
"统计周期": "Период статистики",
"统计周期设置": "Настройка периода статистики",
"统计周期已更新,重新加载数据中...": "Период обновлён, перезагрузка данных...",
"权重配置": "Настройка веса",
"手动调整": "Ручная настройка",
"实际调用": "Фактические вызовы",
"全部历史": "Вся история",
"近 7 天": "Последние 7 дней",
"近 30 天": "Последние 30 дней",
"模型 / 渠道": "Модель / Канал",
"搜索渠道": "Поиск канала",
"不修改": "Не изменять",
"批量保存成功": "Массовое сохранение успешно",
"批量保存失败": "Ошибка массового сохранения",
"序号": "No.",
"热门": "Популярное",
"操作记录": "Записи операций",
"系统操作日志审计": "Аудит журнала операций системы",
"操作详情": "Детали операции",
"操作描述": "Описание операции",
"操作者": "Оператор",
"资源ID": "ID ресурса",
"资源类型": "Тип ресурса",
"资源名称": "Имя ресурса",
"IP地址": "IP-адрес",
"兑换码": "Код обмена",
"角色菜单权限配置": "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>",
"重置当前角色": "Сбросить текущую роль",
"已重置角色配置": "Конфигурация роли была сброшена",
"成本价": "Себестоимость",
"供应商申请": "Заявка поставщика",
"供应商-申请": "Поставщик - Заявка",
"供应商-渠道管理": "Поставщик - Управление каналами",
"供应商渠道管理": "Управление каналами поставщика",
"供应商-定价设置": "Поставщик - Настройка цен",
"供应商定价设置": "Настройка цен поставщика",
"供应商-数据看板": "Поставщик - Панель управления",
"供应商数据看板": "Панель управления поставщика",
"支付宝 (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"
} }
} }

View File

@ -5497,6 +5497,93 @@
"默认 ap-guangzhou可改为 ap-beijing 等": "Chaguo-msingi ni ap-guangzhou, ambayo inaweza kubadilishwa kuwa ap-beijing n.k.", "默认 ap-guangzhou可改为 ap-beijing 等": "Chaguo-msingi ni ap-guangzhou, ambayo inaweza kubadilishwa kuwa ap-beijing n.k.",
"(共 {{total}} 个)": "(Jumla {{total}})", "(共 {{total}} 个)": "(Jumla {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(Jumla ya vipengee {{total}}, vilivyoachwa {{omit}})", "(共 {{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"
} }
} }

View File

@ -5497,6 +5497,93 @@
"默认 ap-guangzhou可改为 ap-beijing 等": "ค่าเริ่มต้นคือ ap-guangzhou ซึ่งสามารถเปลี่ยนเป็น ap-beijing เป็นต้น", "默认 ap-guangzhou可改为 ap-beijing 等": "ค่าเริ่มต้นคือ ap-guangzhou ซึ่งสามารถเปลี่ยนเป็น ap-beijing เป็นต้น",
"(共 {{total}} 个)": "(รวม {{total}})", "(共 {{total}} 个)": "(รวม {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(ทั้งหมด {{total}} รายการ ละเว้น {{omit}} รายการ)", "(共 {{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"
} }
} }

View File

@ -5497,6 +5497,93 @@
"默认 ap-guangzhou可改为 ap-beijing 等": "Mặc định là ap-guangzhou, có thể thay đổi thành ap-beijing, v.v.", "默认 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}} 个)": "(Tổng cộng {{total}})",
"(共 {{total}} 个,省略 {{omit}} 个)": "(Tổng số mục {{total}}, đã bỏ qua mục {{omit}})", "(共 {{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"
} }
} }

View File

@ -5445,6 +5445,93 @@
"(共 {{total}} 个)": "(共 {{total}} 个)", "(共 {{total}} 个)": "(共 {{total}} 个)",
"(共 {{total}} 个,省略 {{omit}} 个)": "(共 {{total}} 个,省略 {{omit}} 个)", "(共 {{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 密钥,并下载商户证书序列号。",
"可选,留空则使用默认地址": "可选,留空则使用默认地址"
} }
} }

View File

@ -5496,6 +5496,93 @@
"默认 ap-guangzhou可改为 ap-beijing 等": "預設 ap-guangzhou可改為 ap-beijing 等", "默认 ap-guangzhou可改为 ap-beijing 等": "預設 ap-guangzhou可改為 ap-beijing 等",
"(共 {{total}} 个)": "(共 {{total}} 個)", "(共 {{total}} 个)": "(共 {{total}} 個)",
"(共 {{total}} 个,省略 {{omit}} 个)": "(共 {{total}} 個,省略 {{omit}} 個)", "(共 {{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"
} }
} }

View File

@ -336,9 +336,9 @@ export default function SettingsSidebarModulesAdmin(props) {
value={selectedRole} value={selectedRole}
onChange={(e) => setSelectedRole(e.target.value)} onChange={(e) => setSelectedRole(e.target.value)}
> >
<Radio value={String(USER_ROLES.USER)}>{USER_ROLE_NAMES[USER_ROLES.USER]}</Radio> <Radio value={String(USER_ROLES.USER)}>{t(USER_ROLE_NAMES[USER_ROLES.USER])}</Radio>
<Radio value={String(USER_ROLES.ADMIN)}>{USER_ROLE_NAMES[USER_ROLES.ADMIN]}</Radio> <Radio value={String(USER_ROLES.ADMIN)}>{t(USER_ROLE_NAMES[USER_ROLES.ADMIN])}</Radio>
<Radio value={String(USER_ROLES.ROOT)}>{USER_ROLE_NAMES[USER_ROLES.ROOT]}</Radio> <Radio value={String(USER_ROLES.ROOT)}>{t(USER_ROLE_NAMES[USER_ROLES.ROOT])}</Radio>
</RadioGroup> </RadioGroup>
</div> </div>

View File

@ -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 (
<Spin spinning={loading}>
<Form
initValues={inputs}
onValueChange={handleFormChange}
getFormApi={(api) => (formApiRef.current = api)}
>
<Form.Section text={t("支付宝 (Alipay) 设置")}>
<Text>
{t(
"支付宝支付集成说明:请先在支付宝开放平台创建应用并获取以下配置信息。"
)}
<br />
<a
href="https://open.alipay.com/"
target="_blank"
rel="noreferrer"
>
{t("支付宝开放平台")}
</a>
{" | "}
<a
href="https://open.alipay.com/develop/sandbox"
target="_blank"
rel="noreferrer"
>
{t("沙箱环境")}
</a>
</Text>
<Banner
type="info"
description={`${t("异步通知地址 (Notify URL)")}${props.options.ServerAddress ? removeTrailingSlash(props.options.ServerAddress) : t("网站地址")}/api/alipay/notify`}
/>
<Banner
type="warning"
description={`${t("请确保已在支付宝开放平台配置 RSA2 密钥,并设置签约产品为「电脑网站支付」。")}`}
/>
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Switch
field="AlipayEnabled"
size="default"
checkedText="|"
uncheckedText="O"
label={t("启用支付宝支付")}
/>
</Col>
</Row>
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="AlipayAppId"
label={t("支付宝 App ID")}
placeholder={t("支付宝开放平台应用的 APPID")}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="AlipayPrivateKey"
label={t("商户私钥")}
placeholder={t("应用私钥PKCS8 格式的 RSA2 私钥")}
type="password"
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="AlipayPublicKey"
label={t("支付宝公钥")}
placeholder={t("支付宝开放平台获取的支付宝公钥")}
type="password"
/>
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 16 }}
>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="AlipayNotifyUrl"
label={t("自定义异步通知地址")}
placeholder={t("可选,留空则使用默认地址")}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="AlipayReturnUrl"
label={t("自定义同步跳转地址")}
placeholder={t("可选,留空则使用默认地址")}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.InputNumber
field="AlipayUnitPrice"
precision={2}
label={t("单价(元/额度)")}
placeholder={t("例如1就是1元/额度")}
/>
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 16 }}
>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.InputNumber
field="AlipayMinTopUp"
label={t("最低充值额度")}
placeholder={t("例如1")}
/>
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 16 }}
>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Switch
field="AlipaySandbox"
size="default"
checkedText="|"
uncheckedText="O"
label={t("沙箱模式")}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="AlipaySandboxAppId"
label={t("沙箱 App ID")}
placeholder={t("沙箱环境的 APPID")}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="AlipaySandboxPrivateKey"
label={t("沙箱商户私钥")}
placeholder={t("沙箱环境的应用私钥")}
type="password"
/>
</Col>
</Row>
<Button onClick={submitAlipaySetting} style={{ marginTop: 16 }}>
{t("更新支付宝设置")}
</Button>
</Form.Section>
</Form>
</Spin>
);
}

View File

@ -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 (
<Spin spinning={loading}>
<Form
initValues={inputs}
onValueChange={handleFormChange}
getFormApi={(api) => (formApiRef.current = api)}
>
<Form.Section text={t("微信支付 (WeChat Pay) 设置")}>
<Text>
{t(
"微信支付集成说明:请先在微信支付商户平台申请接入,获取以下配置信息。"
)}
<br />
<a
href="https://pay.weixin.qq.com/"
target="_blank"
rel="noreferrer"
>
{t("微信支付商户平台")}
</a>
{" | "}
<a
href="https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/api.shtml"
target="_blank"
rel="noreferrer"
>
{t("API v3 文档")}
</a>
</Text>
<Banner
type="info"
description={`${t("异步通知地址 (Notify URL)")}${props.options.ServerAddress ? removeTrailingSlash(props.options.ServerAddress) : t("网站地址")}/api/wechatpay/notify`}
/>
<Banner
type="warning"
description={`${t("请确保已配置 APIv3 密钥,并下载商户证书序列号。")}`}
/>
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Switch
field="WechatPayEnabled"
size="default"
checkedText="|"
uncheckedText="O"
label={t("启用微信支付")}
/>
</Col>
</Row>
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="WechatPayAppId"
label={t("微信公众号/小程序 AppID")}
placeholder={t("微信开放平台的 AppID")}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="WechatPayMchId"
label={t("商户号 (MchID)")}
placeholder={t("微信支付商户号")}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="WechatPayApiV3Key"
label={t("APIv3 密钥")}
placeholder={t("APIv3 密钥32位字符")}
type="password"
/>
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 16 }}
>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="WechatPayPrivateKey"
label={t("商户私钥")}
placeholder={t("商户 API 证书的私钥 (PKCS8 PEM)")}
type="password"
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="WechatPayCertSerial"
label={t("证书序列号")}
placeholder={t("商户 API 证书序列号")}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="WechatPayNotifyUrl"
label={t("自定义异步通知地址")}
placeholder={t("可选,留空则使用默认地址")}
/>
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 16 }}
>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.InputNumber
field="WechatPayUnitPrice"
precision={2}
label={t("单价(元/额度)")}
placeholder={t("例如1就是1元/额度")}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.InputNumber
field="WechatPayMinTopUp"
label={t("最低充值额度")}
placeholder={t("例如1")}
/>
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 16 }}
>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Switch
field="WechatPaySandbox"
size="default"
checkedText="|"
uncheckedText="O"
label={t("沙箱模式")}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="WechatPaySandboxMchId"
label={t("沙箱商户号")}
placeholder={t("沙箱环境的商户号")}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="WechatPaySandboxApiV3Key"
label={t("沙箱 APIv3 密钥")}
placeholder={t("沙箱环境 APIv3 密钥")}
type="password"
/>
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 16 }}
>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field="WechatPaySandboxPrivateKey"
label={t("沙箱商户私钥")}
placeholder={t("沙箱环境商户私钥")}
type="password"
/>
</Col>
</Row>
<Button onClick={submitWechatPaySetting} style={{ marginTop: 16 }}>
{t("更新微信支付设置")}
</Button>
</Form.Section>
</Form>
</Spin>
);
}