Files
video-gen/video-gen-admin/src/pages/AdminPaymentConfig.tsx
T
2026-06-25 17:07:32 +08:00

282 lines
13 KiB
TypeScript
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.
import React, { useEffect, useState } from 'react';
import {
Button, Card, Form, Input, message, Switch, Typography, InputNumber, Space,
} from 'antd';
import {
SaveOutlined, WechatOutlined, AlipayCircleOutlined, DollarOutlined, ClockCircleOutlined,
} from '@ant-design/icons';
import { getPaymentConfigs, batchUpdatePaymentConfigs } from '../api';
const AdminPaymentConfig: React.FC = () => {
const [saving, setSaving] = useState(false);
const [wechatEnabled, setWechatEnabled] = useState(false);
const [alipayEnabled, setAlipayEnabled] = useState(false);
const [mockMode, setMockMode] = useState(false);
const [orderTimeout, setOrderTimeout] = useState(180);
const [form] = Form.useForm();
const load = async () => {
try {
const data = await getPaymentConfigs();
const map: Record<string, string> = {};
data.forEach((c: any) => { map[c.key] = c.value; });
form.setFieldsValue({
wechat_appid: map['payment_wechat_appid'] || '',
wechat_mch_id: map['payment_wechat_mch_id'] || '',
wechat_private_key: map['payment_wechat_private_key'] || '',
wechat_cert_serial_no: map['payment_wechat_cert_serial_no'] || '',
wechat_api_v3_key: map['payment_wechat_api_v3_key'] || '',
wechat_notify_url: map['payment_wechat_notify_url'] || '',
wechat_public_key: map['payment_wechat_public_key'] || '',
wechat_public_key_id: map['payment_wechat_public_key_id'] || '',
alipay_app_id: map['payment_alipay_app_id'] || '',
alipay_private_key: map['payment_alipay_private_key'] || '',
alipay_public_key: map['payment_alipay_public_key'] || '',
alipay_notify_url: map['payment_alipay_notify_url'] || '',
alipay_gateway: map['payment_alipay_gateway'] || '',
order_timeout: map['payment_order_timeout'] || '180',
});
setWechatEnabled(map['payment_wechat_enabled'] === 'true');
setAlipayEnabled(map['payment_alipay_enabled'] === 'true');
setMockMode(map['payment_mock'] === 'true');
setOrderTimeout(parseInt(map['payment_order_timeout'] || '180', 10));
} catch {
message.error('加载支付配置失败');
}
};
useEffect(() => { load(); }, []);
const handleSave = async () => {
try {
const values = await form.validateFields();
setSaving(true);
await batchUpdatePaymentConfigs({
payment_mock: String(mockMode),
payment_wechat_enabled: String(wechatEnabled),
payment_wechat_appid: values.wechat_appid || '',
payment_wechat_mch_id: values.wechat_mch_id || '',
payment_wechat_private_key: values.wechat_private_key || '',
payment_wechat_cert_serial_no: values.wechat_cert_serial_no || '',
payment_wechat_api_v3_key: values.wechat_api_v3_key || '',
payment_wechat_notify_url: values.wechat_notify_url || '',
payment_wechat_public_key: values.wechat_public_key || '',
payment_wechat_public_key_id: values.wechat_public_key_id || '',
payment_alipay_enabled: String(alipayEnabled),
payment_alipay_app_id: values.alipay_app_id || '',
payment_alipay_private_key: values.alipay_private_key || '',
payment_alipay_public_key: values.alipay_public_key || '',
payment_alipay_notify_url: values.alipay_notify_url || '',
payment_alipay_gateway: values.alipay_gateway || '',
payment_order_timeout: String(values.order_timeout || 180),
});
message.success('支付配置已保存');
load();
} catch {
message.error('保存失败');
} finally {
setSaving(false);
}
};
return (
<div style={{ maxWidth: '100%' }}>
{/* 上边一排:通用设置 + 模拟支付模式 */}
<div style={{ display: 'flex', gap: 24, flexWrap: 'wrap', marginBottom: 24 }}>
{/* 通用设置:订单超时时间 */}
<div style={{ flex: '1 1 400px', minWidth: 400 }}>
<Card variant="outlined" style={{ borderRadius: 12, border: '1px solid #f0f0f5' }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<div style={{
width: 40, height: 40, borderRadius: 10,
background: 'rgba(99,102,241,0.08)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: 20, color: '#6366f1',
}}><ClockCircleOutlined /></div>
<div>
<Typography.Text strong style={{ fontSize: 14 }}>订单超时时间</Typography.Text>
</div>
</div>
<Form form={form} layout="inline">
<Form.Item name="order_timeout" style={{ margin: 0 }}>
<Space.Compact style={{ width: 140 }}>
<InputNumber
min={30}
max={86400}
placeholder="180"
style={{ width: '100%' }}
size="middle"
/>
<Typography.Text></Typography.Text>
</Space.Compact>
</Form.Item>
</Form>
</div>
</Card>
</div>
{/* 模拟支付模式 */}
<div style={{ flex: '1 1 400px', minWidth: 400 }}>
<Card variant="outlined" style={{ borderRadius: 12, border: '1px solid #f0f0f5', background: mockMode ? '#fff7e6' : '#fafbff' }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<div style={{
width: 40, height: 40, borderRadius: 10,
background: mockMode ? 'rgba(245,158,11,0.15)' : 'rgba(99,102,241,0.08)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: 20, color: mockMode ? '#f59e0b' : '#6366f1',
}}><DollarOutlined /></div>
<div>
<Typography.Text strong style={{ fontSize: 14 }}>模拟支付模式</Typography.Text>
</div>
</div>
<Switch checked={mockMode} onChange={setMockMode} checkedChildren="已开启" unCheckedChildren="已关闭" />
</div>
</Card>
</div>
</div>
{/* 下边一排:微信支付 + 支付宝 */}
<div style={{ display: 'flex', gap: 24, flexWrap: 'wrap' }}>
{/* 微信支付 */}
<div style={{ flex: '1 1 400px', minWidth: 400 }}>
<Card variant="outlined" style={{ borderRadius: 12, border: '1px solid #f0f0f5' }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 20 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<div style={{
width: 44, height: 44, borderRadius: 10,
background: 'rgba(7,193,96,0.08)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: 22, color: '#07c160',
}}><WechatOutlined /></div>
<div>
<Typography.Title level={5} style={{ margin: 0 }}>微信支付</Typography.Title>
<Typography.Text type="secondary" style={{ fontSize: 12 }}>微信商户号支付配置</Typography.Text>
</div>
</div>
<Switch checked={wechatEnabled} onChange={setWechatEnabled} checkedChildren="已启用" unCheckedChildren="未启用" />
</div>
<Form form={form} layout="vertical">
<Form.Item name="wechat_appid" label="AppID">
<Input placeholder="微信支付 AppID" size="large" disabled={!wechatEnabled} />
</Form.Item>
<Form.Item name="wechat_mch_id" label="商户号 (MchID)">
<Input placeholder="微信支付商户号" size="large" disabled={!wechatEnabled} />
</Form.Item>
<Form.Item
name="wechat_private_key"
label="商户私钥"
extra={`
⚠️ 重要:请填写 apiclient_key.pem 的内容,不是 apiclient_cert.pem
内容应该以 -----BEGIN PRIVATE KEY----- 或 -----BEGIN RSA PRIVATE KEY----- 开头
`}
>
<Input.TextArea
rows={4}
placeholder="
-----BEGIN PRIVATE KEY-----
...这里是私钥内容...
-----END PRIVATE KEY-----
"
disabled={!wechatEnabled}
/>
</Form.Item>
<Form.Item
name="wechat_cert_serial_no"
label="商户证书序列号"
extra="从 apiclient_cert.pem 中提取的证书序列号"
>
<Input placeholder="商户证书序列号" size="large" disabled={!wechatEnabled} />
</Form.Item>
<Form.Item name="wechat_api_v3_key" label="API v3 密钥">
<Input.Password placeholder="API v3 密钥" size="large" disabled={!wechatEnabled} />
</Form.Item>
<Form.Item name="wechat_notify_url" label="回调地址">
<Input placeholder="https://yourdomain.com/api/payments/wechat/callback" size="large" disabled={!wechatEnabled} />
</Form.Item>
<Form.Item
name="wechat_public_key"
label="微信平台公钥(可选)"
>
<Input.TextArea
rows={3}
placeholder="微信支付平台公钥内容"
disabled={!wechatEnabled}
/>
</Form.Item>
<Form.Item
name="wechat_public_key_id"
label="微信平台公钥ID(可选)"
>
<Input placeholder="平台公钥ID" size="large" disabled={!wechatEnabled} />
</Form.Item>
</Form>
</Card>
</div>
{/* 支付宝 */}
<div style={{ flex: '1 1 400px', minWidth: 400 }}>
<Card variant="outlined" style={{ borderRadius: 12, border: '1px solid #f0f0f5' }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 20 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<div style={{
width: 44, height: 44, borderRadius: 10,
background: 'rgba(0,122,255,0.08)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: 22, color: '#007aff',
}}><AlipayCircleOutlined /></div>
<div>
<Typography.Title level={5} style={{ margin: 0 }}>支付宝</Typography.Title>
<Typography.Text type="secondary" style={{ fontSize: 12 }}>支付宝应用支付配置</Typography.Text>
</div>
</div>
<Switch checked={alipayEnabled} onChange={setAlipayEnabled} checkedChildren="已启用" unCheckedChildren="未启用" />
</div>
<Form form={form} layout="vertical">
<Form.Item name="alipay_app_id" label="AppID">
<Input placeholder="支付宝应用AppID" size="large" disabled={!alipayEnabled} />
</Form.Item>
<Form.Item
name="alipay_private_key"
label="应用私钥"
extra="支付宝应用私钥(PKCS8格式),不需要添加头部和尾部标记"
>
<Input.TextArea rows={4} placeholder="
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
" disabled={!alipayEnabled} />
</Form.Item>
<Form.Item
name="alipay_public_key"
label="支付宝公钥"
extra="从支付宝开放平台获取的支付宝公钥"
>
<Input.TextArea rows={4} placeholder="
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
" disabled={!alipayEnabled} />
</Form.Item>
<Form.Item name="alipay_gateway" label="网关地址">
<Input placeholder="https://openapi.alipay.com/gateway.do" size="large" disabled={!alipayEnabled} />
</Form.Item>
<Form.Item name="alipay_notify_url" label="回调地址">
<Input placeholder="https://yourdomain.com/api/payments/alipay/callback" size="large" disabled={!alipayEnabled} />
</Form.Item>
</Form>
</Card>
</div>
</div>
<div style={{ display: 'flex', justifyContent: 'center', marginTop: 24 }}>
<Button type="primary" icon={<SaveOutlined />} onClick={handleSave} loading={saving}
size="large" style={{ borderRadius: 8, minWidth: 160, height: 44, fontSize: 15 }}>
保存配置
</Button>
</div>
</div>
);
};
export default AdminPaymentConfig;