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:
parent
b63f75ade4
commit
6fc114ec32
|
|
@ -35,3 +35,12 @@ redis_data/
|
||||||
.gomodcache/
|
.gomodcache/
|
||||||
.gocache-temp
|
.gocache-temp
|
||||||
.gopath
|
.gopath
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
# Test artifacts
|
||||||
|
.playwright-cli/
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,515 @@
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/QuantumNous/new-api/common"
|
||||||
|
"github.com/QuantumNous/new-api/logger"
|
||||||
|
"github.com/QuantumNous/new-api/model"
|
||||||
|
"github.com/QuantumNous/new-api/service"
|
||||||
|
"github.com/QuantumNous/new-api/setting"
|
||||||
|
"github.com/QuantumNous/new-api/setting/operation_setting"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PaymentMethodWechatPay = "wxpay"
|
||||||
|
)
|
||||||
|
|
||||||
|
var wechatPayAdaptor = &WechatPayAdaptor{}
|
||||||
|
|
||||||
|
// WechatPayRequest represents a payment request for WeChat Pay.
|
||||||
|
type WechatPayRequest struct {
|
||||||
|
Amount int64 `json:"amount"`
|
||||||
|
PaymentMethod string `json:"payment_method"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WechatPayAdaptor struct{}
|
||||||
|
|
||||||
|
func getWechatPayAppId() string {
|
||||||
|
return setting.WechatPayAppId
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWechatPayMchId() string {
|
||||||
|
if setting.WechatPaySandbox && setting.WechatPaySandboxMchId != "" {
|
||||||
|
return setting.WechatPaySandboxMchId
|
||||||
|
}
|
||||||
|
return setting.WechatPayMchId
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWechatPayApiV3Key() string {
|
||||||
|
if setting.WechatPaySandbox && setting.WechatPaySandboxApiV3Key != "" {
|
||||||
|
return setting.WechatPaySandboxApiV3Key
|
||||||
|
}
|
||||||
|
return setting.WechatPayApiV3Key
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWechatPayPrivateKey() string {
|
||||||
|
if setting.WechatPaySandbox && setting.WechatPaySandboxPrivateKey != "" {
|
||||||
|
return setting.WechatPaySandboxPrivateKey
|
||||||
|
}
|
||||||
|
return setting.WechatPayPrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWechatPayAPIBaseURL() string {
|
||||||
|
if setting.WechatPaySandbox {
|
||||||
|
return "https://apihk.mch.weixin.qq.com/sandboxnew"
|
||||||
|
}
|
||||||
|
return "https://api.mch.weixin.qq.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWechatPayMinTopUp() int64 {
|
||||||
|
minTopup := setting.WechatPayMinTopUp
|
||||||
|
if operation_setting.GetQuotaDisplayType() == operation_setting.QuotaDisplayTypeTokens {
|
||||||
|
minTopup = minTopup * int(common.QuotaPerUnit)
|
||||||
|
}
|
||||||
|
return int64(minTopup)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWechatPayPayMoney(amount float64, group string) float64 {
|
||||||
|
originalAmount := amount
|
||||||
|
if operation_setting.GetQuotaDisplayType() == operation_setting.QuotaDisplayTypeTokens {
|
||||||
|
amount = amount / common.QuotaPerUnit
|
||||||
|
}
|
||||||
|
topupGroupRatio := common.GetTopupGroupRatio(group)
|
||||||
|
if topupGroupRatio == 0 {
|
||||||
|
topupGroupRatio = 1
|
||||||
|
}
|
||||||
|
discount := 1.0
|
||||||
|
if ds, ok := operation_setting.GetPaymentSetting().AmountDiscount[int(originalAmount)]; ok {
|
||||||
|
if ds > 0 {
|
||||||
|
discount = ds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return amount * setting.WechatPayUnitPrice * topupGroupRatio * discount
|
||||||
|
}
|
||||||
|
|
||||||
|
// wechatPaySign generates the WeChat Pay v3 signature
|
||||||
|
func wechatPaySign(method string, urlPath string, body string, timestamp string, nonce string, privateKeyStr string) (string, error) {
|
||||||
|
message := fmt.Sprintf("%s\n%s\n%s\n%s\n", method, urlPath, timestamp, nonce)
|
||||||
|
if body != "" && body != "{}" {
|
||||||
|
message = fmt.Sprintf("%s\n%s\n%s\n%s\n", method, urlPath, timestamp, nonce) + body
|
||||||
|
} else {
|
||||||
|
message = fmt.Sprintf("%s\n%s\n%s\n%s\n", method, urlPath, timestamp, nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
block, _ := pem.Decode([]byte(privateKeyStr))
|
||||||
|
if block == nil {
|
||||||
|
return "", fmt.Errorf("failed to decode private key PEM")
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
priv, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse private key: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rsaKey, ok := priv.(*rsa.PrivateKey)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("private key is not RSA key")
|
||||||
|
}
|
||||||
|
|
||||||
|
hashed := sha256.Sum256([]byte(message))
|
||||||
|
signature, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.SHA256, hashed[:])
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("sign failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString(signature), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// wechatPayBuildAuthHeader builds the WeChat Pay v3 Authorization header
|
||||||
|
func wechatPayBuildAuthHeader(method string, urlPath string, body string) (string, error) {
|
||||||
|
mchId := getWechatPayMchId()
|
||||||
|
privateKey := getWechatPayPrivateKey()
|
||||||
|
certSerial := setting.WechatPayCertSerial
|
||||||
|
|
||||||
|
if mchId == "" || privateKey == "" || certSerial == "" {
|
||||||
|
return "", fmt.Errorf("WeChat Pay not fully configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||||
|
nonce := common.GetRandomString(32)
|
||||||
|
|
||||||
|
sign, err := wechatPaySign(method, urlPath, body, timestamp, nonce, privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
auth := fmt.Sprintf(`WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%s",serial_no="%s",signature="%s"`,
|
||||||
|
mchId, nonce, timestamp, certSerial, sign)
|
||||||
|
|
||||||
|
return auth, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// wechatPayPost sends a POST request to WeChat Pay API v3
|
||||||
|
func wechatPayPost(path string, requestBody interface{}) (map[string]interface{}, error) {
|
||||||
|
jsonBytes, err := common.Marshal(requestBody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("marshal request body failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyStr := string(jsonBytes)
|
||||||
|
authHeader, err := wechatPayBuildAuthHeader("POST", path, bodyStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
apiBaseURL := getWechatPayAPIBaseURL()
|
||||||
|
req, err := http.NewRequest("POST", apiBaseURL+path, strings.NewReader(bodyStr))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("create request failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", authHeader)
|
||||||
|
req.Header.Set("Accept", "application/json")
|
||||||
|
req.Header.Set("User-Agent", "TokenFactory")
|
||||||
|
|
||||||
|
client := &http.Client{Timeout: 30 * time.Second}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("request failed: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read response failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 && resp.StatusCode != 201 && resp.StatusCode != 204 {
|
||||||
|
return nil, fmt.Errorf("WeChat Pay API error: HTTP %d, body: %s", resp.StatusCode, string(respBody))
|
||||||
|
}
|
||||||
|
|
||||||
|
var result map[string]interface{}
|
||||||
|
if len(respBody) > 0 {
|
||||||
|
if err := common.Unmarshal(respBody, &result); err != nil {
|
||||||
|
return nil, fmt.Errorf("parse response failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// wechatPayDecryptResource decrypts WeChat Pay notification resource
|
||||||
|
func wechatPayDecryptResource(ciphertext, associatedData, nonce, apiV3Key string) ([]byte, error) {
|
||||||
|
aesKey, err := base64.StdEncoding.DecodeString(apiV3Key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode api v3 key failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cipherData, err := base64.StdEncoding.DecodeString(ciphertext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode ciphertext failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nonceBytes := []byte(nonce)
|
||||||
|
adBytes := []byte(associatedData)
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(aesKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("create cipher failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
aesGCM, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("create GCM failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
plaintext, err := aesGCM.Open(nil, nonceBytes, cipherData, adBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decrypt failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return plaintext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *WechatPayAdaptor) RequestPay(c *gin.Context, req *WechatPayRequest) {
|
||||||
|
if req.PaymentMethod != PaymentMethodWechatPay {
|
||||||
|
c.JSON(200, gin.H{"message": "error", "data": "不支持的支付渠道"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
minTopup := getWechatPayMinTopUp()
|
||||||
|
if req.Amount < minTopup {
|
||||||
|
c.JSON(200, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", minTopup)})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.Amount > 100000 {
|
||||||
|
c.JSON(200, gin.H{"message": "error", "data": "充值数量不能大于 100000"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id := c.GetInt("id")
|
||||||
|
user, err := model.GetUserById(id, false)
|
||||||
|
if err != nil || user == nil {
|
||||||
|
c.JSON(200, gin.H{"message": "error", "data": "用户不存在"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
group, err := model.GetUserGroup(id, true)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(200, gin.H{"message": "error", "data": "获取用户分组失败"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
payMoney := getWechatPayPayMoney(float64(req.Amount), group)
|
||||||
|
if payMoney <= 0.01 {
|
||||||
|
c.JSON(200, gin.H{"message": "error", "data": "充值金额过低"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tradeNo := fmt.Sprintf("WX%d%s%d", id, common.GetRandomString(12), time.Now().Unix())
|
||||||
|
|
||||||
|
topUp := &model.TopUp{
|
||||||
|
UserId: id,
|
||||||
|
Amount: req.Amount,
|
||||||
|
Money: payMoney,
|
||||||
|
TradeNo: tradeNo,
|
||||||
|
PaymentMethod: PaymentMethodWechatPay,
|
||||||
|
CreateTime: time.Now().Unix(),
|
||||||
|
Status: common.TopUpStatusPending,
|
||||||
|
}
|
||||||
|
err = topUp.Insert()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("创建WeChat Pay订单失败: %v", err)
|
||||||
|
c.JSON(200, gin.H{"message": "error", "data": "创建订单失败"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create WeChat Pay order via API v3
|
||||||
|
callBackAddress := service.GetCallbackAddress()
|
||||||
|
notifyUrl := strings.TrimRight(callBackAddress, "/") + "/api/wechatpay/notify"
|
||||||
|
if setting.WechatPayNotifyUrl != "" {
|
||||||
|
notifyUrl = setting.WechatPayNotifyUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
amountFen := int64(payMoney * 100) // Convert yuan to fen
|
||||||
|
|
||||||
|
log.Printf("WeChat Pay创建订单 - 用户: %d, 订单: %s, 金额: %.2f元 (%d分)", id, tradeNo, payMoney, amountFen)
|
||||||
|
|
||||||
|
payParams := map[string]interface{}{
|
||||||
|
"mchid": getWechatPayMchId(),
|
||||||
|
"out_trade_no": tradeNo,
|
||||||
|
"appid": getWechatPayAppId(),
|
||||||
|
"description": "TokenFactory TopUp",
|
||||||
|
"notify_url": notifyUrl,
|
||||||
|
"amount": map[string]interface{}{
|
||||||
|
"total": amountFen,
|
||||||
|
"currency": "CNY",
|
||||||
|
},
|
||||||
|
"payer": map[string]interface{}{
|
||||||
|
"openid": "", // Will be obtained from WeChat OAuth
|
||||||
|
},
|
||||||
|
"scene_info": map[string]interface{}{
|
||||||
|
"payer_client_ip": c.ClientIP(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// For H5 payment
|
||||||
|
payParams["scene_info"].(map[string]interface{})["h5_info"] = map[string]interface{}{
|
||||||
|
"type": "Wap",
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := wechatPayPost("/v3/pay/transactions/h5", payParams)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("WeChat Pay创建订单失败: %v", err)
|
||||||
|
c.JSON(200, gin.H{"message": "error", "data": "拉起支付失败"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h5Url := ""
|
||||||
|
if result != nil {
|
||||||
|
if urlStr, ok := result["h5_url"].(string); ok {
|
||||||
|
h5Url = urlStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("WeChat Pay订单创建成功 - 用户ID: %d, 订单号: %s, 金额: %.2f", id, tradeNo, payMoney)
|
||||||
|
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"message": "success",
|
||||||
|
"data": gin.H{
|
||||||
|
"pay_url": h5Url,
|
||||||
|
"trade_no": tradeNo,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WechatPayNotify handles the WeChat Pay payment notification
|
||||||
|
func WechatPayNotify(c *gin.Context) {
|
||||||
|
bodyBytes, err := io.ReadAll(c.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("读取WeChat Pay通知失败: %v", err)
|
||||||
|
c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "read body failed"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Request.Body.Close()
|
||||||
|
|
||||||
|
log.Printf("收到WeChat Pay通知: %s", string(bodyBytes))
|
||||||
|
|
||||||
|
var notifyData struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
CreateTime string `json:"create_time"`
|
||||||
|
EventType string `json:"event_type"`
|
||||||
|
ResourceType string `json:"resource_type"`
|
||||||
|
Resource struct {
|
||||||
|
Algorithm string `json:"algorithm"`
|
||||||
|
Ciphertext string `json:"ciphertext"`
|
||||||
|
AssociatedData string `json:"associated_data"`
|
||||||
|
Nonce string `json:"nonce"`
|
||||||
|
OriginalType string `json:"original_type"`
|
||||||
|
} `json:"resource"`
|
||||||
|
Summary string `json:"summary"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := common.Unmarshal(bodyBytes, ¬ifyData); err != nil {
|
||||||
|
log.Printf("解析WeChat Pay通知失败: %v", err)
|
||||||
|
c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "parse failed"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiV3Key := getWechatPayApiV3Key()
|
||||||
|
if apiV3Key == "" {
|
||||||
|
log.Printf("WeChat Pay API v3 key not configured")
|
||||||
|
c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "not configured"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt the resource
|
||||||
|
plaintext, err := wechatPayDecryptResource(
|
||||||
|
notifyData.Resource.Ciphertext,
|
||||||
|
notifyData.Resource.AssociatedData,
|
||||||
|
notifyData.Resource.Nonce,
|
||||||
|
apiV3Key,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("WeChat Pay通知解密失败: %v", err)
|
||||||
|
c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "decrypt failed"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("WeChat Pay通知解密成功: %s", string(plaintext))
|
||||||
|
|
||||||
|
var paymentResult struct {
|
||||||
|
AppId string `json:"appid"`
|
||||||
|
MchId string `json:"mchid"`
|
||||||
|
OutTradeNo string `json:"out_trade_no"`
|
||||||
|
TransactionId string `json:"transaction_id"`
|
||||||
|
TradeType string `json:"trade_type"`
|
||||||
|
TradeState string `json:"trade_state"`
|
||||||
|
TradeStateDesc string `json:"trade_state_desc"`
|
||||||
|
BankType string `json:"bank_type"`
|
||||||
|
Attach string `json:"attach"`
|
||||||
|
SuccessTime string `json:"success_time"`
|
||||||
|
Payer struct {
|
||||||
|
OpenId string `json:"openid"`
|
||||||
|
} `json:"payer"`
|
||||||
|
Amount struct {
|
||||||
|
Total int `json:"total"`
|
||||||
|
PayerTotal int `json:"payer_total"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
PayerCurrency string `json:"payer_currency"`
|
||||||
|
} `json:"amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := common.Unmarshal(plaintext, &paymentResult); err != nil {
|
||||||
|
log.Printf("解析WeChat Pay支付结果失败: %v", err)
|
||||||
|
c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "parse result failed"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tradeNo := paymentResult.OutTradeNo
|
||||||
|
log.Printf("WeChat Pay支付结果: out_trade_no=%s, transaction_id=%s, trade_state=%s",
|
||||||
|
tradeNo, paymentResult.TransactionId, paymentResult.TradeState)
|
||||||
|
|
||||||
|
if paymentResult.TradeState != "SUCCESS" {
|
||||||
|
log.Printf("WeChat Pay支付未成功: %s, trade_state=%s", tradeNo, paymentResult.TradeState)
|
||||||
|
c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": "ok"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
LockOrder(tradeNo)
|
||||||
|
defer UnlockOrder(tradeNo)
|
||||||
|
|
||||||
|
topUp := model.GetTopUpByTradeNo(tradeNo)
|
||||||
|
if topUp == nil {
|
||||||
|
log.Printf("WeChat Pay通知未找到订单: %s", tradeNo)
|
||||||
|
c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "order not found"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if topUp.Status != common.TopUpStatusPending {
|
||||||
|
log.Printf("WeChat Pay订单状态错误: %s, 当前状态: %s", tradeNo, topUp.Status)
|
||||||
|
c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": "ok"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dAmount := decimal.NewFromInt(topUp.Amount)
|
||||||
|
dQuotaPerUnit := decimal.NewFromFloat(common.QuotaPerUnit)
|
||||||
|
quotaToAdd := int(dAmount.Mul(dQuotaPerUnit).IntPart())
|
||||||
|
|
||||||
|
if quotaToAdd <= 0 {
|
||||||
|
log.Printf("WeChat Pay无效的充值额度: %s", tradeNo)
|
||||||
|
c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "invalid quota"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
topUp.CompleteTime = common.GetTimestamp()
|
||||||
|
topUp.Status = common.TopUpStatusSuccess
|
||||||
|
if err := topUp.Update(); err != nil {
|
||||||
|
log.Printf("WeChat Pay更新订单失败: %v", err)
|
||||||
|
c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "update failed"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := model.IncreaseUserQuota(topUp.UserId, quotaToAdd, true); err != nil {
|
||||||
|
log.Printf("WeChat Pay更新用户额度失败: %v", err)
|
||||||
|
c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "quota update failed"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
model.RecordLog(topUp.UserId, model.LogTypeTopup,
|
||||||
|
fmt.Sprintf("使用微信支付充值成功,充值金额: %v,支付金额:%.2f",
|
||||||
|
logger.LogQuota(quotaToAdd), topUp.Money))
|
||||||
|
model.ApplyAffiliateTopupReward(topUp.UserId, quotaToAdd)
|
||||||
|
|
||||||
|
log.Printf("WeChat Pay充值成功 - 订单: %s, 额度: %d", tradeNo, quotaToAdd)
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": "ok"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestWechatPay handles the WeChat Pay payment request from frontend
|
||||||
|
func RequestWechatPay(c *gin.Context) {
|
||||||
|
if !setting.WechatPayEnabled {
|
||||||
|
c.JSON(200, gin.H{"message": "error", "data": "微信支付未启用"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req WechatPayRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(200, gin.H{"message": "error", "data": "参数错误"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wechatPayAdaptor.RequestPay(c, &req)
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
|
@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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 密钥,并下载商户证书序列号。",
|
||||||
|
"可选,留空则使用默认地址": "可选,留空则使用默认地址"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue