fix(payment): resolve payment settings save logic and enable persistence

- Fix Bug 1: Always save all provider-specific fields regardless of
  currently selected provider in dropdown, preventing data loss when
  switching between Alipay/WeChat/ePay/Yipay configurations
- Fix Bug 2: Add EpayEnabled/YipayEnabled backend Go variables and
  OptionMap entries so enable/disable checkboxes persist across page
  reloads
- Fix white screen crash by wrapping orphan Col elements in proper Row
- Add EpayEnabled/YipayEnabled to PaymentSetting.jsx initial state
- Remove duplicate payment card imports for Stripe/Creem/Waffo/Alipay/Wechat
This commit is contained in:
xiezhouwei 2026-06-09 14:35:37 +08:00
parent 6fc114ec32
commit 82016579db
8 changed files with 277 additions and 286 deletions

1
$null Normal file
View File

@ -0,0 +1 @@
ERROR: The process "main.exe" not found.

View File

@ -102,6 +102,8 @@ func InitOptionMap() {
common.OptionMap["YipayReturnUrl"] = operation_setting.YipayReturnUrl
common.OptionMap["YipayRequestURL"] = operation_setting.YipayRequestURL
common.OptionMap["YipayChannelExtra"] = operation_setting.YipayChannelExtra
common.OptionMap["EpayEnabled"] = strconv.FormatBool(operation_setting.EpayEnabled)
common.OptionMap["YipayEnabled"] = strconv.FormatBool(operation_setting.YipayEnabled)
common.OptionMap["Price"] = strconv.FormatFloat(operation_setting.Price, 'f', -1, 64)
common.OptionMap["USDExchangeRate"] = strconv.FormatFloat(operation_setting.USDExchangeRate, 'f', -1, 64)
common.OptionMap["MinTopUp"] = strconv.Itoa(operation_setting.MinTopUp)
@ -495,6 +497,10 @@ func updateOptionMap(key string, value string) (err error) {
case "YipayChannelExtra":
operation_setting.YipayChannelExtra = strings.TrimSpace(value)
common.OptionMap[key] = operation_setting.YipayChannelExtra
case "EpayEnabled":
operation_setting.EpayEnabled = value == "true"
case "YipayEnabled":
operation_setting.YipayEnabled = value == "true"
case "Price":
operation_setting.Price, _ = strconv.ParseFloat(value, 64)
case "USDExchangeRate":

View File

@ -8,6 +8,8 @@ var CustomCallbackAddress = ""
var EpayId = ""
var EpayKey = ""
var YipayAppSecret = ""
var EpayEnabled = false
var YipayEnabled = false
var OnlinePayProvider = "yipay"
var Price = 7.3
var MinTopUp = 1

View File

@ -21,11 +21,7 @@ import React, { useEffect, useState } from "react";
import { Card, Spin } from "@douyinfe/semi-ui";
import SettingsGeneralPayment from "../../pages/Setting/Payment/SettingsGeneralPayment";
import SettingsPaymentGateway from "../../pages/Setting/Payment/SettingsPaymentGateway";
import SettingsPaymentGatewayStripe from "../../pages/Setting/Payment/SettingsPaymentGatewayStripe";
import SettingsPaymentGatewayCreem from "../../pages/Setting/Payment/SettingsPaymentGatewayCreem";
import SettingsPaymentGatewayWaffo from "../../pages/Setting/Payment/SettingsPaymentGatewayWaffo";
import SettingsPaymentGatewayAlipay from "../../pages/Setting/Payment/SettingsPaymentGatewayAlipay";
import SettingsPaymentGatewayWechat from "../../pages/Setting/Payment/SettingsPaymentGatewayWechat";
import { API, showError, toBoolean } from "../../helpers";
import { useTranslation } from "react-i18next";
@ -45,6 +41,8 @@ const PaymentSetting = () => {
YipayReturnUrl: "",
YipayChannelExtra: "",
YipayRequestURL: "",
EpayEnabled: false,
YipayEnabled: false,
Price: 7.3,
MinTopUp: 1,
TopupGroupRatio: "",
@ -53,13 +51,6 @@ const PaymentSetting = () => {
AmountOptions: "",
AmountDiscount: "",
StripeApiSecret: "",
StripeWebhookSecret: "",
StripePriceId: "",
StripeUnitPrice: 8.0,
StripeMinTopUp: 1,
StripePromotionCodesEnabled: false,
AlipayEnabled: false,
AlipayAppId: "",
AlipayPrivateKey: "",
@ -131,10 +122,6 @@ const PaymentSetting = () => {
break;
case "Price":
case "MinTopUp":
case "StripeUnitPrice":
case "StripeMinTopUp":
newInputs[item.key] = parseFloat(item.value);
break;
case "YipayAppSecret":
newInputs[item.key] =
item.value != null && item.value !== undefined
@ -181,21 +168,6 @@ const PaymentSetting = () => {
<Card style={{ marginTop: "10px" }}>
<SettingsPaymentGateway options={inputs} refresh={onRefresh} />
</Card>
<Card style={{ marginTop: "10px" }}>
<SettingsPaymentGatewayStripe options={inputs} refresh={onRefresh} />
</Card>
<Card style={{ marginTop: "10px" }}>
<SettingsPaymentGatewayCreem options={inputs} refresh={onRefresh} />
</Card>
<Card style={{ marginTop: "10px" }}>
<SettingsPaymentGatewayWaffo options={inputs} refresh={onRefresh} />
</Card>
<Card style={{ marginTop: "10px" }}>
<SettingsPaymentGatewayAlipay options={inputs} refresh={onRefresh} />
</Card>
<Card style={{ marginTop: "10px" }}>
<SettingsPaymentGatewayWechat options={inputs} refresh={onRefresh} />
</Card>
</Spin>
</>
);

View File

@ -70,6 +70,8 @@ const RechargeCard = ({
statusLoading,
topupInfo,
onOpenHistory,
enableAlipayTopUp,
enableWechatPayTopUp,
enableWaffoTopUp,
waffoTopUp,
waffoPayMethods,
@ -213,13 +215,15 @@ const RechargeCard = ({
) : enableOnlineTopUp ||
enableStripeTopUp ||
enableCreemTopUp ||
enableAlipayTopUp ||
enableWechatPayTopUp ||
enableWaffoTopUp ? (
<Form
getFormApi={(api) => (onlineFormApiRef.current = api)}
initValues={{ topUpCount: topUpCount }}
>
<div className='space-y-6'>
{(enableOnlineTopUp || enableStripeTopUp || enableWaffoTopUp) && (
{(enableOnlineTopUp || enableStripeTopUp || enableWaffoTopUp || enableAlipayTopUp || enableWechatPayTopUp) && (
<Row gutter={12}>
<Col xs={24} sm={24} md={24} lg={10} xl={10}>
<Form.InputNumber
@ -228,7 +232,9 @@ const RechargeCard = ({
disabled={
!enableOnlineTopUp &&
!enableStripeTopUp &&
!enableWaffoTopUp
!enableWaffoTopUp &&
!enableAlipayTopUp &&
!enableWechatPayTopUp
}
placeholder={
t('充值数量,最低 ') + renderTopUpCount(minTopUp)
@ -282,26 +288,30 @@ const RechargeCard = ({
/>
</Col>
{payMethods &&
payMethods.filter((m) => m.type !== 'waffo').length > 0 && (
payMethods.filter((m) => m.type !== 'waffo' && m.type !== 'custom1').length > 0 && (
<Col xs={24} sm={24} md={24} lg={14} xl={14}>
<Form.Slot label={t('选择支付方式')}>
<Space wrap>
{payMethods
.filter((m) => m.type !== 'waffo')
.filter((m) => m.type !== 'waffo' && m.type !== 'custom1')
.map((payMethod) => {
const minTopupVal =
Number(payMethod.min_topup) || 0;
const isStripe = payMethod.type === 'stripe';
const isAlipay = payMethod.type === 'alipay';
const isWechat = payMethod.type === 'wxpay';
const disabled =
(!enableOnlineTopUp && !isStripe) ||
(!enableOnlineTopUp && !isStripe && !isAlipay && !isWechat) ||
(!enableStripeTopUp && isStripe) ||
(!enableAlipayTopUp && isAlipay) ||
(!enableWechatPayTopUp && isWechat) ||
minTopupVal > Number(topUpCount || 0);
const buttonEl = (
<Button
key={payMethod.type}
theme='outline'
type='tertiary'
theme={payWay === payMethod.type ? 'solid' : 'outline'}
type={payWay === payMethod.type ? 'primary' : 'tertiary'}
onClick={() => preTopUp(payMethod.type)}
disabled={disabled}
loading={
@ -310,11 +320,11 @@ const RechargeCard = ({
}
icon={
payMethod.type === 'alipay' ? (
<SiAlipay size={18} color='#1677FF' />
<SiAlipay size={18} color={payWay === payMethod.type ? 'white' : '#1677FF'} />
) : payMethod.type === 'wxpay' ? (
<SiWechat size={18} color='#07C160' />
<SiWechat size={18} color={payWay === payMethod.type ? 'white' : '#07C160'} />
) : payMethod.type === 'stripe' ? (
<SiStripe size={18} color='#635BFF' />
<SiStripe size={18} color={payWay === payMethod.type ? 'white' : '#635BFF'} />
) : (
<CreditCard
size={18}
@ -356,7 +366,7 @@ const RechargeCard = ({
</Row>
)}
{(enableOnlineTopUp || enableStripeTopUp || enableWaffoTopUp) && (
{(enableOnlineTopUp || enableStripeTopUp || enableWaffoTopUp || enableAlipayTopUp || enableWechatPayTopUp) && (
<Form.Slot
label={
<div className='flex items-center gap-2'>

View File

@ -527,22 +527,26 @@ const TopUp = () => {
const enableStripeTopUp = data.enable_stripe_topup || false;
const enableOnlineTopUp = data.enable_online_topup || false;
const enableCreemTopUp = data.enable_creem_topup || false;
const enableWaffoTopUp = data.enable_waffo_topup || false;
const enableAlipayTopUp = data.enable_alipay_topup || false;
const enableWechatPayTopUp = data.enable_wechatpay_topup || false;
const minTopUpValue = enableOnlineTopUp
? data.min_topup
: enableStripeTopUp
? data.stripe_min_topup
: data.enable_waffo_topup
: enableWaffoTopUp
? data.waffo_min_topup
: 1;
: enableAlipayTopUp
? data.alipay_min_topup
: enableWechatPayTopUp
? data.wechatpay_min_topup
: 1;
setEnableOnlineTopUp(enableOnlineTopUp);
setEnableStripeTopUp(enableStripeTopUp);
setEnableCreemTopUp(enableCreemTopUp);
const enableWaffoTopUp = data.enable_waffo_topup || false;
const enableAlipayTopUp = data.enable_alipay_topup || false;
setEnableAlipayTopUp(enableAlipayTopUp);
const enableWechatPayTopUp = data.enable_wechatpay_topup || false;
setEnableWechatPayTopUp(enableWechatPayTopUp);
setEnableWaffoTopUp(enableWaffoTopUp);
setEnableAlipayTopUp(enableAlipayTopUp);
setEnableWechatPayTopUp(enableWechatPayTopUp);
setWaffoPayMethods(data.waffo_pay_methods || []);
setWaffoMinTopUp(data.waffo_min_topup || 1);
setMinTopUp(minTopUpValue);
@ -810,9 +814,13 @@ const TopUp = () => {
}
const minTopupVal = Number(method.min_topup) || 0;
const isStripe = method.type === 'stripe';
const isAlipay = method.type === 'alipay';
const isWechat = method.type === 'wxpay';
const disabled =
(!enableOnlineTopUp && !isStripe) ||
(!enableOnlineTopUp && !isStripe && !isAlipay && !isWechat) ||
(!enableStripeTopUp && isStripe) ||
(!enableAlipayTopUp && isAlipay) ||
(!enableWechatPayTopUp && isWechat) ||
minTopupVal > Number(count || 0);
return !disabled;
});
@ -823,7 +831,7 @@ const TopUp = () => {
selectPresetAmount(preset);
const enabledMethods = getEnabledEpayMethods(preset.value);
if (enabledMethods.length === 0) {
showError(t('当前额度暂无可用支付方式'));
showError(t('暂无可用支付方式,请联系管理员配置'));
return;
}
if (enabledMethods.length === 1) {
@ -880,6 +888,8 @@ const TopUp = () => {
payMethods={payMethods}
enableOnlineTopUp={enableOnlineTopUp}
enableStripeTopUp={enableStripeTopUp}
enableAlipayTopUp={enableAlipayTopUp}
enableWechatPayTopUp={enableWechatPayTopUp}
onSelect={handlePaymentMethodSelect}
paymentLoading={paymentLoading}
payWay={payWay}
@ -994,6 +1004,8 @@ const TopUp = () => {
enableWaffoTopUp={enableWaffoTopUp}
waffoTopUp={waffoTopUp}
waffoPayMethods={waffoPayMethods}
enableAlipayTopUp={enableAlipayTopUp}
enableWechatPayTopUp={enableWechatPayTopUp}
presetAmounts={presetAmounts}
selectedPreset={selectedPreset}
selectPresetAmount={selectPresetAmount}

View File

@ -14,11 +14,13 @@ const PaymentMethodSelectModal = ({
payMethods = [],
enableOnlineTopUp,
enableStripeTopUp,
enableAlipayTopUp,
enableWechatPayTopUp,
onSelect,
paymentLoading,
payWay,
}) => {
const epayMethods = payMethods.filter((m) => m.type !== 'waffo');
const epayMethods = payMethods.filter((m) => m.type !== 'waffo' && m.type !== 'custom1');
const renderPayIcon = (payMethod) => {
if (payMethod.type === 'alipay') {
@ -61,9 +63,13 @@ const PaymentMethodSelectModal = ({
{epayMethods.map((payMethod) => {
const minTopupVal = Number(payMethod.min_topup) || 0;
const isStripe = payMethod.type === 'stripe';
const isAlipay = payMethod.type === 'alipay';
const isWechat = payMethod.type === 'wxpay';
const disabled =
(!enableOnlineTopUp && !isStripe) ||
(!enableOnlineTopUp && !isStripe && !isAlipay && !isWechat) ||
(!enableStripeTopUp && isStripe) ||
(!enableAlipayTopUp && isAlipay) ||
(!enableWechatPayTopUp && isWechat) ||
minTopupVal > Number(topUpCount || 0);
const buttonEl = (

View File

@ -18,7 +18,7 @@ For commercial licensing, please contact support@quantumnous.com
*/
import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';
import { Button, Form, Row, Col, Typography, Spin, TextArea } from '@douyinfe/semi-ui';
import { Button, Form, Row, Col, Typography, Spin, TextArea, Checkbox } from '@douyinfe/semi-ui';
const { Text } = Typography;
import {
API,
@ -67,6 +67,12 @@ function getDefaultYipayReturnHint(serverAddress) {
*/
function normalizeOnlinePayProvider(provider) {
const value = (provider || '').toLowerCase();
if (value === 'alipay') {
return 'alipay';
}
if (value === 'wechatpay' || value === 'wxpay') {
return 'wechatpay';
}
if (value === 'yipay' || value === 'jeepay') {
return 'yipay';
}
@ -96,6 +102,16 @@ function buildFormModelFromOptions(opts) {
YipayReturnUrl: opts.YipayReturnUrl || '',
YipayChannelExtra: String(opts.YipayChannelExtra ?? ''),
YipayRequestURL: yipayReq,
AlipayEnabled: opts.AlipayEnabled === true || opts.AlipayEnabled === 'true',
AlipayAppId: opts.AlipayAppId || '',
AlipayPrivateKey: opts.AlipayPrivateKey || '',
AlipayPublicKey: opts.AlipayPublicKey || '',
WechatPayEnabled: opts.WechatPayEnabled === true || opts.WechatPayEnabled === 'true',
WechatPayAppId: opts.WechatPayAppId || '',
WechatPayMchId: opts.WechatPayMchId || '',
WechatPayApiV3Key: opts.WechatPayApiV3Key || '',
EpayEnabled: opts.EpayEnabled === true || opts.EpayEnabled === 'true',
YipayEnabled: opts.YipayEnabled === true || opts.YipayEnabled === 'true',
Price: opts.Price !== undefined ? parseFloat(String(opts.Price)) : 7.3,
MinTopUp:
opts.MinTopUp !== undefined ? parseFloat(String(opts.MinTopUp)) : 1,
@ -149,6 +165,16 @@ export default function SettingsPaymentGateway(props) {
YipayReturnUrl: '',
YipayChannelExtra: '',
YipayRequestURL: '',
AlipayEnabled: false,
AlipayAppId: '',
AlipayPrivateKey: '',
AlipayPublicKey: '',
WechatPayEnabled: false,
WechatPayAppId: '',
WechatPayMchId: '',
WechatPayApiV3Key: '',
EpayEnabled: false,
YipayEnabled: false,
Price: 7.3,
MinTopUp: 1,
TopupGroupRatio: '',
@ -289,6 +315,26 @@ export default function SettingsPaymentGateway(props) {
}
}
if (currentProvider === 'alipay') {
if (!inputs.AlipayAppId?.trim()) {
showError(t('支付宝模式下必须填写应用ID'));
return;
}
if (!inputs.AlipayPrivateKey?.trim()) {
showError(t('支付宝模式下必须填写私钥'));
return;
}
}
if (currentProvider === 'wechatpay') {
if (!inputs.WechatPayAppId?.trim()) {
showError(t('微信支付模式下必须填写应用ID'));
return;
}
if (!inputs.WechatPayMchId?.trim()) {
showError(t('微信支付模式下必须填写商户号'));
return;
}
}
if (currentProvider === 'yipay') {
if (!inputs.YipayMchNo?.trim()) {
showError(t('Yipay 模式下必须填写商户号'));
@ -328,50 +374,62 @@ export default function SettingsPaymentGateway(props) {
},
];
if (currentProvider === 'epay') {
options.push({
// Alipay options
options.push({ key: 'AlipayEnabled', value: inputs.AlipayEnabled ? 'true' : 'false' });
options.push({ key: 'AlipayAppId', value: inputs.AlipayAppId?.trim() || '' });
options.push({ key: 'AlipayPrivateKey', value: inputs.AlipayPrivateKey || '' });
options.push({ key: 'AlipayPublicKey', value: inputs.AlipayPublicKey?.trim() || '' });
// Epay/Yipay options
options.push({ key: 'EpayEnabled', value: inputs.EpayEnabled ? 'true' : 'false' });
options.push({ key: 'YipayEnabled', value: inputs.YipayEnabled ? 'true' : 'false' });
// WeChat Pay options
options.push({ key: 'WechatPayEnabled', value: inputs.WechatPayEnabled ? 'true' : 'false' });
options.push({ key: 'WechatPayAppId', value: inputs.WechatPayAppId?.trim() || '' });
options.push({ key: 'WechatPayMchId', value: inputs.WechatPayMchId?.trim() || '' });
options.push({ key: 'WechatPayApiV3Key', value: inputs.WechatPayApiV3Key || '' });
options.push({
key: 'PayAddress',
value: removeTrailingSlash(inputs.PayAddress || ''),
});
}
if (currentProvider === 'yipay') {
const gatewayBase = removeTrailingSlash(
(inputs.YipayRequestURL || '').trim(),
);
options.push({ key: 'YipayRequestURL', value: gatewayBase });
options.push({ key: 'PayAddress', value: gatewayBase });
}
if (currentProvider === 'epay' && inputs.EpayId !== '') {
if (inputs.EpayId !== '') {
options.push({ key: 'EpayId', value: inputs.EpayId });
}
if (currentProvider === 'yipay' && inputs.YipayMchNo !== '') {
if (inputs.YipayMchNo !== '') {
options.push({ key: 'YipayMchNo', value: inputs.YipayMchNo.trim() });
}
if (
currentProvider === 'epay' &&
inputs.EpayKey !== undefined &&
inputs.EpayKey !== ''
) {
options.push({ key: 'EpayKey', value: inputs.EpayKey });
}
if (currentProvider === 'yipay' && inputs.YipayAppId !== '') {
if (inputs.YipayAppId !== '') {
options.push({ key: 'YipayAppId', value: inputs.YipayAppId.trim() });
}
if (currentProvider === 'yipay' && inputs.YipayNotifyUrl !== '') {
if (inputs.YipayNotifyUrl !== '') {
options.push({
key: 'YipayNotifyUrl',
value: inputs.YipayNotifyUrl.trim(),
});
}
if (currentProvider === 'yipay' && inputs.YipayReturnUrl !== '') {
if (inputs.YipayReturnUrl !== '') {
options.push({
key: 'YipayReturnUrl',
value: inputs.YipayReturnUrl.trim(),
});
}
if (
currentProvider === 'yipay' &&
(inputs.YipayChannelExtra ?? '').trim() !==
(originInputs.YipayChannelExtra ?? '').trim()
) {
@ -381,7 +439,6 @@ export default function SettingsPaymentGateway(props) {
});
}
if (
currentProvider === 'yipay' &&
inputs.YipayAppSecret !== undefined &&
inputs.YipayAppSecret.trim() !== '' &&
inputs.YipayAppSecret !== originInputs.YipayAppSecret
@ -459,198 +516,161 @@ export default function SettingsPaymentGateway(props) {
>
<Form.Section text={t('支付设置')}>
<Text>
{t(
'(当前支持易支付/Yipay 配置,默认使用上方服务器地址作为回调地址!)',
)}
{t('支持支付宝、微信支付、易支付 / Yipay 等在线支付渠道配置')}
</Text>
<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.Select
field='OnlinePayProvider'
field="OnlinePayProvider"
label={t('在线支付通道')}
optionList={[
{ label: t('支付宝'), value: 'alipay' },
{ label: t('微信支付'), value: 'wechatpay' },
{ label: t('易支付'), value: 'epay' },
{ label: t('Yipay'), value: 'yipay' },
]}
/>
</Col>
<Col
xs={24}
sm={24}
md={8}
lg={8}
xl={8}
xs={24} sm={24} md={8} lg={8} xl={8}
style={{
display: currentProvider === 'epay' ? 'block' : 'none',
display: currentProvider === 'alipay' ? 'flex' : 'none',
alignItems: 'center',
paddingTop: 30,
}}
>
<Checkbox
checked={inputs.AlipayEnabled}
onChange={(e) => { const val = e.target.checked; setInputs((prev) => ({ ...prev, AlipayEnabled: val })); }}
>
{t('启用支付宝支付')}
</Checkbox>
</Col>
<Col
xs={24} sm={24} md={8} lg={8} xl={8}
style={{
display: currentProvider === 'wechatpay' ? 'flex' : 'none',
alignItems: 'center',
paddingTop: 30,
}}
>
<Checkbox
checked={inputs.WechatPayEnabled}
onChange={(e) => { const val = e.target.checked; setInputs((prev) => ({ ...prev, WechatPayEnabled: val })); }}
>
{t('启用微信支付')}
</Checkbox>
</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}
style={{
display: currentProvider === 'epay' ? 'flex' : 'none',
alignItems: 'center',
paddingTop: 30,
}}
>
<Checkbox
checked={inputs.EpayEnabled}
onChange={(e) => { const val = e.target.checked; setInputs((prev) => ({ ...prev, EpayEnabled: val })); }}
>
{t('启用易支付')}
</Checkbox>
</Col>
<Col
xs={24} sm={24} md={8} lg={8} xl={8}
style={{
display: currentProvider === 'yipay' ? 'flex' : 'none',
alignItems: 'center',
paddingTop: 30,
}}
>
<Checkbox
checked={inputs.YipayEnabled}
onChange={(e) => { const val = e.target.checked; setInputs((prev) => ({ ...prev, YipayEnabled: val })); }}
>
{t('启用 Yipay')}
</Checkbox>
</Col>
</Row><Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 8, display: currentProvider === 'alipay' ? 'flex' : 'none' }}
>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input field="AlipayAppId" label={t('支付宝应用ID')} placeholder={t('请填写支付宝应用ID')} />
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input field="AlipayPrivateKey" label={t('支付宝私钥')} placeholder={t('请填写支付宝私钥')} type="password" />
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input field="AlipayPublicKey" label={t('支付宝公钥')} placeholder={t('请填写支付宝公钥')} />
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 8, display: currentProvider === 'wechatpay' ? 'flex' : 'none' }}
>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input field="WechatPayAppId" label={t('微信支付应用ID')} placeholder={t('请填写微信支付应用ID')} />
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input field="WechatPayMchId" label={t('微信支付商户号')} placeholder={t('请填写微信支付商户号')} />
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input field="WechatPayApiV3Key" label={t('微信支付 API V3 密钥')} placeholder={t('请填写 API V3 密钥')} type="password" />
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 8, display: currentProvider === 'epay' ? 'flex' : 'none' }}
>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input field="PayAddress" label={t('支付地址')} placeholder={t('例如https://yourdomain.com')} />
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input field="EpayId" label={t('易支付商户号')} placeholder={t('例如0001')} />
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input field="EpayKey" label={t('易支付商户密钥')} placeholder={t('敏感信息不会发送到前端显示')} type="password" />
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 8, display: currentProvider === 'yipay' ? 'flex' : 'none' }}
>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field='PayAddress'
field="YipayRequestURL"
label={t('支付地址')}
placeholder={t('例如https://yourdomain.com')}
placeholder={t('例如https://pay.xxx.com与易支付网关根地址相同')}
extraText={t('对应配置项 YipayRequestURL保存时会同步写入 PayAddress供服务端与易支付字段兼容。')}
/>
</Col>
<Col
xs={24}
sm={24}
md={8}
lg={8}
xl={8}
style={{
display: currentProvider === 'yipay' ? 'block' : 'none',
}}
>
<Form.Input
field='YipayRequestURL'
label={t('支付地址')}
placeholder={t(
'例如https://pay.xxx.com与易支付网关根地址相同',
)}
extraText={t(
'对应配置项 YipayRequestURL保存时会同步写入 PayAddress供服务端与易支付字段兼容。',
)}
/>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input field="YipayMchNo" label={t('Yipay 商户号(可选覆盖)')} placeholder={t('例如M1691xxxx')} />
</Col>
<Col
xs={24}
sm={24}
md={8}
lg={8}
xl={8}
style={{
display: currentProvider === 'epay' ? 'block' : 'none',
}}
>
<Form.Input
field='EpayId'
label={t('易支付商户号')}
placeholder={t('例如0001')}
/>
</Col>
<Col
xs={24}
sm={24}
md={8}
lg={8}
xl={8}
style={{
display: currentProvider === 'epay' ? 'block' : 'none',
}}
>
<Form.Input
field='EpayKey'
label={t('易支付商户密钥')}
placeholder={t('敏感信息不会发送到前端显示')}
type='password'
/>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input field="YipayAppId" label={t('Yipay 应用ID')} placeholder={t('例如62b2f8f6xxxxxx')} />
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{
marginTop: 16,
display: currentProvider === 'yipay' ? 'flex' : 'none',
}}
style={{ marginTop: 16, display: currentProvider === 'yipay' ? 'flex' : 'none' }}
>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field='YipayMchNo'
label={t('Yipay 商户号(可选覆盖)')}
placeholder={t('例如M1691xxxx')}
/>
<Form.Input field="YipayAppSecret" label={t('Yipay AppSecret')} placeholder={t('用于 MD5 签名的密钥(后端脱敏回显;修改后保存才会更新)')} autoComplete="off" />
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field='YipayAppId'
label={t('Yipay 应用ID')}
placeholder={t('例如62b2f8f6xxxxxx')}
/>
<Form.Input field="YipayNotifyUrl" label={t('Yipay 异步通知地址')} placeholder={t('可留空,留空则使用下方"回调地址"或服务器地址拼接默认路径')} />
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field='YipayAppSecret'
label={t('Yipay AppSecret')}
placeholder={t(
'用于 MD5 签名的密钥(后端脱敏回显;修改后保存才会更新)',
)}
autoComplete='off'
/>
<Form.Input field="YipayReturnUrl" label={t('Yipay 同步通知地址')} placeholder={t('例如https://yourdomain.com/console/log')} />
</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='CustomCallbackAddress'
label={t('回调地址')}
placeholder={t('例如https://yourdomain.com')}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.InputNumber
field='Price'
precision={2}
label={t('充值价格x元/美金)')}
placeholder={t('例如7就是7元/美金')}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.InputNumber
field='MinTopUp'
label={t('最低充值美元数量')}
placeholder={t('例如2就是最低充值2$')}
/>
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{
marginTop: 16,
display: currentProvider === 'yipay' ? 'flex' : 'none',
}}
>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field='YipayNotifyUrl'
label={t('Yipay 异步通知地址')}
placeholder={t(
'可留空,留空则使用下方「回调地址」或服务器地址拼接默认路径',
)}
extraText={(() => {
const hint = getDefaultYipayNotifyHint(
props.options.ServerAddress,
props.options.CustomCallbackAddress,
);
return hint
? `默认:${hint}(留空时与后端异步通知规则一致)`
: '请先配置「服务器地址」或「回调地址」以预览默认异步通知 URL';
})()}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field='YipayReturnUrl'
label={t('Yipay 同步通知地址')}
placeholder={t('例如https://yourdomain.com/console/log')}
extraText={(() => {
const hint = getDefaultYipayReturnHint(
props.options.ServerAddress,
);
return hint
? `默认:${hint}(留空时使用)`
: '请先配置「服务器地址」以预览默认同步跳转 URL';
})()}
/>
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{
marginTop: 8,
display: currentProvider === 'yipay' ? 'flex' : 'none',
}}
style={{ marginTop: 8, display: currentProvider === 'yipay' ? 'flex' : 'none' }}
>
<Col span={24}>
<div style={{ marginBottom: 8 }}>
@ -659,77 +679,39 @@ export default function SettingsPaymentGateway(props) {
<TextArea
value={inputs.YipayChannelExtra}
onChange={handleYipayChannelExtraChange}
placeholder={'{"payDataType":"payUrl"}'}
placeholder='{"payDataType":"payUrl"}'
rows={1}
autoComplete='off'
style={{
width: '100%',
fontFamily: 'monospace',
fontSize: 12,
}}
autoComplete="off"
style={{ width: "100%", fontFamily: "monospace", fontSize: 12 }}
/>
<Text
type='tertiary'
size='small'
style={{ marginTop: 8, display: 'block' }}
>
{t(
'可选。Jeepay channelExtraJSON。留空时一般 _PC 会附带 payDataType=payUrlPP_PC 会附带 payDataType 与 cancelUrl与 returnUrl 同源,见 yiPay PPPcOrderRQ。可在此覆盖 cancelUrl 等。',
)}
<Text type="tertiary" size="small" style={{ marginTop: 8, display: "block" }}>
{t('可选。Jeepay channelExtraJSON。留空时一般 _PC 会附带 payDataType=payUrlPP_PC 会附带 payDataType 与 cancelUrl与 returnUrl 同源,见 yiPay PPPcOrderRQ。可在此覆盖 cancelUrl 等。')}
</Text>
</Col>
</Row>
<Form.TextArea
field='TopupGroupRatio'
label={t('充值分组倍率')}
placeholder={t('为一个 JSON 文本,键为组名称,值为倍率')}
autosize
/>
<Form.TextArea
field='PayMethods'
label={t('充值方式设置')}
placeholder={t('为一个 JSON 文本')}
autosize
/>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 16 }}
>
<Col span={24}>
<Form.TextArea
field='AmountOptions'
label={t('自定义充值数量选项')}
placeholder={t(
'为一个 JSON 数组,例如:[10, 20, 50, 100, 200, 500]',
)}
autosize
extraText={t(
'设置用户可选择的充值数量选项,例如:[10, 20, 50, 100, 200, 500]',
)}
/>
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }} style={{ marginTop: 8 }}>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input field="CustomCallbackAddress" label={t('回调地址')} placeholder={t('例如https://yourdomain.com')} />
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.InputNumber field="Price" precision={2} label={t('充值价格x元/美金)')} placeholder={t('例如7就是7元/美金')} />
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.InputNumber field="MinTopUp" label={t('最低充值美元数量')} placeholder={t('例如2就是最低充值2$')} />
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 16 }}
>
<Form.TextArea field="TopupGroupRatio" label={t('充值分组倍率')} placeholder={t('为一个 JSON 文本,键为组名称,值为倍率')} autosize />
<Form.TextArea field="PayMethods" label={t('充值方式设置')} placeholder={t('为一个 JSON 文本')} autosize />
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }} style={{ marginTop: 16 }}>
<Col span={24}>
<Form.TextArea
field='AmountDiscount'
label={t('充值金额折扣配置')}
placeholder={t(
'为一个 JSON 对象,例如:{"100": 0.95, "200": 0.9, "500": 0.85}',
)}
autosize
extraText={t(
'设置不同充值金额对应的折扣,键为充值金额,值为折扣率,例如:{"100": 0.95, "200": 0.9, "500": 0.85}',
)}
/>
<Form.TextArea field="AmountOptions" label={t('自定义充值数量选项')} placeholder={t('为一个 JSON 数组,例如:[10, 20, 50, 100, 200, 500]')} autosize extraText={t('设置用户可选择的充值数量选项,例如:[10, 20, 50, 100, 200, 500]')} />
</Col>
</Row>
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }} style={{ marginTop: 16 }}>
<Col span={24}>
<Form.TextArea field="AmountDiscount" label={t('充值金额折扣配置')} placeholder={t('为一个 JSON 对象,例如:{"100": 0.95, "200": 0.9, "500": 0.85}')} autosize extraText={t('设置不同充值金额对应的折扣,键为充值金额,值为折扣率,例如:{"100": 0.95, "200": 0.9, "500": 0.85}')} />
</Col>
</Row>
<Button onClick={submitPayAddress}>{t('更新支付设置')}</Button>
</Form.Section>
</Form>