initial commit

This commit is contained in:
Kar k1
2025-08-30 18:18:57 +05:30
commit 7219108342
270 changed files with 70221 additions and 0 deletions

View File

@@ -0,0 +1,115 @@
import { NextRequest, NextResponse } from 'next/server'
import crypto from 'crypto'
interface PayUFailureResponse {
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
}
/**
* PayU Payment Failure Handler
* Processes failed payment responses from PayU gateway
*/
export async function POST(request: NextRequest) {
try {
const formData = await request.formData()
const payuResponse: Partial<PayUFailureResponse> = {}
// 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(`Payment failed - Transaction: ${txnid}, Status: ${status}, Error: ${error || error_Message || 'Unknown'}`)
// Validate required parameters
if (!txnid) {
console.error('Missing transaction ID in failure response')
return NextResponse.redirect(new URL('/payment/failed?error=invalid-response', 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 failure response for transaction: ${txnid}`)
}
}
try {
// TODO: Update database with failed payment status
// In a real implementation:
// await updateBillingStatus(txnid, 'failed', error || error_Message)
// Mock database update for demo
await mockUpdatePaymentStatus(txnid as string, 'failed', error || error_Message || 'Payment failed')
// Determine failure reason for user-friendly message
let failureReason = 'unknown'
if (error_Message?.toLowerCase().includes('insufficient')) {
failureReason = 'insufficient-funds'
} else if (error_Message?.toLowerCase().includes('declined')) {
failureReason = 'card-declined'
} else if (error_Message?.toLowerCase().includes('timeout')) {
failureReason = 'timeout'
} else if (error_Message?.toLowerCase().includes('cancelled')) {
failureReason = 'user-cancelled'
}
// Redirect to failure page with transaction details
const failureUrl = new URL('/payment/failed', request.url)
failureUrl.searchParams.set('txn', txnid as string)
failureUrl.searchParams.set('reason', failureReason)
if (amount) {
failureUrl.searchParams.set('amount', amount as string)
}
return NextResponse.redirect(failureUrl)
} catch (dbError) {
console.error('Database update error during failure handling:', dbError)
// Continue to failure page even if DB update fails
return NextResponse.redirect(new URL('/payment/failed?error=db-error&txn=' + txnid, request.url))
}
} catch (error) {
console.error('Payment failure handler error:', error)
return NextResponse.redirect(new URL('/payment/failed?error=processing-error', request.url))
}
}
// Mock function for demonstration
async function mockUpdatePaymentStatus(txnid: string, status: string, errorMessage: string) {
// In real implementation, this would update MongoDB/database
console.log(`Mock DB Update: Transaction ${txnid} marked as ${status}`)
console.log(`Failure reason: ${errorMessage}`)
// Simulate database operations
const billingUpdate = {
billing_id: txnid,
payment_status: status,
payment_date: new Date(),
error_message: errorMessage,
retry_count: 1 // Could track retry attempts
}
// Mock delay
await new Promise(resolve => setTimeout(resolve, 100))
return billingUpdate
}

View File

@@ -0,0 +1,112 @@
import { NextRequest, NextResponse } from 'next/server'
import jwt from 'jsonwebtoken'
import crypto from 'crypto'
interface PaymentInitiateRequest {
billing_id: string
amount: number
service: string
}
interface UserTokenPayload {
siliconId: string
email: string
type: string
}
/**
* PayU Payment Gateway Integration
* Initiates payment for billing records
*/
export async function POST(request: NextRequest) {
try {
// Verify user authentication
const token = request.cookies.get('accessToken')?.value
if (!token) {
return NextResponse.json(
{ success: false, message: 'Authentication required' },
{ status: 401 }
)
}
const secret = process.env.JWT_SECRET || 'your-secret-key'
const user = jwt.verify(token, secret) as UserTokenPayload
// Parse request body
const body: PaymentInitiateRequest = await request.json()
const { billing_id, amount, service } = body
// Validate input
if (!billing_id || !amount || amount <= 0 || !service) {
return NextResponse.json(
{ success: false, message: 'Invalid payment parameters' },
{ status: 400 }
)
}
// TODO: Verify billing record exists and belongs to user
// In a real implementation, you would check database:
// const billing = await verifyBillingRecord(billing_id, user.siliconId)
// PayU configuration (from environment variables)
const merchantKey = process.env.PAYU_MERCHANT_KEY || 'test-key'
const merchantSalt = process.env.PAYU_MERCHANT_SALT || 'test-salt'
const payuUrl = process.env.PAYU_URL || 'https://test.payu.in/_payment'
// Prepare payment data
const txnid = billing_id
const productinfo = service.substring(0, 100)
const firstname = 'Customer'
const email = user.email
const phone = '9876543210' // Default phone or fetch from user profile
// Success and failure URLs
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:4023'
const surl = `${baseUrl}/api/payments/success`
const furl = `${baseUrl}/api/payments/failure`
// Generate PayU hash
const hashString = `${merchantKey}|${txnid}|${amount}|${productinfo}|${firstname}|${email}|||||||||||${merchantSalt}`
const hash = crypto.createHash('sha512').update(hashString).digest('hex')
// Return payment form data for frontend submission
const paymentData = {
success: true,
payment_url: payuUrl,
form_data: {
key: merchantKey,
txnid,
amount: amount.toFixed(2),
productinfo,
firstname,
email,
phone,
surl,
furl,
hash,
service_provider: 'payu_paisa',
},
}
return NextResponse.json(paymentData)
} catch (error) {
console.error('Payment initiation error:', error)
return NextResponse.json(
{ success: false, message: 'Payment initiation failed' },
{ status: 500 }
)
}
}
// Mock function - in real implementation, verify against database
async function verifyBillingRecord(billingId: string, siliconId: string) {
// TODO: Implement database verification
// Check if billing record exists and belongs to the user
return {
billing_id: billingId,
amount: 1000,
service: 'Cloud Instance',
user_silicon_id: siliconId,
status: 'pending',
}
}

View File

@@ -0,0 +1,110 @@
import { NextRequest, NextResponse } from 'next/server'
import crypto from 'crypto'
interface PayUResponse {
status: string
txnid: string
amount: string
productinfo: string
firstname: string
email: string
hash: string
key: string
[key: string]: string
}
/**
* PayU Payment Success Handler
* Processes successful payment responses from PayU gateway
*/
export async function POST(request: NextRequest) {
try {
const formData = await request.formData()
const payuResponse: Partial<PayUResponse> = {}
// 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')
return NextResponse.redirect(new URL('/payment/failed?error=invalid-response', request.url))
}
// Verify payment status
if (status !== 'success') {
console.log(`Payment failed for transaction: ${txnid}, status: ${status}`)
return NextResponse.redirect(new URL('/payment/failed?txn=' + txnid, 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 transaction: ${txnid}`)
// Log potential fraud attempt
console.error('Expected hash:', expectedHash)
console.error('Received hash:', hash?.toLowerCase())
return NextResponse.redirect(new URL('/payment/failed?error=hash-mismatch', request.url))
}
try {
// TODO: Update database with successful payment
// In a real implementation:
// await updateBillingStatus(txnid, 'success')
// await updateUserBalance(userSiliconId, parseFloat(amount))
console.log(`Payment successful for transaction: ${txnid}, amount: ₹${amount}`)
// Mock database update for demo
await mockUpdatePaymentStatus(txnid as string, 'success', parseFloat(amount as string))
// Redirect to success page with transaction details
const successUrl = new URL('/payment/success', request.url)
successUrl.searchParams.set('txn', txnid as string)
successUrl.searchParams.set('amount', amount as string)
return NextResponse.redirect(successUrl)
} catch (dbError) {
console.error('Database update error:', dbError)
// Even if DB update fails, payment was successful at gateway
// Log for manual reconciliation
return NextResponse.redirect(new URL('/payment/success?warning=db-update-failed&txn=' + txnid, request.url))
}
} catch (error) {
console.error('Payment success handler error:', error)
return NextResponse.redirect(new URL('/payment/failed?error=processing-error', request.url))
}
}
// Mock function for demonstration
async function mockUpdatePaymentStatus(txnid: string, status: string, amount: number) {
// In real implementation, this would update MongoDB/database
console.log(`Mock DB Update: Transaction ${txnid} marked as ${status}, amount: ₹${amount}`)
// Simulate database operations
const billingUpdate = {
billing_id: txnid,
payment_status: status,
payment_date: new Date(),
amount: amount
}
const userBalanceUpdate = {
// This would update user's account balance for "add_balance" transactions
increment: status === 'success' && txnid.includes('balance') ? amount : 0
}
// Mock delay
await new Promise(resolve => setTimeout(resolve, 100))
return { billingUpdate, userBalanceUpdate }
}