116 lines
4.0 KiB
TypeScript
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 }
|
|
)
|
|
}
|
|
}
|
|
|