import { NextRequest, NextResponse } from 'next/server'; import { db } from '@/lib/db'; import { CryptoEngine } from '@/lib/crypto-engine'; export async function POST(req: NextRequest) { try { const { merchantId, amount, network, currency } = await req.json(); if (!merchantId || !amount || !network) { return NextResponse.json({ error: 'Missing required fields' }, { status: 400 }); } // 1. Fetch Merchant details const merchantRes = await db.query('SELECT * FROM merchants WHERE id = $1', [merchantId]); const merchant = merchantRes.rows[0]; if (!merchant) { return NextResponse.json({ error: 'Merchant not found' }, { status: 404 }); } // Resolve per-network payout address const netKey = ['POLYGON', 'BSC', 'ETH'].includes(network) ? 'EVM' : network; const payoutAddresses = merchant.payout_addresses || {}; const destinationAddress = payoutAddresses[netKey] || merchant.payout_address; if (!destinationAddress) { return NextResponse.json({ error: `Merchant payout address for ${netKey} is not set. Please configure it in merchant settings.` }, { status: 400 }); } const availableBalance = parseFloat(merchant.available_balance || '0'); const payoutAmount = parseFloat(amount); if (availableBalance < payoutAmount) { return NextResponse.json({ error: 'Insufficient balance' }, { status: 400 }); } // 2. Get Treasury Private Key based on network const treasuryKeys: Record = { 'POLYGON': process.env.TREASURY_EVM_KEY, 'BSC': process.env.TREASURY_EVM_KEY, 'ETH': process.env.TREASURY_EVM_KEY, 'SOLANA': process.env.TREASURY_SOL_KEY, 'TRON': process.env.TREASURY_TRON_KEY, 'BITCOIN': process.env.TREASURY_BTC_KEY }; const privateKey = treasuryKeys[network]; if (!privateKey) { return NextResponse.json({ error: `Treasury private key for ${network} is not configured in .env` }, { status: 500 }); } // 3. Check merchant's crypto balance for this network/token const selectedCurrency = currency || 'SOL'; const balRes = await db.query( 'SELECT balance, withdrawn FROM merchant_balances WHERE merchant_id = $1 AND network = $2 AND token = $3', [merchantId, network, selectedCurrency] ); const cryptoBalance = balRes.rows[0] ? parseFloat(balRes.rows[0].balance) : 0; const cryptoWithdrawn = balRes.rows[0] ? parseFloat(balRes.rows[0].withdrawn) : 0; const cryptoAvailable = cryptoBalance - cryptoWithdrawn; if (cryptoAvailable < payoutAmount) { return NextResponse.json({ error: `Yetersiz ${selectedCurrency} bakiyesi. Çekilebilir: ${cryptoAvailable.toFixed(6)} ${selectedCurrency}, talep edilen: ${payoutAmount} ${selectedCurrency}` }, { status: 400 }); } // 4. Execute Transfer const engine = new CryptoEngine(network); const transfer = await engine.sendPayout( privateKey, destinationAddress, amount.toString(), selectedCurrency ); if (!transfer.success) { return NextResponse.json({ error: transfer.error || 'Blockchain transfer failed' }, { status: 500 }); } // 5. Update Database await db.query('BEGIN'); try { // Deduct from merchant's crypto balance await db.query(` UPDATE merchant_balances SET withdrawn = withdrawn + $1 WHERE merchant_id = $2 AND network = $3 AND token = $4 `, [payoutAmount, merchantId, network, selectedCurrency]); // Also update legacy TRY balance (best effort price conversion) try { const coinIdMap: Record = { 'SOL': 'solana', 'USDC': 'usd-coin', 'USDT': 'tether', 'TRX': 'tron', 'BTC': 'bitcoin', 'ETH': 'ethereum', 'MATIC': 'matic-network', 'BNB': 'binancecoin' }; const coinId = coinIdMap[selectedCurrency] || 'solana'; const priceRes = await fetch(`https://api.coingecko.com/api/v3/simple/price?ids=${coinId}&vs_currencies=try`); const priceData = await priceRes.json(); const tryEquivalent = payoutAmount * (priceData[coinId]?.try || 0); if (tryEquivalent > 0) { await db.query(` UPDATE merchants SET available_balance = GREATEST(0, available_balance - $1), withdrawn_balance = withdrawn_balance + $1 WHERE id = $2 `, [tryEquivalent, merchantId]); } } catch (e) { console.warn('[Payout] TRY balance update skipped (price fetch failed)'); } // Log the payout await db.query(` INSERT INTO payouts (merchant_id, amount, currency, network, destination_address, tx_hash, status) VALUES ($1, $2, $3, $4, $5, $6, $7) `, [ merchantId, payoutAmount, selectedCurrency, network, destinationAddress, transfer.txHash, 'succeeded' ]); await db.query('COMMIT'); console.log(`[Payout] ✅ Sent ${payoutAmount} ${selectedCurrency} on ${network} to ${destinationAddress}`); } catch (dbErr) { await db.query('ROLLBACK'); throw dbErr; } return NextResponse.json({ success: true, txHash: transfer.txHash, message: 'Payout processed successfully' }); } catch (err: any) { console.error('Payout Error:', err); return NextResponse.json({ error: err.message }, { status: 500 }); } } export async function GET(req: NextRequest) { try { const { searchParams } = new URL(req.url); const merchantId = searchParams.get('merchantId'); let query = 'SELECT * FROM payouts ORDER BY created_at DESC'; let params: any[] = []; if (merchantId) { query = 'SELECT * FROM payouts WHERE merchant_id = $1 ORDER BY created_at DESC'; params = [merchantId]; } const result = await db.query(query, params); return NextResponse.json(result.rows); } catch (err: any) { return NextResponse.json({ error: err.message }, { status: 500 }); } }