/* 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 . For commercial licensing, please contact support@quantumnous.com */ import React, { useEffect, useState, useRef } from 'react'; import { Banner, Button, Form, Row, Col, Typography, Spin, Table, Modal, Input, Space, } from '@douyinfe/semi-ui'; const { Text } = Typography; import { API, showError, showSuccess } from '../../../helpers'; import { useTranslation } from 'react-i18next'; export default function SettingsPaymentGatewayWaffo(props) { const { t } = useTranslation(); const [loading, setLoading] = useState(false); const [inputs, setInputs] = useState({ WaffoEnabled: false, WaffoApiKey: '', WaffoPrivateKey: '', WaffoPublicCert: '', WaffoSandboxPublicCert: '', WaffoSandboxApiKey: '', WaffoSandboxPrivateKey: '', WaffoSandbox: false, WaffoMerchantId: '', WaffoCurrency: 'USD', WaffoUnitPrice: 1.0, WaffoMinTopUp: 1, WaffoNotifyUrl: '', WaffoReturnUrl: '', }); const [originInputs, setOriginInputs] = useState({}); const formApiRef = useRef(null); const iconFileInputRef = useRef(null); const handleIconFileChange = (e) => { const file = e.target.files[0]; if (!file) return; const MAX_ICON_SIZE = 100 * 1024; // 100 KB if (file.size > MAX_ICON_SIZE) { showError(t('图标文件不能超过 100KB,请压缩后重新上传')); e.target.value = ''; return; } const reader = new FileReader(); reader.onload = (event) => { setPayMethodForm((prev) => ({ ...prev, icon: event.target.result })); }; reader.readAsDataURL(file); e.target.value = ''; }; // 支付方式列表 const [waffoPayMethods, setWaffoPayMethods] = useState([]); // 支付方式弹窗 const [payMethodModalVisible, setPayMethodModalVisible] = useState(false); // 当前编辑的索引,-1 表示新增 const [editingPayMethodIndex, setEditingPayMethodIndex] = useState(-1); // 弹窗内表单字段的临时状态 const [payMethodForm, setPayMethodForm] = useState({ name: '', icon: '', payMethodType: '', payMethodName: '', }); useEffect(() => { if (props.options && formApiRef.current) { const currentInputs = { WaffoEnabled: props.options.WaffoEnabled === 'true' || props.options.WaffoEnabled === true, WaffoApiKey: props.options.WaffoApiKey || '', WaffoPrivateKey: props.options.WaffoPrivateKey || '', WaffoPublicCert: props.options.WaffoPublicCert || '', WaffoSandboxPublicCert: props.options.WaffoSandboxPublicCert || '', WaffoSandboxApiKey: props.options.WaffoSandboxApiKey || '', WaffoSandboxPrivateKey: props.options.WaffoSandboxPrivateKey || '', WaffoSandbox: props.options.WaffoSandbox === 'true', WaffoMerchantId: props.options.WaffoMerchantId || '', WaffoCurrency: props.options.WaffoCurrency || 'USD', WaffoUnitPrice: parseFloat(props.options.WaffoUnitPrice) || 1.0, WaffoMinTopUp: parseInt(props.options.WaffoMinTopUp) || 1, WaffoNotifyUrl: props.options.WaffoNotifyUrl || '', WaffoReturnUrl: props.options.WaffoReturnUrl || '', }; setInputs(currentInputs); setOriginInputs({ ...currentInputs }); formApiRef.current.setValues(currentInputs); // 解析支付方式列表 try { const rawPayMethods = props.options.WaffoPayMethods; if (rawPayMethods) { const parsed = JSON.parse(rawPayMethods); if (Array.isArray(parsed)) { setWaffoPayMethods(parsed); } } } catch { setWaffoPayMethods([]); } } }, [props.options]); const handleFormChange = (values) => { setInputs(values); }; const submitWaffoSetting = async () => { setLoading(true); try { const options = []; options.push({ key: 'WaffoEnabled', value: inputs.WaffoEnabled ? 'true' : 'false', }); if (inputs.WaffoApiKey && inputs.WaffoApiKey !== '') { options.push({ key: 'WaffoApiKey', value: inputs.WaffoApiKey }); } if (inputs.WaffoPrivateKey && inputs.WaffoPrivateKey !== '') { options.push({ key: 'WaffoPrivateKey', value: inputs.WaffoPrivateKey }); } options.push({ key: 'WaffoPublicCert', value: inputs.WaffoPublicCert || '' }); options.push({ key: 'WaffoSandboxPublicCert', value: inputs.WaffoSandboxPublicCert || '' }); if (inputs.WaffoSandboxApiKey && inputs.WaffoSandboxApiKey !== '') { options.push({ key: 'WaffoSandboxApiKey', value: inputs.WaffoSandboxApiKey }); } if (inputs.WaffoSandboxPrivateKey && inputs.WaffoSandboxPrivateKey !== '') { options.push({ key: 'WaffoSandboxPrivateKey', value: inputs.WaffoSandboxPrivateKey }); } options.push({ key: 'WaffoSandbox', value: inputs.WaffoSandbox ? 'true' : 'false', }); options.push({ key: 'WaffoMerchantId', value: inputs.WaffoMerchantId || '' }); options.push({ key: 'WaffoCurrency', value: inputs.WaffoCurrency || '' }); options.push({ key: 'WaffoUnitPrice', value: String(inputs.WaffoUnitPrice || 1.0), }); options.push({ key: 'WaffoMinTopUp', value: String(inputs.WaffoMinTopUp || 1), }); options.push({ key: 'WaffoNotifyUrl', value: inputs.WaffoNotifyUrl || '' }); options.push({ key: 'WaffoReturnUrl', value: inputs.WaffoReturnUrl || '' }); // 保存支付方式列表 options.push({ key: 'WaffoPayMethods', value: JSON.stringify(waffoPayMethods), }); // 发送请求 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); }; // 打开新增弹窗 const openAddPayMethodModal = () => { setEditingPayMethodIndex(-1); setPayMethodForm({ name: '', icon: '', payMethodType: '', payMethodName: '' }); setPayMethodModalVisible(true); }; // 打开编辑弹窗 const openEditPayMethodModal = (record, index) => { setEditingPayMethodIndex(index); setPayMethodForm({ name: record.name || '', icon: record.icon || '', payMethodType: record.payMethodType || '', payMethodName: record.payMethodName || '', }); setPayMethodModalVisible(true); }; // 确认保存弹窗(新增或编辑) const handlePayMethodModalOk = () => { if (!payMethodForm.name || payMethodForm.name.trim() === '') { showError(t('支付方式名称不能为空')); return; } const newMethod = { name: payMethodForm.name.trim(), icon: payMethodForm.icon.trim(), payMethodType: payMethodForm.payMethodType.trim(), payMethodName: payMethodForm.payMethodName.trim(), }; if (editingPayMethodIndex === -1) { // 新增 setWaffoPayMethods([...waffoPayMethods, newMethod]); } else { // 编辑 const updated = [...waffoPayMethods]; updated[editingPayMethodIndex] = newMethod; setWaffoPayMethods(updated); } setPayMethodModalVisible(false); }; // 删除支付方式 const handleDeletePayMethod = (index) => { const updated = waffoPayMethods.filter((_, i) => i !== index); setWaffoPayMethods(updated); }; // 支付方式表格列定义 const payMethodColumns = [ { title: t('显示名称'), dataIndex: 'name', }, { title: t('图标'), dataIndex: 'icon', render: (text) => text ? ( ) : ( — ), }, { title: t('支付方式类型'), dataIndex: 'payMethodType', render: (text) => text || —, }, { title: t('支付方式名称'), dataIndex: 'payMethodName', render: (text) => text || —, }, { title: t('操作'), key: 'action', render: (_, record, index) => ( openEditPayMethodModal(record, index)} > {t('编辑')} handleDeletePayMethod(index)} > {t('删除')} ), }, ]; return ( (formApiRef.current = api)} > {t('Waffo 是一个支付聚合平台,支持多种支付方式。')} Waffo Official Site {t('更新 Waffo 设置')} {/* 支付方式配置区块(独立于 Form,使用独立状态管理) */} {t('支付方式')} {t('配置 Waffo 充值时可用的支付方式,保存后在充值页面展示给用户。')} {t('新增支付方式')} index} pagination={false} size='small' empty={{t('暂无支付方式,点击上方按钮新增')}} /> {t('更新 Waffo 设置')} {/* 新增/编辑支付方式弹窗 */} setPayMethodModalVisible(false)} okText={t('确定')} cancelText={t('取消')} > {t('显示名称')} * setPayMethodForm({ ...payMethodForm, name: val })} placeholder={t('例如:Credit Card')} /> {t('用户在充值页面看到的支付方式名称,例如:Credit Card')} {t('图标')} {payMethodForm.icon && ( )} iconFileInputRef.current?.click()} > {payMethodForm.icon ? t('重新上传') : t('上传图片')} {payMethodForm.icon && ( setPayMethodForm((prev) => ({ ...prev, icon: '' })) } > {t('清除')} )} {t('上传 PNG/JPG/SVG 图片,建议尺寸 ≤ 128×128px')} {t('Pay Method Type')} setPayMethodForm({ ...payMethodForm, payMethodType: val })} placeholder='CREDITCARD,DEBITCARD' maxLength={64} /> {t('Waffo API 参数,可空,例如:CREDITCARD,DEBITCARD(最多64位)')} {t('Pay Method Name')} setPayMethodForm({ ...payMethodForm, payMethodName: val })} placeholder={t('可空')} maxLength={64} /> {t('Waffo API 参数,可空(最多64位)')} ); }