'use client'; import React, { useState, useEffect } from 'react'; import Link from 'next/link'; import { Plus, Search, Building2, Copy, Check, Pencil, Trash2, ExternalLink, Globe, LayoutGrid, List, AlertCircle, X, DollarSign, Wallet, ArrowRight, ShieldCheck, Loader2, Coins, History } from 'lucide-react'; export default function MerchantsPage() { const [merchants, setMerchants] = useState([]); const [isLoading, setIsLoading] = useState(true); const [treasuryData, setTreasuryData] = useState(null); const [isLoadingTreasury, setIsLoadingTreasury] = useState(true); const [copiedId, setCopiedId] = useState(null); const [showEditModal, setShowEditModal] = useState(false); const [showPayoutModal, setShowPayoutModal] = useState(false); const [selectedMerchant, setSelectedMerchant] = useState(null); const [payoutAmount, setPayoutAmount] = useState(''); const [payoutNetwork, setPayoutNetwork] = useState('POLYGON'); const [payoutCurrency, setPayoutCurrency] = useState('USDT'); const [merchantBalances, setMerchantBalances] = useState([]); const [merchantFeePercent, setMerchantFeePercent] = useState(1); const [isSubmitting, setIsSubmitting] = useState(false); const [editForm, setEditForm] = useState({ name: '', webhook_url: '', fee_percent: '', payout_address: '', payout_addresses: { EVM: '', SOLANA: '', TRON: '', BITCOIN: '' } as Record }); const [isUpdating, setIsUpdating] = useState(false); useEffect(() => { fetchMerchants(); fetchTreasury(); }, []); const fetchTreasury = async () => { setIsLoadingTreasury(true); try { const res = await fetch('/api/admin/treasury/balances'); const data = await res.json(); if (res.ok) setTreasuryData(data.balances); } catch (e) { console.error(e); } finally { setIsLoadingTreasury(false); } }; const fetchMerchants = async () => { setIsLoading(true); try { const response = await fetch('/api/merchants'); const data = await response.json(); if (!response.ok) throw new Error(data.error); setMerchants(data); } catch (err) { console.error('Fetch error:', err); } finally { setIsLoading(false); } }; const copyToClipboard = (text: string, id: string) => { navigator.clipboard.writeText(text); setCopiedId(id); setTimeout(() => setCopiedId(null), 2000); }; const handleEditClick = (merchant: any) => { setSelectedMerchant(merchant); setEditForm({ name: merchant.name, webhook_url: merchant.webhook_url || '', fee_percent: merchant.fee_percent?.toString() || '1.0', payout_address: merchant.payout_address || '', payout_addresses: merchant.payout_addresses || { EVM: '', SOLANA: '', TRON: '', BITCOIN: '' } }); setShowEditModal(true); }; const handlePayoutClick = async (merchant: any) => { setSelectedMerchant(merchant); setPayoutAmount(''); setShowPayoutModal(true); // Fetch merchant's per-network balances try { const res = await fetch(`/api/merchants/${merchant.id}/balances`); const data = await res.json(); setMerchantBalances(data.balances || []); setMerchantFeePercent(data.feePercent || 1); } catch (e) { setMerchantBalances([]); } }; const handleUpdateMerchant = async (e: React.FormEvent) => { e.preventDefault(); setIsUpdating(true); try { const response = await fetch(`/api/merchants/${selectedMerchant.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: editForm.name, webhook_url: editForm.webhook_url, fee_percent: parseFloat(editForm.fee_percent), payout_address: editForm.payout_address, payout_addresses: editForm.payout_addresses }) }); if (!response.ok) throw new Error('Güncelleme başarısız.'); await fetchMerchants(); setShowEditModal(false); setSelectedMerchant(null); } catch (err: any) { alert(err.message); } finally { setIsUpdating(false); } }; const handleDeleteMerchant = async (id: string) => { if (!confirm('Bu firmayı silmek istediğinize emin misiniz? Bu işlem geri alınamaz.')) return; try { const response = await fetch(`/api/merchants/${id}`, { method: 'DELETE' }); if (!response.ok) throw new Error('Silme işlemi başarısız.'); await fetchMerchants(); } catch (err: any) { alert(err.message); } }; const handleProcessPayout = async (e: React.FormEvent) => { e.preventDefault(); if (!selectedMerchant || parseFloat(payoutAmount) <= 0) return; setIsSubmitting(true); try { const response = await fetch('/api/admin/payouts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ merchantId: selectedMerchant.id, amount: parseFloat(payoutAmount), network: payoutNetwork, currency: payoutCurrency }) }); const data = await response.json(); if (!response.ok) throw new Error(data.error || 'Ödeme işlemi başarısız.'); alert('Ödeme başarıyla gönderildi! TX Hash: ' + data.txHash); await fetchMerchants(); setShowPayoutModal(false); setSelectedMerchant(null); setPayoutAmount(''); } catch (err: any) { alert(err.message); } finally { setIsSubmitting(false); } }; return (
{/* Header */}

Firmalar (Merchants)

Ödeme alan tüm işletmeler

Yeni Firma Ekle
{/* Merchant Cards Grid */}
{isLoading ? (
) : merchants.map((m) => { const identifier = m.short_id || m.id; const paymentLink = `https://p2cgateway.store/checkout?merchant_id=${identifier}&amount=0`; return (
{m.name.substring(0, 1).toUpperCase()}

{m.name}

ID: {identifier}
Fee: %{m.fee_percent || '1.0'}
{/* Balance Section */}

İçerideki Bakiye

{new Intl.NumberFormat('tr-TR', { style: 'currency', currency: 'TRY' }).format(Number(m.available_balance || 0))}

Toplam Ödenen

{new Intl.NumberFormat('tr-TR', { style: 'currency', currency: 'TRY' }).format(Number(m.withdrawn_balance || 0))}

{m.balance_breakdown && m.balance_breakdown.length > 0 && (
{m.balance_breakdown.map((b: any, i: number) => (
{Number(b.amount).toFixed(4)} {b.token}
))}
)}
{/* API Key Section */}
{m.api_key || '••••••••••••••••'}
{/* Webhook Section */}
{m.webhook_url || 'Ayarlanmamış'}
{/* Payment Link Section */}

Yukarıdaki linki müşterinize ileterek manuel ödeme alabilir veya sitenize gömebilirsiniz.

Aktif
Tüm İşlemleri Gör Firma Paneli
); })} {!isLoading && merchants.length === 0 && (

Henüz firma bulunmuyor

İlk firmanızı ekleyerek ödeme almaya başlayın.

Firma Ekleyerek Başlayın
)}
{/* Edit Modal */} {showEditModal && (

Firmayı Düzenle

Firma bilgilerini güncelle

setEditForm({ ...editForm, name: e.target.value })} placeholder="Örn: Ayris Teknoloji" className="w-full pl-14 pr-6 py-4 bg-gray-50 border-2 border-transparent focus:border-blue-500 focus:bg-white rounded-[24px] outline-none transition-all font-bold text-gray-900 placeholder:text-gray-300" />
%
setEditForm({ ...editForm, fee_percent: e.target.value })} placeholder="1.0" className="w-full pl-6 pr-12 py-4 bg-gray-50 border-2 border-transparent focus:border-blue-500 focus:bg-white rounded-[24px] outline-none transition-all font-bold text-gray-900 placeholder:text-gray-300" />

Kesinti oranı. Boş bırakılırsa sistem varsayılanı kullanılır.

setEditForm({ ...editForm, webhook_url: e.target.value })} placeholder="https://siteniz.com/webhook" className="w-full pl-14 pr-6 py-4 bg-gray-50 border-2 border-transparent focus:border-blue-500 focus:bg-white rounded-[24px] outline-none transition-all font-bold text-gray-900 placeholder:text-gray-300" />

Her ağ için payout yapılacak cüzdan adresini girin.

{[ { key: 'EVM', label: 'EVM (Polygon/BSC/ETH)', placeholder: '0x...', icon: '🟣' }, { key: 'SOLANA', label: 'Solana', placeholder: '5pLH...veya base58 adres', icon: '🟢' }, { key: 'TRON', label: 'TRON', placeholder: 'T...', icon: '🔴' }, { key: 'BITCOIN', label: 'Bitcoin', placeholder: 'bc1q...', icon: '🟠' } ].map(net => (
{net.icon} setEditForm({ ...editForm, payout_addresses: { ...editForm.payout_addresses, [net.key]: e.target.value } })} className="w-full pl-12 pr-6 py-3 bg-gray-50 border-2 border-transparent focus:border-blue-500 focus:bg-white rounded-2xl outline-none transition-all font-mono text-xs text-gray-900 placeholder:text-gray-300" placeholder={`${net.label}: ${net.placeholder}`} />
))}
)} {/* Payout Modal */} {showPayoutModal && (

Ödeme Gönder

Treasury'den Merchant Cüzdanına Transfer

{(() => { const bal = merchantBalances.find(b => b.network === payoutNetwork && b.token === payoutCurrency); const totalGross = bal ? bal.totalGross : 0; const totalNet = bal ? bal.balance : 0; const totalFee = totalGross - totalNet; const withdrawn = bal ? bal.withdrawn : 0; const available = totalNet - withdrawn; return (

Alıcı Firma

{selectedMerchant?.name}

Top. Brüt (Hazine)

{totalGross.toFixed(4)} {payoutCurrency}

Platform Fee (%{merchantFeePercent})

-{totalFee.toFixed(4)} {payoutCurrency}

Net Hak Ediş (Çekilebilir)

{available.toFixed(6)}

{payoutCurrency}

{withdrawn > 0 && (

Daha önce {withdrawn.toFixed(4)} {payoutCurrency} çekildi.

)}
{merchantBalances.length > 0 && (
{merchantBalances.filter(b => b.available > 0).map((b, i) => ( { setPayoutNetwork(b.network); setPayoutCurrency(b.token); }} > {b.network}/{b.token}: {b.available.toFixed(4)} ))}
)}
); })()}
{/* Simplified Selection Indicator */}

Seçili Ödeme Hattı

{payoutNetwork === 'SOLANA' ? '🟢' : payoutNetwork === 'TRON' ? '🔴' : payoutNetwork === 'BITCOIN' ? '🟠' : '🟣'}

{payoutNetwork}

{payoutCurrency}

Yukarıdaki chiplerden seçin
{(() => { const bal = merchantBalances.find(b => b.network === payoutNetwork && b.token === payoutCurrency); const maxAvailable = bal ? bal.available : 0; const isOverspend = parseFloat(payoutAmount || '0') > maxAvailable; return ( <>
setPayoutAmount(e.target.value)} className={`w-full pl-5 pr-24 py-4 bg-gray-50 border rounded-2xl focus:ring-2 focus:ring-emerald-500 transition outline-none font-black text-lg ${isOverspend ? 'border-red-400 bg-red-50' : 'border-gray-100'}`} placeholder="0.00" required />
{maxAvailable > 0 && ( )} {payoutCurrency}
{isOverspend && (

⚠️ Girilen miktar firma bakiyesini ({maxAvailable.toFixed(6)} {payoutCurrency}) aşıyor!

)} ); })()}

Treasury cüzdanından düşülüp merchant adresine kripto olarak iletilecek.

Bu işlem kalıcıdır. Payout adresi ({payoutNetwork}): {(() => { const netKey = ['POLYGON', 'BSC', 'ETH'].includes(payoutNetwork) ? 'EVM' : payoutNetwork; const addr = selectedMerchant?.payout_addresses?.[netKey]; return addr || ADRES EKSİK! Lütfen firma ayarlarından {netKey} adresini girin.; })()}

)}
); }