initial commit
This commit is contained in:
132
app/api/balance/success/route.ts
Normal file
132
app/api/balance/success/route.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import crypto from 'crypto'
|
||||
|
||||
interface PayUBalanceResponse {
|
||||
status: string
|
||||
txnid: string
|
||||
amount: string
|
||||
productinfo: string
|
||||
firstname: string
|
||||
email: string
|
||||
hash: string
|
||||
key: string
|
||||
[key: string]: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Balance Addition Success Handler
|
||||
* Processes successful "Add Balance" payments from PayU gateway
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const formData = await request.formData()
|
||||
const payuResponse: Partial<PayUBalanceResponse> = {}
|
||||
|
||||
// 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 } = payuResponse
|
||||
|
||||
// Validate required parameters
|
||||
if (!status || !txnid || !amount || !hash) {
|
||||
console.error('Missing required PayU parameters in balance success')
|
||||
return NextResponse.redirect(new URL('/payment/failed?error=invalid-response', request.url))
|
||||
}
|
||||
|
||||
// Verify payment status
|
||||
if (status !== 'success') {
|
||||
console.log(`Balance payment failed for transaction: ${txnid}, status: ${status}`)
|
||||
return NextResponse.redirect(new URL('/payment/failed?txn=' + txnid + '&type=balance', request.url))
|
||||
}
|
||||
|
||||
// Verify PayU hash for security
|
||||
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 for balance transaction: ${txnid}`)
|
||||
return NextResponse.redirect(new URL('/payment/failed?error=hash-mismatch&type=balance', request.url))
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO: Update database with successful balance addition
|
||||
// In real implementation:
|
||||
// const siliconId = await getSiliconIdFromTransaction(txnid)
|
||||
// await updateAddBalanceStatus(txnid, 'success')
|
||||
// await incrementUserBalance(siliconId, parseFloat(amount))
|
||||
|
||||
console.log(`Balance addition successful for transaction: ${txnid}, amount: ₹${amount}`)
|
||||
|
||||
// Mock database update for demo
|
||||
const updateResult = await mockUpdateBalanceSuccess(txnid as string, parseFloat(amount as string))
|
||||
|
||||
// Redirect to profile page with success message
|
||||
const successUrl = new URL('/profile', request.url)
|
||||
successUrl.searchParams.set('payment', 'success')
|
||||
successUrl.searchParams.set('type', 'balance')
|
||||
successUrl.searchParams.set('amount', amount as string)
|
||||
successUrl.searchParams.set('txn', txnid as string)
|
||||
|
||||
return NextResponse.redirect(successUrl)
|
||||
|
||||
} catch (dbError) {
|
||||
console.error('Database update error during balance success:', dbError)
|
||||
// Log for manual reconciliation - payment was successful at gateway
|
||||
console.error(`CRITICAL: Balance payment ${txnid} succeeded at PayU but DB update failed`)
|
||||
|
||||
// Still redirect to success but with warning
|
||||
const successUrl = new URL('/profile', request.url)
|
||||
successUrl.searchParams.set('payment', 'success')
|
||||
successUrl.searchParams.set('type', 'balance')
|
||||
successUrl.searchParams.set('amount', amount as string)
|
||||
successUrl.searchParams.set('txn', txnid as string)
|
||||
successUrl.searchParams.set('warning', 'db-update-failed')
|
||||
|
||||
return NextResponse.redirect(successUrl)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Balance success handler error:', error)
|
||||
return NextResponse.redirect(new URL('/payment/failed?error=processing-error&type=balance', request.url))
|
||||
}
|
||||
}
|
||||
|
||||
// Mock function for demonstration
|
||||
async function mockUpdateBalanceSuccess(txnid: string, amount: number) {
|
||||
console.log(`Mock DB Update: Balance Transaction ${txnid} successful`)
|
||||
|
||||
// Mock: Get user Silicon ID from transaction
|
||||
const mockSiliconId = 'USR_12345' // In real implementation, fetch from add_balance_history table
|
||||
|
||||
// Mock: Update add_balance_history status
|
||||
const historyUpdate = {
|
||||
transaction_id: txnid,
|
||||
status: 'success',
|
||||
updated_at: new Date(),
|
||||
payment_gateway_response: {
|
||||
amount: amount,
|
||||
currency: 'INR',
|
||||
payment_date: new Date()
|
||||
}
|
||||
}
|
||||
|
||||
// Mock: Update user balance
|
||||
const balanceUpdate = {
|
||||
silicon_id: mockSiliconId,
|
||||
balance_increment: amount,
|
||||
previous_balance: 5000, // Mock previous balance
|
||||
new_balance: 5000 + amount,
|
||||
updated_at: new Date()
|
||||
}
|
||||
|
||||
console.log(`Mock: User ${mockSiliconId} balance increased by ₹${amount}`)
|
||||
console.log(`Mock: New balance: ₹${balanceUpdate.new_balance}`)
|
||||
|
||||
// Mock delay
|
||||
await new Promise(resolve => setTimeout(resolve, 150))
|
||||
|
||||
return { historyUpdate, balanceUpdate }
|
||||
}
|
||||
Reference in New Issue
Block a user