ai-wpa/app/payment/failed/page.tsx

303 lines
10 KiB
TypeScript

'use client'
import { useEffect, useState, Suspense } from 'react'
import { useSearchParams, useRouter } from 'next/navigation'
import { XCircle, RotateCcw, Home, AlertTriangle, HelpCircle, CreditCard } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
interface FailureDetails {
txn?: string
amount?: string
type?: string
reason?: string
error?: string
message?: string
}
interface FailureInfo {
title: string
description: string
suggestions: string[]
icon: typeof XCircle
color: string
}
const FAILURE_REASONS: Record<string, FailureInfo> = {
'insufficient-funds': {
title: 'Insufficient Funds',
description: 'Your payment method does not have enough balance to complete this transaction.',
suggestions: [
'Add money to your payment account',
'Try a different payment method',
'Contact your bank if you believe this is an error'
],
icon: CreditCard,
color: 'text-red-500'
},
'card-declined': {
title: 'Card Declined',
description: 'Your card was declined by the bank or payment processor.',
suggestions: [
'Check your card details are correct',
'Ensure your card is not expired',
'Try a different card',
'Contact your bank for more information'
],
icon: XCircle,
color: 'text-red-500'
},
'timeout': {
title: 'Payment Timeout',
description: 'The payment took too long to process and timed out.',
suggestions: [
'Check your internet connection',
'Try again with a stable connection',
'Clear your browser cache and cookies'
],
icon: AlertTriangle,
color: 'text-orange-500'
},
'user-cancelled': {
title: 'Payment Cancelled',
description: 'The payment was cancelled by you during the process.',
suggestions: [
'You can try the payment again',
'Make sure to complete the payment process',
'Contact support if you need assistance'
],
icon: XCircle,
color: 'text-gray-500'
},
'invalid-details': {
title: 'Invalid Payment Details',
description: 'The payment details provided were invalid or incorrect.',
suggestions: [
'Double-check your card number and CVV',
'Verify the expiry date is correct',
'Ensure billing address matches your card'
],
icon: AlertTriangle,
color: 'text-yellow-500'
},
'unknown': {
title: 'Payment Failed',
description: 'An unexpected error occurred while processing your payment.',
suggestions: [
'Try again in a few minutes',
'Use a different payment method',
'Contact support if the problem persists'
],
icon: HelpCircle,
color: 'text-gray-500'
}
}
function PaymentFailedContent() {
const searchParams = useSearchParams()
const router = useRouter()
const [details, setDetails] = useState<FailureDetails>({})
const [failureInfo, setFailureInfo] = useState<FailureInfo>(FAILURE_REASONS.unknown)
const [countdown, setCountdown] = useState(15)
useEffect(() => {
// Extract failure details from URL parameters
const txn = searchParams.get('txn')
const amount = searchParams.get('amount')
const type = searchParams.get('type') || 'billing'
const reason = searchParams.get('reason') || 'unknown'
const error = searchParams.get('error')
const message = searchParams.get('message')
const failureDetails: FailureDetails = {
txn: txn || undefined,
amount: amount || undefined,
type,
reason,
error: error || undefined,
message: message ? decodeURIComponent(message) : undefined
}
setDetails(failureDetails)
setFailureInfo(FAILURE_REASONS[reason] || FAILURE_REASONS.unknown)
// Auto-redirect countdown
const timer = setInterval(() => {
setCountdown((prev) => {
if (prev <= 1) {
router.push('/profile')
return 0
}
return prev - 1
})
}, 1000)
return () => clearInterval(timer)
}, [searchParams, router])
const handleRetryPayment = () => {
if (details.type === 'balance') {
router.push('/profile?tab=balance')
} else {
router.push('/services')
}
}
const handleDashboardRedirect = () => {
router.push('/profile')
}
const handleContactSupport = () => {
// TODO: Implement support contact functionality
router.push('/contact')
}
const IconComponent = failureInfo.icon
return (
<div className="min-h-screen bg-gradient-to-br from-red-50 to-orange-50 dark:from-red-900/10 dark:to-orange-900/10 flex items-center justify-center p-4">
<Card className="w-full max-w-lg">
<CardContent className="p-8 text-center">
{/* Failure Icon */}
<div className="mb-6">
<div className="mx-auto w-16 h-16 bg-red-100 dark:bg-red-900/30 rounded-full flex items-center justify-center">
<IconComponent className={`w-10 h-10 ${failureInfo.color}`} />
</div>
</div>
{/* Failure Message */}
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100 mb-2">
{failureInfo.title}
</h1>
<p className="text-gray-600 dark:text-gray-400 mb-6">
{details.message || failureInfo.description}
</p>
{/* Payment Details (if available) */}
{details.txn && (
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-4 mb-6 space-y-3">
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600 dark:text-gray-400">Transaction ID</span>
<code className="text-sm font-mono text-gray-900 dark:text-gray-100">{details.txn}</code>
</div>
{details.amount && (
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600 dark:text-gray-400">Amount</span>
<span className="text-lg font-bold text-gray-900 dark:text-gray-100">
{parseFloat(details.amount).toLocaleString('en-IN', { minimumFractionDigits: 2 })}
</span>
</div>
)}
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600 dark:text-gray-400">Type</span>
<span className="text-sm capitalize text-gray-900 dark:text-gray-100">
{details.type === 'balance' ? 'Balance Addition' : 'Service Payment'}
</span>
</div>
<div className="flex justify-between items-center">
<span className="text-sm text-gray-600 dark:text-gray-400">Date</span>
<span className="text-sm text-gray-900 dark:text-gray-100">
{new Date().toLocaleDateString('en-IN', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
})}
</span>
</div>
</div>
)}
{/* Action Buttons */}
<div className="flex flex-col sm:flex-row gap-3 mb-6">
<Button
onClick={handleRetryPayment}
className="flex-1"
>
<RotateCcw className="w-4 h-4 mr-2" />
Try Again
</Button>
<Button
onClick={handleDashboardRedirect}
variant="outline"
className="flex-1"
>
<Home className="w-4 h-4 mr-2" />
Dashboard
</Button>
</div>
{/* Suggestions */}
<div className="mb-6 p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
<h3 className="text-sm font-medium text-blue-900 dark:text-blue-100 mb-2">
What can you do?
</h3>
<ul className="text-sm text-blue-700 dark:text-blue-300 space-y-1 text-left">
{failureInfo.suggestions.map((suggestion, index) => (
<li key={index} className="flex items-start">
<span className="mr-2 mt-0.5"></span>
<span>{suggestion}</span>
</li>
))}
</ul>
</div>
{/* Support Section */}
<div className="mb-6 p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<h3 className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-2">
Need Help?
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-3">
If you continue to experience issues, our support team is here to help.
</p>
<Button
onClick={handleContactSupport}
variant="outline"
size="sm"
>
Contact Support
</Button>
</div>
{/* Auto-redirect Notice */}
<p className="text-sm text-gray-500 dark:text-gray-400">
Redirecting to dashboard in {countdown} seconds
</p>
{/* Error Details (for debugging, only show in development) */}
{details.error && process.env.NODE_ENV === 'development' && (
<details className="mt-4 text-left">
<summary className="text-sm text-gray-500 cursor-pointer">
Debug Information
</summary>
<pre className="mt-2 p-2 bg-gray-100 dark:bg-gray-800 rounded text-xs overflow-auto">
{JSON.stringify({ details, failureInfo }, null, 2)}
</pre>
</details>
)}
</CardContent>
</Card>
</div>
)
}
export default function PaymentFailedPage() {
return (
<Suspense fallback={
<div className="min-h-screen bg-gradient-to-br from-red-50 to-red-100 dark:from-red-950 dark:to-red-900 flex items-center justify-center p-4">
<Card className="w-full max-w-md mx-auto">
<CardContent className="p-8">
<div className="text-center space-y-4">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-red-600 mx-auto"></div>
<p className="text-gray-600">Loading...</p>
</div>
</CardContent>
</Card>
</div>
}>
<PaymentFailedContent />
</Suspense>
)
}