import { NextRequest, NextResponse } from 'next/server' import crypto from 'crypto' interface PayUBalanceFailureResponse { status: string txnid: string amount: string productinfo: string firstname: string email: string hash: string key: string error?: string error_Message?: string [key: string]: string | undefined } /** * Balance Addition Failure Handler * Processes failed "Add Balance" payments from PayU gateway */ export async function POST(request: NextRequest) { try { const formData = await request.formData() const payuResponse: Partial = {} // Extract all PayU response parameters for (const [key, value] of formData.entries()) { payuResponse[key] = value.toString() } const { status, txnid, amount, productinfo, firstname, email, hash, key: merchantKey, error, error_Message } = payuResponse // Log failure details for debugging console.log(`Balance addition failed - Transaction: ${txnid}, Status: ${status}, Error: ${error || error_Message || 'Unknown'}`) // Validate required parameters if (!txnid) { console.error('Missing transaction ID in balance failure response') return NextResponse.redirect(new URL('/payment/failed?error=invalid-response&type=balance', request.url)) } // Verify PayU hash if provided (for security) if (hash && merchantKey && status && amount) { const merchantSalt = process.env.PAYU_MERCHANT_SALT || 'test-salt' const expectedHashString = `${merchantSalt}|${status}|||||||||||${email}|${firstname}|${productinfo}|${amount}|${txnid}|${merchantKey}` const expectedHash = crypto.createHash('sha512').update(expectedHashString).digest('hex').toLowerCase() if (hash.toLowerCase() !== expectedHash) { console.error(`Hash mismatch in balance failure response for transaction: ${txnid}`) } } try { // TODO: Update database with failed balance transaction // In real implementation: // await updateAddBalanceStatus(txnid, 'failed', error || error_Message) // Mock database update for demo await mockUpdateBalanceFailure(txnid as string, error || error_Message || 'Payment failed') // Determine failure reason for user-friendly message let failureReason = 'unknown' let userMessage = 'Your balance addition failed. Please try again.' if (error_Message?.toLowerCase().includes('insufficient')) { failureReason = 'insufficient-funds' userMessage = 'Insufficient funds in your payment method.' } else if (error_Message?.toLowerCase().includes('declined')) { failureReason = 'card-declined' userMessage = 'Your card was declined. Please try a different payment method.' } else if (error_Message?.toLowerCase().includes('timeout')) { failureReason = 'timeout' userMessage = 'Payment timed out. Please try again.' } else if (error_Message?.toLowerCase().includes('cancelled')) { failureReason = 'user-cancelled' userMessage = 'Payment was cancelled.' } else if (error_Message?.toLowerCase().includes('invalid')) { failureReason = 'invalid-details' userMessage = 'Invalid payment details. Please check and try again.' } // Redirect to profile page with failure message const failureUrl = new URL('/profile', request.url) failureUrl.searchParams.set('payment', 'failed') failureUrl.searchParams.set('type', 'balance') failureUrl.searchParams.set('reason', failureReason) failureUrl.searchParams.set('txn', txnid as string) failureUrl.searchParams.set('message', encodeURIComponent(userMessage)) if (amount) { failureUrl.searchParams.set('amount', amount as string) } return NextResponse.redirect(failureUrl) } catch (dbError) { console.error('Database update error during balance failure handling:', dbError) // Continue to failure page even if DB update fails const failureUrl = new URL('/profile', request.url) failureUrl.searchParams.set('payment', 'failed') failureUrl.searchParams.set('type', 'balance') failureUrl.searchParams.set('error', 'db-error') failureUrl.searchParams.set('txn', txnid as string) return NextResponse.redirect(failureUrl) } } catch (error) { console.error('Balance failure handler error:', error) return NextResponse.redirect(new URL('/profile?payment=failed&type=balance&error=processing-error', request.url)) } } // Mock function for demonstration async function mockUpdateBalanceFailure(txnid: string, errorMessage: string) { console.log(`Mock DB Update: Balance Transaction ${txnid} failed`) console.log(`Failure reason: ${errorMessage}`) // Mock: Update add_balance_history status const historyUpdate = { transaction_id: txnid, status: 'failed', error_message: errorMessage, updated_at: new Date(), retry_count: 1, // Could track retry attempts payment_gateway_response: { status: 'failed', error: errorMessage, failed_at: new Date() } } // Mock: Could also track failed attempt statistics const analytics = { failed_balance_additions: 1, common_failure_reasons: { [errorMessage]: 1 } } console.log('Mock: Balance failure recorded for analysis') // Mock delay await new Promise(resolve => setTimeout(resolve, 100)) return { historyUpdate, analytics } }