Files
2025-12-21 08:43:43 +01:00

116 lines
4.0 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import pool from '@/lib/db'
import { getNowPaymentsConfig } from '@/lib/nowpayments'
// POST /api/payments/cleanup-expired - Clean up expired pending orders
// This endpoint should be called periodically (e.g., via cron job every minute)
export async function POST(request: NextRequest) {
try {
// Optional: Add authentication/authorization check here
// For security, you might want to require an API key or admin authentication
const authHeader = request.headers.get('authorization')
const expectedToken = process.env.CLEANUP_API_TOKEN
if (expectedToken && authHeader !== `Bearer ${expectedToken}`) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
)
}
// Find all expired pending orders
const [expiredRows] = await pool.execute(
'SELECT * FROM pending_orders WHERE expires_at < NOW()',
)
const expiredOrders = expiredRows as any[]
if (expiredOrders.length === 0) {
return NextResponse.json({
message: 'No expired orders to clean up',
cleaned: 0,
})
}
const nowPaymentsConfig = getNowPaymentsConfig()
let cleanedCount = 0
const errors: string[] = []
// Process each expired order
for (const order of expiredOrders) {
try {
// Try to cancel the NOWPayments invoice if it still exists
// Note: NOWPayments may not have a direct cancel endpoint, but we can try
// to check the status and handle accordingly
try {
const statusResponse = await fetch(
`${nowPaymentsConfig.baseUrl}/v1/payment/${order.payment_id}`,
{
method: 'GET',
headers: {
'x-api-key': nowPaymentsConfig.apiKey,
'Content-Type': 'application/json',
},
}
)
if (statusResponse.ok) {
const paymentStatus = await statusResponse.json()
// If payment is still pending/waiting, we can consider it expired
// NOWPayments will handle the expiration on their end based on invoice_timeout
console.log(`Payment ${order.payment_id} status:`, paymentStatus.payment_status)
}
} catch (apiError) {
// Invoice might already be expired/cancelled on NOWPayments side
console.log(`Could not check status for payment ${order.payment_id}:`, apiError)
}
// Delete the expired pending order
await pool.execute('DELETE FROM pending_orders WHERE id = ?', [order.id])
cleanedCount++
console.log(`Cleaned up expired pending order ${order.id} (payment_id: ${order.payment_id})`)
} catch (error) {
const errorMsg = `Error cleaning up order ${order.id}: ${error}`
console.error(errorMsg)
errors.push(errorMsg)
}
}
return NextResponse.json({
message: `Cleaned up ${cleanedCount} expired pending orders`,
cleaned: cleanedCount,
total: expiredOrders.length,
errors: errors.length > 0 ? errors : undefined,
})
} catch (error) {
console.error('Error in cleanup endpoint:', error)
return NextResponse.json(
{ error: 'Failed to clean up expired orders' },
{ status: 500 }
)
}
}
// GET /api/payments/cleanup-expired - Get status of expired orders (for monitoring)
export async function GET() {
try {
const [expiredRows] = await pool.execute(
'SELECT COUNT(*) as count FROM pending_orders WHERE expires_at < NOW()',
)
const result = expiredRows as any[]
const expiredCount = result[0]?.count || 0
return NextResponse.json({
expired_orders_count: expiredCount,
message: expiredCount > 0
? `There are ${expiredCount} expired pending orders that need cleanup`
: 'No expired orders',
})
} catch (error) {
console.error('Error checking expired orders:', error)
return NextResponse.json(
{ error: 'Failed to check expired orders' },
{ status: 500 }
)
}
}