new-api/web/classic/src/pages/Setting/Payment/SettingsPaymentGatewayWaffoPancake.jsx

203 lines
6.7 KiB
JavaScript
Vendored
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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, useRef, useState } from 'react';
import { Banner, Button, Col, Form, Row, Spin } from '@douyinfe/semi-ui';
import {
API,
removeTrailingSlash,
showError,
showSuccess,
} from '../../../helpers';
import { useTranslation } from 'react-i18next';
import { BookOpen } from 'lucide-react';
const defaultInputs = {
WaffoPancakeMerchantID: '',
WaffoPancakePrivateKey: '',
WaffoPancakeReturnURL: '',
};
export default function SettingsPaymentGatewayWaffoPancake(props) {
const { t } = useTranslation();
const sectionTitle = props.hideSectionTitle
? undefined
: t('Waffo Pancake 设置');
const [loading, setLoading] = useState(false);
const [inputs, setInputs] = useState(defaultInputs);
const formApiRef = useRef(null);
useEffect(() => {
if (!props.options || !formApiRef.current) return;
const currentInputs = {
WaffoPancakeMerchantID: props.options.WaffoPancakeMerchantID || '',
WaffoPancakePrivateKey: props.options.WaffoPancakePrivateKey || '',
WaffoPancakeReturnURL: props.options.WaffoPancakeReturnURL || '',
};
setInputs(currentInputs);
formApiRef.current.setValues(currentInputs);
}, [props.options]);
const handleFormChange = (values) => {
setInputs(values);
};
const submitWaffoPancakeSetting = async () => {
const values = {
...inputs,
...(formApiRef.current?.getValues?.() || {}),
};
setLoading(true);
try {
// Classic admin only persists the three operator-typed fields.
// Store/Product binding is handled exclusively by the default
// frontend's catalog flow (see waffo-pancake-settings-section.tsx)
// because picking entities from a live catalog needs the Select +
// dependent-dropdown UX that the classic Semi-UI page doesn't have.
const options = [
{
key: 'WaffoPancakeMerchantID',
value: values.WaffoPancakeMerchantID || '',
},
{
key: 'WaffoPancakeReturnURL',
value: removeTrailingSlash(values.WaffoPancakeReturnURL || ''),
},
];
if ((values.WaffoPancakePrivateKey || '').trim()) {
options.push({
key: 'WaffoPancakePrivateKey',
value: values.WaffoPancakePrivateKey,
});
}
const results = await Promise.all(
options.map((opt) =>
API.put('/api/option/', {
key: opt.key,
value: opt.value,
}),
),
);
const errorResults = results.filter((res) => !res.data.success);
if (errorResults.length > 0) {
errorResults.forEach((res) => showError(res.data.message));
return;
}
showSuccess(t('更新成功'));
props.refresh?.();
} catch (error) {
showError(t('更新失败'));
} finally {
setLoading(false);
}
};
return (
<Spin spinning={loading}>
<Form
initValues={inputs}
onValueChange={handleFormChange}
getFormApi={(api) => (formApiRef.current = api)}
>
<Form.Section text={sectionTitle}>
<Banner
type='info'
icon={<BookOpen size={16} />}
description={
<>
Waffo Pancake 商户 ID 与私钥请在
<a
href='https://pancake.waffo.ai/merchant/dashboard'
target='_blank'
rel='noreferrer'
>
Waffo Pancake 控制台
</a>
获取保存后系统会自动在该商户名下创建 Store + Product无需手动配置
环境test / 生产由你粘贴的 API 私钥本身决定
请在 Pancake 控制台把下面两个回调地址分别注册到 Test Mode Production Mode
两个 webhook 位置分开走避免测试流量污染生产数据
<br />
{t('Test 回调地址')}
{props.options.ServerAddress
? removeTrailingSlash(props.options.ServerAddress)
: t('网站地址')}
/api/waffo-pancake/webhook/test
<br />
{t('Production 回调地址')}
{props.options.ServerAddress
? removeTrailingSlash(props.options.ServerAddress)
: t('网站地址')}
/api/waffo-pancake/webhook/prod
</>
}
style={{ marginBottom: 12 }}
/>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 16 }}
>
<Col xs={24} sm={24} md={12} lg={12} xl={12}>
<Form.Input
field='WaffoPancakeMerchantID'
label={t('商户 ID')}
placeholder={t('例如MER_xxx')}
/>
</Col>
<Col xs={24} sm={24} md={12} lg={12} xl={12}>
<Form.Input
field='WaffoPancakeReturnURL'
label={t('支付返回地址')}
placeholder={t('例如https://example.com/console/topup')}
/>
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 16 }}
>
<Col xs={24}>
<Form.TextArea
field='WaffoPancakePrivateKey'
label={t('API 私钥')}
placeholder={t('填写后覆盖当前私钥,留空表示保持当前不变')}
extraText={t('⚠ 测试 / 生产环境由你粘进来的 API 私钥本身决定——集成阶段用 Test Key正式上线时再换成 Production Key')}
type='password'
autosize={{ minRows: 4, maxRows: 8 }}
/>
</Col>
</Row>
<Button onClick={submitWaffoPancakeSetting}>
{t('更新 Waffo Pancake 设置')}
</Button>
</Form.Section>
</Form>
</Spin>
);
}