Files
biblical-guide.com/app/[locale]/subscription/success/page.tsx
Andrei bc9fe1d9bb fix: correct localStorage token name from 'token' to 'authToken'
Fixed authentication token inconsistency in subscription pages:

Issue:
- Subscription pages were using localStorage.getItem('token')
- Rest of the app uses localStorage.getItem('authToken')
- This caused users to be redirected to login when accessing subscription pages

Files Fixed:
- app/[locale]/subscription/page.tsx
  * fetchUserData() function
  * handleUpgrade() function
  * handleManageSubscription() function
- app/[locale]/subscription/success/page.tsx
  * SuccessContent component verification
- components/subscription/usage-display.tsx
  * fetchUsageData() function

Result:
- Users can now access subscription pages when logged in
- Consistent authentication token naming across entire app
- No more unwanted redirects to login page

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-12 22:35:34 +00:00

282 lines
8.0 KiB
TypeScript

'use client'
import { useState, useEffect, Suspense } from 'react'
import {
Container,
Box,
Typography,
Button,
Card,
CardContent,
CircularProgress,
Alert
} from '@mui/material'
import {
CheckCircle,
ChatBubble,
AutoAwesome,
EmojiEvents,
Favorite
} from '@mui/icons-material'
import { useRouter, useSearchParams } from 'next/navigation'
import { useTranslations, useLocale } from 'next-intl'
import Link from 'next/link'
function SuccessContent() {
const router = useRouter()
const searchParams = useSearchParams()
const locale = useLocale()
const t = useTranslations('subscription')
const [loading, setLoading] = useState(true)
const [error, setError] = useState('')
useEffect(() => {
// Verify session and refresh user data
const sessionId = searchParams.get('session_id')
if (!sessionId) {
setError(t('errors.noSession'))
setLoading(false)
return
}
// Give webhooks a moment to process, then verify the user's subscription
const timer = setTimeout(async () => {
try {
const token = localStorage.getItem('authToken')
if (!token) {
router.push(`/${locale}/login`)
return
}
// Refresh user profile to confirm upgrade
const response = await fetch('/api/user/profile', {
headers: {
'Authorization': `Bearer ${token}`
}
})
if (response.ok) {
const data = await response.json()
if (data.user.subscriptionTier === 'premium') {
setLoading(false)
} else {
setError(t('errors.upgradeNotConfirmed'))
setLoading(false)
}
} else {
setError(t('errors.loadFailed'))
setLoading(false)
}
} catch (err) {
console.error('Error verifying subscription:', err)
setError(t('errors.generic'))
setLoading(false)
}
}, 2000) // Wait 2 seconds for webhook processing
return () => clearTimeout(timer)
}, [searchParams, router, locale, t])
if (loading) {
return (
<Container maxWidth="md" sx={{ py: 8, display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
<CircularProgress size={60} sx={{ mb: 3 }} />
<Typography variant="h6" color="text.secondary">
{t('success.verifying')}
</Typography>
</Container>
)
}
if (error) {
return (
<Container maxWidth="md" sx={{ py: 8 }}>
<Alert severity="warning" sx={{ mb: 4 }}>
{error}
</Alert>
<Box sx={{ textAlign: 'center' }}>
<Button
variant="contained"
component={Link}
href={`/${locale}/subscription`}
>
{t('success.viewSubscription')}
</Button>
</Box>
</Container>
)
}
return (
<Container maxWidth="md" sx={{ py: 8 }}>
{/* Success Header */}
<Box sx={{ textAlign: 'center', mb: 6 }}>
<Box
sx={{
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
width: 80,
height: 80,
borderRadius: '50%',
bgcolor: 'success.main',
mb: 3
}}
>
<CheckCircle sx={{ fontSize: 50, color: 'white' }} />
</Box>
<Typography variant="h3" component="h1" gutterBottom fontWeight="700">
{t('success.title')}
</Typography>
<Typography variant="h6" color="text.secondary" sx={{ mb: 2 }}>
{t('success.subtitle')}
</Typography>
<Typography variant="body1" color="text.secondary">
{t('success.message')}
</Typography>
</Box>
{/* Premium Benefits */}
<Card elevation={2} sx={{ mb: 4 }}>
<CardContent sx={{ p: 4 }}>
<Typography variant="h5" gutterBottom fontWeight="600" sx={{ mb: 3 }}>
{t('success.benefitsTitle')}
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2.5 }}>
<Box sx={{ display: 'flex', gap: 2, alignItems: 'flex-start' }}>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: 48,
height: 48,
borderRadius: 2,
bgcolor: 'primary.light',
flexShrink: 0
}}
>
<AutoAwesome sx={{ color: 'primary.main' }} />
</Box>
<Box>
<Typography variant="h6" gutterBottom>
{t('success.benefits.unlimited.title')}
</Typography>
<Typography variant="body2" color="text.secondary">
{t('success.benefits.unlimited.description')}
</Typography>
</Box>
</Box>
<Box sx={{ display: 'flex', gap: 2, alignItems: 'flex-start' }}>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: 48,
height: 48,
borderRadius: 2,
bgcolor: 'success.light',
flexShrink: 0
}}
>
<EmojiEvents sx={{ color: 'success.main' }} />
</Box>
<Box>
<Typography variant="h6" gutterBottom>
{t('success.benefits.priority.title')}
</Typography>
<Typography variant="body2" color="text.secondary">
{t('success.benefits.priority.description')}
</Typography>
</Box>
</Box>
<Box sx={{ display: 'flex', gap: 2, alignItems: 'flex-start' }}>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: 48,
height: 48,
borderRadius: 2,
bgcolor: 'error.light',
flexShrink: 0
}}
>
<Favorite sx={{ color: 'error.main' }} />
</Box>
<Box>
<Typography variant="h6" gutterBottom>
{t('success.benefits.support.title')}
</Typography>
<Typography variant="body2" color="text.secondary">
{t('success.benefits.support.description')}
</Typography>
</Box>
</Box>
</Box>
</CardContent>
</Card>
{/* Action Buttons */}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<Button
variant="contained"
size="large"
fullWidth
startIcon={<ChatBubble />}
component={Link}
href={`/${locale}/chat`}
sx={{ py: 1.5 }}
>
{t('success.startChatting')}
</Button>
<Button
variant="outlined"
size="large"
fullWidth
component={Link}
href={`/${locale}/subscription`}
>
{t('success.viewSubscription')}
</Button>
<Button
variant="text"
size="large"
fullWidth
component={Link}
href={`/${locale}`}
>
{t('success.backHome')}
</Button>
</Box>
{/* Additional Info */}
<Box sx={{ mt: 6, textAlign: 'center' }}>
<Typography variant="body2" color="text.secondary">
{t('success.receiptInfo')}
</Typography>
</Box>
</Container>
)
}
export default function SubscriptionSuccessPage() {
return (
<Suspense
fallback={
<Container maxWidth="md" sx={{ py: 8, display: 'flex', justifyContent: 'center' }}>
<CircularProgress />
</Container>
}
>
<SuccessContent />
</Suspense>
)
}