initial commit
This commit is contained in:
147
app/api/balance/failure/route.ts
Normal file
147
app/api/balance/failure/route.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
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<PayUBalanceFailureResponse> = {}
|
||||
|
||||
// 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 }
|
||||
}
|
||||
Reference in New Issue
Block a user