api / web /src /pages /Setting /Payment /SettingsPaymentGatewayStripe.jsx
lengfeng1360's picture
Upload 732 files
4ef3a0e verified
/*
Copyright (C) 2025 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
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 SettingsPaymentGateway(props) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [inputs, setInputs] = useState({
StripeApiSecret: '',
StripeWebhookSecret: '',
StripePriceId: '',
StripeUnitPrice: 8.0,
StripeMinTopUp: 1,
StripePromotionCodesEnabled: false,
});
const [originInputs, setOriginInputs] = useState({});
const formApiRef = useRef(null);
useEffect(() => {
if (props.options && formApiRef.current) {
const currentInputs = {
StripeApiSecret: props.options.StripeApiSecret || '',
StripeWebhookSecret: props.options.StripeWebhookSecret || '',
StripePriceId: props.options.StripePriceId || '',
StripeUnitPrice:
props.options.StripeUnitPrice !== undefined
? parseFloat(props.options.StripeUnitPrice)
: 8.0,
StripeMinTopUp:
props.options.StripeMinTopUp !== undefined
? parseFloat(props.options.StripeMinTopUp)
: 1,
StripePromotionCodesEnabled:
props.options.StripePromotionCodesEnabled !== undefined
? props.options.StripePromotionCodesEnabled
: false,
};
setInputs(currentInputs);
setOriginInputs({ ...currentInputs });
formApiRef.current.setValues(currentInputs);
}
}, [props.options]);
const handleFormChange = (values) => {
setInputs(values);
};
const submitStripeSetting = async () => {
if (props.options.ServerAddress === '') {
showError(t('请先填写服务器地址'));
return;
}
setLoading(true);
try {
const options = [];
if (inputs.StripeApiSecret && inputs.StripeApiSecret !== '') {
options.push({ key: 'StripeApiSecret', value: inputs.StripeApiSecret });
}
if (inputs.StripeWebhookSecret && inputs.StripeWebhookSecret !== '') {
options.push({
key: 'StripeWebhookSecret',
value: inputs.StripeWebhookSecret,
});
}
if (inputs.StripePriceId !== '') {
options.push({ key: 'StripePriceId', value: inputs.StripePriceId });
}
if (
inputs.StripeUnitPrice !== undefined &&
inputs.StripeUnitPrice !== null
) {
options.push({
key: 'StripeUnitPrice',
value: inputs.StripeUnitPrice.toString(),
});
}
if (
inputs.StripeMinTopUp !== undefined &&
inputs.StripeMinTopUp !== null
) {
options.push({
key: 'StripeMinTopUp',
value: inputs.StripeMinTopUp.toString(),
});
}
if (
originInputs['StripePromotionCodesEnabled'] !==
inputs.StripePromotionCodesEnabled &&
inputs.StripePromotionCodesEnabled !== undefined
) {
options.push({
key: 'StripePromotionCodesEnabled',
value: inputs.StripePromotionCodesEnabled ? 'true' : 'false',
});
}
// 发送请求
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('Stripe 设置')}>
<Text>
Stripe 密钥、Webhook 等设置请
<a
href='https://dashboard.stripe.com/developers'
target='_blank'
rel='noreferrer'
>
点击此处
</a>
进行设置,最好先在
<a
href='https://dashboard.stripe.com/test/developers'
target='_blank'
rel='noreferrer'
>
测试环境
</a>
进行测试。
<br />
</Text>
<Banner
type='info'
description={`Webhook :${props.options.ServerAddress ? removeTrailingSlash(props.options.ServerAddress) : t('网站地址')}/api/stripe/webhook`}
/>
<Banner
type='warning'
description={`需要包含事件checkout.session.completed checkout.session.expired`}
/>
<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='StripeApiSecret'
label={t('API 密钥')}
placeholder={t(
'sk_xxx rk_xxx Stripe 密钥敏感信息不显示',
)}
type='password'
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field='StripeWebhookSecret'
label={t('Webhook 签名密钥')}
placeholder={t('whsec_xxx Webhook 签名密钥敏感信息不显示')}
type='password'
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field='StripePriceId'
label={t('商品价格 ID')}
placeholder={t('price_xxx 的商品价格 ID新建产品后可获得')}
/>
</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='StripeUnitPrice'
precision={2}
label={t('充值价格x元/美金)')}
placeholder={t('例如7就是7元/美金')}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.InputNumber
field='StripeMinTopUp'
label={t('最低充值美元数量')}
placeholder={t('例如2就是最低充值2$')}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Switch
field='StripePromotionCodesEnabled'
size='default'
checkedText='|'
uncheckedText='〇'
label={t('允许在 Stripe 支付中输入促销码')}
/>
</Col>
</Row>
<Button onClick={submitStripeSetting}>{t('更新 Stripe 设置')}</Button>
</Form.Section>
</Form>
</Spin>
);
}