add subscribe detail
This commit is contained in:
parent
79ddfc6898
commit
1d7fc6e5f2
@ -261,7 +261,7 @@
|
||||
},
|
||||
"subscription": {
|
||||
"title": "Subscription Management",
|
||||
"subtitle": "Manage your subscription and billing",
|
||||
"subtitle": "Manage your subscription status",
|
||||
"currentPlan": "Current Plan",
|
||||
"planDetails": "Plan Details",
|
||||
"billingCycle": "Billing Cycle",
|
||||
@ -286,7 +286,14 @@
|
||||
"usage": "Usage",
|
||||
"promptsUsed": "Prompts Used",
|
||||
"versionsUsed": "Versions Used",
|
||||
"unlimited": "Unlimited"
|
||||
"unlimited": "Unlimited",
|
||||
"quickActions": "Quick Actions",
|
||||
"manageBilling": "Manage Billing",
|
||||
"viewAllPlans": "View All Plans",
|
||||
"additionalOptions": "Additional Options",
|
||||
"feeDetails": "Fee Details",
|
||||
"feeDetailsDescription": "View transaction history and credit usage",
|
||||
"planDetailsDescription": "View available subscription plans and features"
|
||||
},
|
||||
"admin": {
|
||||
"dashboard": "Admin Dashboard",
|
||||
|
@ -262,7 +262,7 @@
|
||||
},
|
||||
"subscription": {
|
||||
"title": "订阅管理",
|
||||
"subtitle": "管理您的订阅和账单",
|
||||
"subtitle": "管理您的订阅状态",
|
||||
"currentPlan": "当前方案",
|
||||
"planDetails": "方案详情",
|
||||
"billingCycle": "计费周期",
|
||||
@ -287,7 +287,14 @@
|
||||
"usage": "使用情况",
|
||||
"promptsUsed": "已使用提示词",
|
||||
"versionsUsed": "已使用版本",
|
||||
"unlimited": "无限制"
|
||||
"unlimited": "无限制",
|
||||
"quickActions": "快速操作",
|
||||
"manageBilling": "管理账单",
|
||||
"viewAllPlans": "查看所有方案",
|
||||
"additionalOptions": "其他选项",
|
||||
"feeDetails": "费用详情",
|
||||
"feeDetailsDescription": "查看交易历史和信用使用记录",
|
||||
"planDetailsDescription": "查看可用的订阅方案和功能特性"
|
||||
},
|
||||
"admin": {
|
||||
"dashboard": "管理员后台",
|
||||
|
49
src/app/api/subscription/portal/route.ts
Normal file
49
src/app/api/subscription/portal/route.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { createServerSupabaseClient } from '@/lib/supabase-server'
|
||||
import { createCustomerPortalSession } from '@/lib/stripe'
|
||||
|
||||
export async function POST() {
|
||||
try {
|
||||
const supabase = await createServerSupabaseClient()
|
||||
|
||||
// 获取当前用户
|
||||
const { data: { user }, error: authError } = await supabase.auth.getUser()
|
||||
|
||||
if (authError || !user) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
// 获取用户的Stripe客户ID
|
||||
const { data: userData, error: userError } = await supabase
|
||||
.from('users')
|
||||
.select('stripeCustomerId')
|
||||
.eq('id', user.id)
|
||||
.single()
|
||||
|
||||
if (userError || !userData?.stripeCustomerId) {
|
||||
return NextResponse.json(
|
||||
{ error: 'No Stripe customer found' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// 创建客户门户会话
|
||||
const returnUrl = `${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/subscription`
|
||||
|
||||
const portalSession = await createCustomerPortalSession(
|
||||
userData.stripeCustomerId,
|
||||
returnUrl
|
||||
)
|
||||
|
||||
return NextResponse.json({
|
||||
url: portalSession.url
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('Portal session creation failed:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to create portal session' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
@ -139,6 +139,31 @@ export default function SubscriptionPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleManageSubscription = async () => {
|
||||
setActionLoading(true)
|
||||
try {
|
||||
const response = await fetch('/api/subscription/portal', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json()
|
||||
// 重定向到Stripe客户门户
|
||||
window.location.href = result.url
|
||||
} else {
|
||||
throw new Error('Failed to create portal session')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Portal creation failed:', error)
|
||||
alert('Failed to access billing portal. Please try again.')
|
||||
} finally {
|
||||
setActionLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (loading || subscriptionLoading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
@ -201,51 +226,71 @@ export default function SubscriptionPage() {
|
||||
|
||||
{/* Quick Actions */}
|
||||
<div className="bg-card rounded-lg border border-border p-6">
|
||||
<h3 className="font-semibold text-foreground mb-4">Quick Actions</h3>
|
||||
<div className="flex flex-col sm:flex-row gap-3">
|
||||
{currentPlan === 'free' ? (
|
||||
<QuickUpgradeButton className="flex items-center">
|
||||
<Crown className="w-4 h-4 mr-2" />
|
||||
{t('upgradePlan')}
|
||||
</QuickUpgradeButton>
|
||||
) : (
|
||||
<>
|
||||
{/* 简单的账单信息显示 */}
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
const message = currentPlan === 'pro'
|
||||
? 'You are subscribed to the Pro plan ($19.9/month). To make changes to your subscription, please contact support.'
|
||||
: 'You are on the Free plan. Upgrade to Pro for more features!'
|
||||
alert(message)
|
||||
}}
|
||||
className="flex items-center"
|
||||
>
|
||||
<CreditCard className="w-4 h-4 mr-2" />
|
||||
Billing Info
|
||||
</Button>
|
||||
{subscriptionData?.subscriptions && subscriptionData.subscriptions.length > 0 && (
|
||||
<h3 className="font-semibold text-foreground mb-4">{t('quickActions')}</h3>
|
||||
<div className="space-y-4">
|
||||
{/* Primary Actions */}
|
||||
<div className="flex flex-col sm:flex-row gap-3">
|
||||
{currentPlan === 'free' ? (
|
||||
<QuickUpgradeButton className="flex items-center">
|
||||
<Crown className="w-4 h-4 mr-2" />
|
||||
{t('upgradePlan')}
|
||||
</QuickUpgradeButton>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={() => handleCancelSubscription(subscriptionData.subscriptions[0].id)}
|
||||
variant="outline"
|
||||
onClick={handleManageSubscription}
|
||||
disabled={actionLoading}
|
||||
className="flex items-center"
|
||||
>
|
||||
{actionLoading ? <LoadingSpinner className="w-4 h-4 mr-2" /> : <AlertTriangle className="w-4 h-4 mr-2" />}
|
||||
{t('cancelSubscription')}
|
||||
{actionLoading ? <LoadingSpinner className="w-4 h-4 mr-2" /> : <CreditCard className="w-4 h-4 mr-2" />}
|
||||
{t('manageBilling')}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{subscriptionData?.subscriptions && subscriptionData.subscriptions.length > 0 && (
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={() => handleCancelSubscription(subscriptionData.subscriptions[0].id)}
|
||||
disabled={actionLoading}
|
||||
className="flex items-center"
|
||||
>
|
||||
{actionLoading ? <LoadingSpinner className="w-4 h-4 mr-2" /> : <AlertTriangle className="w-4 h-4 mr-2" />}
|
||||
{t('cancelSubscription')}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => router.push('/pricing')}
|
||||
className="flex items-center"
|
||||
>
|
||||
<Star className="w-4 h-4 mr-2" />
|
||||
View All Plans
|
||||
</Button>
|
||||
{/* Secondary Actions */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => router.push('/pricing')}
|
||||
className="flex items-center justify-start p-4 h-auto"
|
||||
>
|
||||
<div className="flex flex-col items-start text-left">
|
||||
<div className="flex items-center mb-1">
|
||||
<Crown className="w-4 h-4 mr-2" />
|
||||
<span className="font-medium">{t('planDetails')}</span>
|
||||
</div>
|
||||
<span className="text-sm text-muted-foreground">{t('planDetailsDescription')}</span>
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => router.push('/credits')}
|
||||
className="flex items-center justify-start p-4 h-auto"
|
||||
>
|
||||
<div className="flex flex-col items-start text-left">
|
||||
<div className="flex items-center mb-1">
|
||||
<CreditCard className="w-4 h-4 mr-2" />
|
||||
<span className="font-medium">{t('feeDetails')}</span>
|
||||
</div>
|
||||
<span className="text-sm text-muted-foreground">{t('feeDetailsDescription')}</span>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user