diff --git a/app/merchant/[id]/(dashboard)/integration/page.tsx b/app/merchant/[id]/(dashboard)/integration/page.tsx index ef2917d..de0bdc8 100644 --- a/app/merchant/[id]/(dashboard)/integration/page.tsx +++ b/app/merchant/[id]/(dashboard)/integration/page.tsx @@ -13,6 +13,7 @@ import { } from 'lucide-react'; import { cookies } from 'next/headers'; import { redirect } from 'next/navigation'; +import ApiKeyVisibilityToggle from '@/components/merchant/ApiKeyVisibilityToggle'; async function getMerchant(identifier: string) { const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(identifier); @@ -116,54 +117,82 @@ export default async function MerchantIntegrationPage(props: {
- +
- {merchant.id} - + {merchant.id}
- -
- - {merchant.api_key.substring(0, 8)}•••••••••••••••••••••••• - - -
+ +
- {/* Webhook Settings */} -
-
-
- + {/* Webhook & Payout Addresses */} +
+ {/* Webhook Settings */} +
+
+
+ +
+
+

Webhooks

+

Anlık Bildirimler

+
-
-

Olay Bildirimleri (Webhooks)

-

Ödeme sonuçlarını anlık olarak sunucunuzda karşılayın

+ +
+
+
+ URL + + {merchant.webhook_url ? 'AKTİF' : 'TANIMSZ'} + +
+
+ + {merchant.webhook_url || 'https://siteniz.com/callback'} + +
+
-
-

- İşlem tamamlandığında sistemimiz belirttiğiniz URL'ye POST isteği gönderir. Bu isteğin içerisinde işlemin tüm detayları yer alır. -

+ {/* Payout Addresses */} +
+
+
+ +
+
+

Hak Ediş Adresleriniz

+

Ödemeleriniz bu adreslere iletilir

+
+
-
-
- Webhook URL - - {merchant.webhook_url ? 'HİZMETE HAZIR' : 'HENÜZ TANIMLANMAMIŞ'} - -
-
- - {merchant.webhook_url || 'https://siteniz.com/api/payment-callback'} - -
+
+ {['EVM', 'SOLANA', 'TRON', 'BITCOIN'].map((net) => { + const addr = merchant.payout_addresses?.[net] || (net === 'EVM' ? merchant.payout_address : null); + return ( +
+
+
+ {net.slice(0, 1)} +
+
+

{net}

+

+ {addr || 'TANIMLANMAMIŞ'} +

+
+
+ {addr && } +
+ ); + })}
diff --git a/app/merchant/[id]/(dashboard)/page.tsx b/app/merchant/[id]/(dashboard)/page.tsx index 5981bfd..7e8b427 100644 --- a/app/merchant/[id]/(dashboard)/page.tsx +++ b/app/merchant/[id]/(dashboard)/page.tsx @@ -8,7 +8,8 @@ import { CheckCircle2, Calendar, ArrowUpRight, - Search + Search, + ShieldCheck } from 'lucide-react'; import { format } from 'date-fns'; import { tr } from 'date-fns/locale'; @@ -65,6 +66,17 @@ async function getMerchantData(identifier: string) { } }); + // Fetch merchant balances + const bResult = await db.query( + 'SELECT network, token, balance, withdrawn FROM merchant_balances WHERE merchant_id = $1', + [id] + ); + const balances = bResult.rows.map(r => ({ + network: r.network, + token: r.token, + amount: parseFloat(r.balance) - parseFloat(r.withdrawn) + })); + return { merchant, transactions, @@ -72,7 +84,8 @@ async function getMerchantData(identifier: string) { successfulCount, successRate, totalCount, - chartData + chartData, + balances }; } @@ -105,7 +118,7 @@ export default async function MerchantDashboardPage(props: { redirect(`/merchant/${identifier}/login`); } - const { merchant, transactions, totalRevenue, successfulCount, successRate, totalCount, chartData } = data; + const { merchant, transactions, totalRevenue, successfulCount, successRate, totalCount, chartData, balances } = data; const recentTransactions = transactions.slice(0, 8); return ( @@ -118,7 +131,12 @@ export default async function MerchantDashboardPage(props: {

{merchant.name}

-

Hoş Geldiniz, İşlemlerinizi Buradan Takip Edebilirsiniz

+
+

Firma Yönetim Paneli

+ + Komisyon: %{merchant.fee_percent || '1.0'} + +
@@ -132,34 +150,80 @@ export default async function MerchantDashboardPage(props: {
- {/* On-chain Vault Section */} -
-
-
- -
-
-

EVM Kasanız (Polygon/BSC/ETH)

-
- - {merchant.evm_vault_address || 'Henüz Oluşturulmadı'} - -
Copy
+ {/* Balances & Vaults Section */} +
+ {/* Crypto Balances */} +
+
+
+
+ +
+

Mevcut Bakiyeleriniz

+ +
+ {balances && balances.length > 0 ? balances.map((b: any, i: number) => ( +
+
+
+
+

{b.network}

+

{b.token}

+
+
+
+

{b.amount.toFixed(4)}

+

Çekilebilir

+
+
+ )) : ( +
+

Henüz birikmiş bakiye yok

+
+ )} +
+
+ +
+

Dönüşüm Özeti

+

+ {totalRevenue.toLocaleString('tr-TR', { minimumFractionDigits: 2 })} +

-
-
- + {/* Vault Addresses */} +
+
+
+
+ +
+

EVM Kasanız

+

Polygon / BSC / Ethereum Ödemeleri İçin

+
+
+

Adres

+

+ {merchant.evm_vault_address || 'Henüz Oluşturulmadı'} +

+
-
-

Solana Kasanız

-
- - {merchant.sol_vault_address || 'Henüz Oluşturulmadı'} - -
Copy
+ +
+
+
+ +
+

Solana Kasanız

+

Solana / SPL Token Ödemeleri İçin

+
+
+

Adres

+

+ {merchant.sol_vault_address || 'Henüz Oluşturulmadı'} +

diff --git a/app/merchant/[id]/(dashboard)/payouts/page.tsx b/app/merchant/[id]/(dashboard)/payouts/page.tsx new file mode 100644 index 0000000..df9d31f --- /dev/null +++ b/app/merchant/[id]/(dashboard)/payouts/page.tsx @@ -0,0 +1,181 @@ + +import React from 'react'; +import { db } from '@/lib/db'; +import { + Wallet, + ExternalLink, + Clock, + ArrowUpRight, + Search +} from 'lucide-react'; +import { format } from 'date-fns'; +import { tr } from 'date-fns/locale'; +import { cookies } from 'next/headers'; +import { redirect } from 'next/navigation'; +import Link from 'next/link'; + +async function getMerchantPayouts(identifier: string) { + const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(identifier); + let resolvedId = identifier; + + if (!isUUID) { + const result = await db.query('SELECT id FROM merchants WHERE short_id = $1 LIMIT 1', [identifier]); + const merchant = result.rows[0]; + if (merchant) { + resolvedId = merchant.id; + } + } + + try { + const { rows } = await db.query(` + SELECT * FROM payouts + WHERE merchant_id = $1 + ORDER BY created_at DESC + `, [resolvedId]); + return { payouts: rows, merchantId: resolvedId }; + } catch (error) { + console.error('Merchant payout history fetch error:', error); + return { payouts: [], merchantId: resolvedId }; + } +} + +export default async function MerchantPayoutsPage(props: { + params: Promise<{ id: string }>; +}) { + const resolvedParams = await props.params; + const identifier = resolvedParams.id; + const { payouts, merchantId } = await getMerchantPayouts(identifier); + const cookieStore = await cookies(); + + if (!cookieStore.get(`merchant_auth_${merchantId}`)) { + redirect(`/merchant/${identifier}/login`); + } + + return ( +
+ {/* Header Area */} +
+
+
+ +
+
+

Ödemelerim

+

Sistemden cüzdanınıza yapılan transfer geçmişi

+
+
+
+ + {/* Stats Overview */} +
+
+
+ +
+
+

Toplam Çekim

+

{payouts.length}

+
+
+
+
+ +
+
+

Son Çekim

+

+ {payouts.length > 0 ? format(new Date(payouts[0].created_at), 'dd MMM', { locale: tr }) : '-'} +

+
+
+
+
+ +
+
+

Durum

+

Aktif

+
+
+
+ + {/* Payouts Table */} +
+ + + + + + + + + + + + + {payouts.map((p: any) => ( + + + + + + + + + ))} + +
Miktar & VarlıkAğ (Network)Hedef AdresTarihDurumBlockchain
+
+ {Number(p.amount).toFixed(4)} + {p.currency} +
+
+ + {p.network} + + +
+ {p.destination_address.slice(0, 6)}...{p.destination_address.slice(-6)} +
+
+ {format(new Date(p.created_at), 'dd MMM yyyy, HH:mm', { locale: tr })} + + + {p.status === 'succeeded' ? 'Başarılı' : 'Hatalı'} + + + {p.tx_hash && p.tx_hash !== 'mock' ? ( + + GÖRÜNTÜLE + + + ) : ( + Kayıt Yok + )} +
+ {payouts.length === 0 && ( +
+
+ +
+
+

Henüz Ödeme Yapılmadı

+

Sistemimizden cüzdanınıza henüz bir kripto transferi gerçekleşmedi.

+
+
+ )} +
+
+ ); +} diff --git a/components/merchant/ApiKeyVisibilityToggle.tsx b/components/merchant/ApiKeyVisibilityToggle.tsx new file mode 100644 index 0000000..747588e --- /dev/null +++ b/components/merchant/ApiKeyVisibilityToggle.tsx @@ -0,0 +1,44 @@ + +'use client'; + +import React, { useState } from 'react'; +import { Copy, Check, Eye, EyeOff } from 'lucide-react'; + +interface ApiKeyVisibilityToggleProps { + apiKey: string; +} + +export default function ApiKeyVisibilityToggle({ apiKey }: ApiKeyVisibilityToggleProps) { + const [isVisible, setIsVisible] = useState(false); + const [copied, setCopied] = useState(false); + + const handleCopy = () => { + navigator.clipboard.writeText(apiKey); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( +
+ + {isVisible ? apiKey : `${apiKey.substring(0, 8)}${'•'.repeat(24)}`} + +
+ + +
+
+ ); +} diff --git a/components/merchant/MerchantSidebar.tsx b/components/merchant/MerchantSidebar.tsx index 8fcb074..61f8900 100644 --- a/components/merchant/MerchantSidebar.tsx +++ b/components/merchant/MerchantSidebar.tsx @@ -10,7 +10,8 @@ import { Terminal, Building2, ShieldCheck, - LogOut + LogOut, + Wallet } from 'lucide-react'; export default function MerchantSidebar({ merchantId }: { merchantId: string }) { @@ -34,6 +35,7 @@ export default function MerchantSidebar({ merchantId }: { merchantId: string }) const navItems = [ { label: 'Panel', icon: LayoutDashboard, href: `/merchant/${merchantId}` }, { label: 'İşlemler', icon: CreditCard, href: `/merchant/${merchantId}/transactions` }, + { label: 'Ödemeler', icon: Wallet, href: `/merchant/${merchantId}/payouts` }, { label: 'Entegrasyon', icon: Terminal, href: `/merchant/${merchantId}/integration` }, ];