220 lines
11 KiB
TypeScript
220 lines
11 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { GatewayProvider, GatewayCredentials } from '@/lib/types/settings';
|
|
import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle } from '@/components/ui/sheet';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Switch } from '@/components/ui/switch';
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
import { verifyGatewayCredentials, saveGatewayConfig } from '@/lib/actions/payment-settings';
|
|
import { toast } from 'sonner';
|
|
import { Lock, CheckBase, Loader2, Copy } from 'lucide-react';
|
|
import { decrypt } from '@/lib/payment-encryption';
|
|
|
|
interface GatewayConfigSheetProps {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
provider: GatewayProvider;
|
|
initialConfig: GatewayCredentials;
|
|
onSave: () => void;
|
|
}
|
|
|
|
export function GatewayConfigSheet({ open, onOpenChange, provider, initialConfig, onSave }: GatewayConfigSheetProps) {
|
|
const [config, setConfig] = useState<GatewayCredentials>(initialConfig);
|
|
const [loading, setLoading] = useState(false);
|
|
const [verifying, setVerifying] = useState(false);
|
|
|
|
// Reset config when provider changes
|
|
useEffect(() => {
|
|
// Decrypt sensitive fields for editing
|
|
const decrypted = { ...initialConfig };
|
|
if (decrypted.salt) decrypted.salt = decrypt(decrypted.salt);
|
|
setConfig(decrypted);
|
|
}, [provider, initialConfig, open]);
|
|
|
|
const handleVerify = async () => {
|
|
setVerifying(true);
|
|
try {
|
|
const res = await verifyGatewayCredentials(provider, config);
|
|
if (res.success) {
|
|
toast.success('Credentials Verified', { description: res.message });
|
|
} else {
|
|
toast.error('Verification Failed', { description: res.message });
|
|
}
|
|
} catch (error) {
|
|
toast.error('Verification Error');
|
|
} finally {
|
|
setVerifying(false);
|
|
}
|
|
};
|
|
|
|
const handleSave = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const res = await saveGatewayConfig(provider, config);
|
|
if (res.success) {
|
|
toast.success(`Saved ${provider} configuration`);
|
|
onSave();
|
|
onOpenChange(false);
|
|
} else {
|
|
toast.error(res.message);
|
|
}
|
|
} catch (error) {
|
|
toast.error('Failed to save configuration');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
<SheetContent className="w-[400px] sm:w-[540px] overflow-y-auto">
|
|
<SheetHeader>
|
|
<SheetTitle className="flex items-center gap-2 capitalize">
|
|
{provider} Configuration
|
|
</SheetTitle>
|
|
<SheetDescription>
|
|
Configure API keys and secrets for {provider}.
|
|
</SheetDescription>
|
|
</SheetHeader>
|
|
|
|
<div className="py-6 space-y-6">
|
|
{/* Environment Toggle */}
|
|
<div className="flex items-center justify-between p-4 border rounded-lg bg-muted/30">
|
|
<div className="space-y-0.5">
|
|
<Label>Mode</Label>
|
|
<div className="text-xs text-muted-foreground">
|
|
{config.mode === 'live' ? 'Production / Live Traffic' : 'Sandbox / Test Mode'}
|
|
</div>
|
|
</div>
|
|
<Switch
|
|
checked={config.mode === 'live'}
|
|
onCheckedChange={(c) => setConfig({ ...config, mode: c ? 'live' : 'test' })}
|
|
/>
|
|
</div>
|
|
|
|
<Tabs defaultValue="credentials">
|
|
<TabsList className="w-full">
|
|
<TabsTrigger value="credentials" className="flex-1">Credentials</TabsTrigger>
|
|
<TabsTrigger value="features" className="flex-1">Features</TabsTrigger>
|
|
<TabsTrigger value="webhooks" className="flex-1">Webhooks</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<TabsContent value="credentials" className="space-y-4 pt-4">
|
|
{/* Dynamic inputs based on provider */}
|
|
{(provider === 'razorpay' || provider === 'easebuzz') && (
|
|
<div className="space-y-2">
|
|
<Label>Key ID / Merchant Key</Label>
|
|
<Input
|
|
value={config.keyId || config.merchantId || ''}
|
|
onChange={(e) => setConfig({ ...config, keyId: e.target.value, merchantId: e.target.value })}
|
|
placeholder="rzp_test_..."
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{(provider === 'stripe') && (
|
|
<div className="space-y-2">
|
|
<Label>Public Key</Label>
|
|
<Input
|
|
value={config.publicKey || ''}
|
|
onChange={(e) => setConfig({ ...config, publicKey: e.target.value })}
|
|
placeholder="pk_test_..."
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{(provider === 'payu' || provider === 'easebuzz') && (
|
|
<div className="space-y-2">
|
|
<Label>Salt / Secret Key</Label>
|
|
<div className="relative">
|
|
<Input
|
|
type="password"
|
|
value={config.salt || ''}
|
|
onChange={(e) => setConfig({ ...config, salt: e.target.value })}
|
|
placeholder="Enter secret salt"
|
|
className="pr-10"
|
|
/>
|
|
<Lock className="absolute right-3 top-3 h-4 w-4 text-muted-foreground" />
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<Button variant="secondary" className="w-full mt-4" onClick={handleVerify} disabled={verifying}>
|
|
{verifying && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
Test Credentials
|
|
</Button>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="features" className="space-y-4 pt-4">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div className="flex items-center space-x-2 border p-3 rounded-md">
|
|
<Switch
|
|
id="feat-nb"
|
|
checked={config.features.netbanking}
|
|
onCheckedChange={(c) => setConfig({ ...config, features: { ...config.features, netbanking: c } })}
|
|
/>
|
|
<Label htmlFor="feat-nb">Netbanking</Label>
|
|
</div>
|
|
<div className="flex items-center space-x-2 border p-3 rounded-md">
|
|
<Switch
|
|
id="feat-upi"
|
|
checked={config.features.upi}
|
|
onCheckedChange={(c) => setConfig({ ...config, features: { ...config.features, upi: c } })}
|
|
/>
|
|
<Label htmlFor="feat-upi">UPI</Label>
|
|
</div>
|
|
<div className="flex items-center space-x-2 border p-3 rounded-md">
|
|
<Switch
|
|
id="feat-cards"
|
|
checked={config.features.cards}
|
|
onCheckedChange={(c) => setConfig({ ...config, features: { ...config.features, cards: c } })}
|
|
/>
|
|
<Label htmlFor="feat-cards">Cards</Label>
|
|
</div>
|
|
<div className="flex items-center space-x-2 border p-3 rounded-md">
|
|
<Switch
|
|
id="feat-emi"
|
|
checked={config.features.emi}
|
|
onCheckedChange={(c) => setConfig({ ...config, features: { ...config.features, emi: c } })}
|
|
/>
|
|
<Label htmlFor="feat-emi">EMI</Label>
|
|
</div>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="webhooks" className="space-y-4 pt-4">
|
|
<div className="space-y-2">
|
|
<Label>Webhook URL</Label>
|
|
<div className="flex items-center gap-2">
|
|
<Input readOnly value={`https://api.eventify.com/webhooks/${provider}`} />
|
|
<Button variant="outline" size="icon">
|
|
<Copy className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground">Add this URL to your provider dashboard.</p>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Webhook Secret</Label>
|
|
<Input
|
|
value={config.webhookSecret || ''}
|
|
onChange={(e) => setConfig({ ...config, webhookSecret: e.target.value })}
|
|
placeholder="whsec_..."
|
|
/>
|
|
</div>
|
|
</TabsContent>
|
|
</Tabs>
|
|
</div>
|
|
|
|
<SheetFooter>
|
|
<Button onClick={handleSave} disabled={loading} className="w-full sm:w-auto">
|
|
{loading ? 'Saving...' : 'Save Configuration'}
|
|
</Button>
|
|
</SheetFooter>
|
|
</SheetContent>
|
|
</Sheet>
|
|
);
|
|
}
|