/*
Copyright (C) 2023-2026 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 { type ChangeEvent, useRef, type SetStateAction, useState } from 'react'
import { Plus, Pencil, Trash2 } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Separator } from '@/components/ui/separator'
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table'
import { Textarea } from '@/components/ui/textarea'
import { Dialog } from '@/components/dialog'
import { SettingsSwitchField } from '../components/settings-form-layout'
export interface WaffoSettingsValues {
WaffoEnabled: boolean
WaffoApiKey: string
WaffoPrivateKey: string
WaffoPublicCert: string
WaffoSandboxPublicCert: string
WaffoSandboxApiKey: string
WaffoSandboxPrivateKey: string
WaffoSandbox: boolean
WaffoMerchantId: string
WaffoCurrency: string
WaffoUnitPrice: number
WaffoMinTopUp: number
WaffoNotifyUrl: string
WaffoReturnUrl: string
WaffoPayMethods: string
}
export interface PayMethod {
name: string
icon: string
payMethodType: string
payMethodName: string
}
type WaffoFieldValues = Omit
interface Props {
values: WaffoSettingsValues
onValueChange: (
key: K,
value: WaffoFieldValues[K]
) => void
payMethods: PayMethod[]
onPayMethodsChange: (value: SetStateAction) => void
}
export function WaffoSettingsSection({
values,
onValueChange,
payMethods,
onPayMethodsChange,
}: Props) {
const { t } = useTranslation()
const iconFileInputRef = useRef(null)
const [methodDialogOpen, setMethodDialogOpen] = useState(false)
const [editingIdx, setEditingIdx] = useState(-1)
const [methodForm, setMethodForm] = useState({
name: '',
icon: '',
payMethodType: '',
payMethodName: '',
})
const openAdd = () => {
setEditingIdx(-1)
setMethodForm({ name: '', icon: '', payMethodType: '', payMethodName: '' })
setMethodDialogOpen(true)
}
const openEdit = (idx: number) => {
setEditingIdx(idx)
setMethodForm({ ...payMethods[idx] })
setMethodDialogOpen(true)
}
const saveMethod = () => {
if (!methodForm.name.trim())
return toast.error(t('Payment method name is required'))
if (editingIdx === -1) {
onPayMethodsChange((prev) => [...prev, methodForm])
} else {
onPayMethodsChange((prev) =>
prev.map((m, i) => (i === editingIdx ? methodForm : m))
)
}
setMethodDialogOpen(false)
}
const handleIconFileChange = (event: ChangeEvent) => {
const file = event.target.files?.[0]
if (!file) {
return
}
const maxIconSize = 100 * 1024
if (file.size > maxIconSize) {
toast.error(t('Icon file must be 100 KB or smaller'))
event.target.value = ''
return
}
const reader = new FileReader()
reader.onload = (loadEvent) => {
setMethodForm((previous) => ({
...previous,
icon:
typeof loadEvent.target?.result === 'string'
? loadEvent.target.result
: '',
}))
}
reader.readAsDataURL(file)
event.target.value = ''
}
return (
<>
{t('Waffo Aggregator Gateway')}
{t(
'Payment aggregator mode — onboard with your own registered company (offshore entity). Built for Enterprise.'
)}
{t(
'Obtain the API key, merchant ID, and RSA key pair from the Waffo dashboard, and configure the callback URL.'
)}