feat: add stripe setting page
This commit is contained in:
@@ -2,6 +2,7 @@ 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.js';
|
import SettingsGeneralPayment from '../../pages/Setting/Payment/SettingsGeneralPayment.js';
|
||||||
import SettingsPaymentGateway from '../../pages/Setting/Payment/SettingsPaymentGateway.js';
|
import SettingsPaymentGateway from '../../pages/Setting/Payment/SettingsPaymentGateway.js';
|
||||||
|
import SettingsPaymentGatewayStripe from '../../pages/Setting/Payment/SettingsPaymentGatewayStripe.js';
|
||||||
import { API, showError } from '../../helpers';
|
import { API, showError } from '../../helpers';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@@ -17,6 +18,12 @@ const PaymentSetting = () => {
|
|||||||
TopupGroupRatio: '',
|
TopupGroupRatio: '',
|
||||||
CustomCallbackAddress: '',
|
CustomCallbackAddress: '',
|
||||||
PayMethods: '',
|
PayMethods: '',
|
||||||
|
|
||||||
|
StripeApiSecret: '',
|
||||||
|
StripeWebhookSecret: '',
|
||||||
|
StripePriceId: '',
|
||||||
|
StripeUnitPrice: 8.0,
|
||||||
|
StripeMinTopUp: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
let [loading, setLoading] = useState(false);
|
let [loading, setLoading] = useState(false);
|
||||||
@@ -38,6 +45,8 @@ const PaymentSetting = () => {
|
|||||||
break;
|
break;
|
||||||
case 'Price':
|
case 'Price':
|
||||||
case 'MinTopUp':
|
case 'MinTopUp':
|
||||||
|
case 'StripeUnitPrice':
|
||||||
|
case 'StripeMinTopUp':
|
||||||
newInputs[item.key] = parseFloat(item.value);
|
newInputs[item.key] = parseFloat(item.value);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -80,6 +89,9 @@ const PaymentSetting = () => {
|
|||||||
<Card style={{ marginTop: '10px' }}>
|
<Card style={{ marginTop: '10px' }}>
|
||||||
<SettingsPaymentGateway options={inputs} refresh={onRefresh} />
|
<SettingsPaymentGateway options={inputs} refresh={onRefresh} />
|
||||||
</Card>
|
</Card>
|
||||||
|
<Card style={{ marginTop: '10px' }}>
|
||||||
|
<SettingsPaymentGatewayStripe options={inputs} refresh={onRefresh} />
|
||||||
|
</Card>
|
||||||
</Spin>
|
</Spin>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
196
web/src/pages/Setting/Payment/SettingsPaymentGatewayStripe.js
Normal file
196
web/src/pages/Setting/Payment/SettingsPaymentGatewayStripe.js
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
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,
|
||||||
|
verifyJSON,
|
||||||
|
} 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,
|
||||||
|
});
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
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 !== undefined && inputs.StripeApiSecret !== '') {
|
||||||
|
options.push({ key: 'StripeApiSecret', value: inputs.StripeApiSecret });
|
||||||
|
}
|
||||||
|
if (inputs.StripeWebhookSecret !== undefined && inputs.StripeWebhookSecret !== '') {
|
||||||
|
options.push({ key: 'StripeWebhookSecret', value: inputs.StripeWebhookSecret });
|
||||||
|
}
|
||||||
|
if (inputs.StripePriceId !== '') {
|
||||||
|
options.push({key: 'StripePriceId', value: inputs.StripePriceId,});
|
||||||
|
}
|
||||||
|
if (inputs.StripeUnitPrice !== '') {
|
||||||
|
options.push({ key: 'StripeUnitPrice', value: inputs.StripeUnitPrice.toString() });
|
||||||
|
}
|
||||||
|
if (inputs.StripeMinTopUp !== '') {
|
||||||
|
options.push({ key: 'StripeMinTopUp', value: inputs.StripeMinTopUp.toString() });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
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 && 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 ? props.options.ServerAddress : '网站地址'}/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>
|
||||||
|
</Row>
|
||||||
|
<Button onClick={submitStripeSetting}>{t('更新 Stripe 设置')}</Button>
|
||||||
|
</Form.Section>
|
||||||
|
</Form>
|
||||||
|
</Spin>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user