import { NextRequest, NextResponse } from 'next/server' import { cookies } from 'next/headers' import pool from '@/lib/db' import { getNowPaymentsConfig } from '@/lib/nowpayments' // POST /api/payments/create-invoice - Create a NOWPayments invoice export async function POST(request: NextRequest) { try { // Get buyer_id from session cookie const cookieStore = await cookies() const buyerIdCookie = cookieStore.get('buyer_id')?.value if (!buyerIdCookie) { return NextResponse.json( { error: 'Authentication required' }, { status: 401 } ) } const buyer_id = parseInt(buyerIdCookie, 10) const body = await request.json() const { drop_id, size } = body // Validate required fields if (!drop_id || !size) { return NextResponse.json( { error: 'Missing required fields: drop_id, size' }, { status: 400 } ) } // Get drop details const [dropRows] = await pool.execute( 'SELECT * FROM drops WHERE id = ?', [drop_id] ) const drops = dropRows as any[] if (drops.length === 0) { return NextResponse.json( { error: 'Drop not found' }, { status: 404 } ) } const drop = drops[0] // Check inventory availability (but don't reserve yet - will reserve when payment confirmed) const [salesRows] = await pool.execute( 'SELECT COALESCE(SUM(size), 0) as total_fill FROM sales WHERE drop_id = ?', [drop_id] ) const salesData = salesRows as any[] const currentFill = salesData[0]?.total_fill || 0 // Convert fill to the drop's unit for comparison let currentFillInDropUnit = currentFill let sizeInDropUnit = size if (drop.unit === 'kg') { currentFillInDropUnit = currentFill / 1000 sizeInDropUnit = size / 1000 } // Check if there's enough remaining inventory const remaining = drop.size - currentFillInDropUnit if (sizeInDropUnit > remaining) { return NextResponse.json( { error: 'Not enough inventory remaining' }, { status: 400 } ) } // Calculate price let priceAmount = 0 if (drop.unit === 'kg') { priceAmount = (size / 1000) * drop.ppu } else { priceAmount = size * drop.ppu } // Round to 2 decimal places priceAmount = Math.round(priceAmount * 100) / 100 // Generate order ID const orderId = `SALE-${Date.now()}-${drop_id}-${buyer_id}` // Get base URL for success/cancel redirects const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || request.headers.get('origin') || 'http://localhost:3420' // Get IPN callback URL from environment variable const ipnCallbackUrl = process.env.IPN_CALLBACK_URL if (!ipnCallbackUrl) { return NextResponse.json( { error: 'IPN_CALLBACK_URL environment variable is required' }, { status: 500 } ) } // Get NOWPayments config (testnet or production) const nowPaymentsConfig = getNowPaymentsConfig() // Create NOWPayments invoice const nowPaymentsResponse = await fetch(`${nowPaymentsConfig.baseUrl}/v1/invoice`, { method: 'POST', headers: { 'x-api-key': nowPaymentsConfig.apiKey, 'Content-Type': 'application/json', }, body: JSON.stringify({ price_amount: priceAmount, price_currency: nowPaymentsConfig.currency, order_id: orderId, order_description: `${drop.item} - ${size}g`, ipn_callback_url: ipnCallbackUrl, success_url: `${baseUrl}/?payment=success&order_id=${orderId}`, cancel_url: `${baseUrl}/?payment=cancelled&order_id=${orderId}`, }), }) if (!nowPaymentsResponse.ok) { const error = await nowPaymentsResponse.json() console.error('NOWPayments error:', error) return NextResponse.json( { error: 'Failed to create payment invoice', details: error }, { status: 500 } ) } const invoice = await nowPaymentsResponse.json() // Store pending order (will create sale when payment is confirmed) const [result] = await pool.execute( 'INSERT INTO pending_orders (payment_id, order_id, drop_id, buyer_id, size, price_amount, price_currency) VALUES (?, ?, ?, ?, ?, ?, ?)', [invoice.id, orderId, drop_id, buyer_id, size, priceAmount, nowPaymentsConfig.currency] ) // Return invoice URL - sale will be created when payment is confirmed via IPN return NextResponse.json({ invoice_url: invoice.invoice_url, payment_id: invoice.id, order_id: orderId, }, { status: 201 }) } catch (error) { console.error('Error creating invoice:', error) return NextResponse.json( { error: 'Failed to create invoice' }, { status: 500 } ) } }