add subscribe detail
This commit is contained in:
parent
79ddfc6898
commit
1d7fc6e5f2
@ -261,7 +261,7 @@
|
|||||||
},
|
},
|
||||||
"subscription": {
|
"subscription": {
|
||||||
"title": "Subscription Management",
|
"title": "Subscription Management",
|
||||||
"subtitle": "Manage your subscription and billing",
|
"subtitle": "Manage your subscription status",
|
||||||
"currentPlan": "Current Plan",
|
"currentPlan": "Current Plan",
|
||||||
"planDetails": "Plan Details",
|
"planDetails": "Plan Details",
|
||||||
"billingCycle": "Billing Cycle",
|
"billingCycle": "Billing Cycle",
|
||||||
@ -286,7 +286,14 @@
|
|||||||
"usage": "Usage",
|
"usage": "Usage",
|
||||||
"promptsUsed": "Prompts Used",
|
"promptsUsed": "Prompts Used",
|
||||||
"versionsUsed": "Versions 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": {
|
"admin": {
|
||||||
"dashboard": "Admin Dashboard",
|
"dashboard": "Admin Dashboard",
|
||||||
|
@ -262,7 +262,7 @@
|
|||||||
},
|
},
|
||||||
"subscription": {
|
"subscription": {
|
||||||
"title": "订阅管理",
|
"title": "订阅管理",
|
||||||
"subtitle": "管理您的订阅和账单",
|
"subtitle": "管理您的订阅状态",
|
||||||
"currentPlan": "当前方案",
|
"currentPlan": "当前方案",
|
||||||
"planDetails": "方案详情",
|
"planDetails": "方案详情",
|
||||||
"billingCycle": "计费周期",
|
"billingCycle": "计费周期",
|
||||||
@ -287,7 +287,14 @@
|
|||||||
"usage": "使用情况",
|
"usage": "使用情况",
|
||||||
"promptsUsed": "已使用提示词",
|
"promptsUsed": "已使用提示词",
|
||||||
"versionsUsed": "已使用版本",
|
"versionsUsed": "已使用版本",
|
||||||
"unlimited": "无限制"
|
"unlimited": "无限制",
|
||||||
|
"quickActions": "快速操作",
|
||||||
|
"manageBilling": "管理账单",
|
||||||
|
"viewAllPlans": "查看所有方案",
|
||||||
|
"additionalOptions": "其他选项",
|
||||||
|
"feeDetails": "费用详情",
|
||||||
|
"feeDetailsDescription": "查看交易历史和信用使用记录",
|
||||||
|
"planDetailsDescription": "查看可用的订阅方案和功能特性"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"dashboard": "管理员后台",
|
"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) {
|
if (loading || subscriptionLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
@ -201,51 +226,71 @@ export default function SubscriptionPage() {
|
|||||||
|
|
||||||
{/* Quick Actions */}
|
{/* Quick Actions */}
|
||||||
<div className="bg-card rounded-lg border border-border p-6">
|
<div className="bg-card rounded-lg border border-border p-6">
|
||||||
<h3 className="font-semibold text-foreground mb-4">Quick Actions</h3>
|
<h3 className="font-semibold text-foreground mb-4">{t('quickActions')}</h3>
|
||||||
<div className="flex flex-col sm:flex-row gap-3">
|
<div className="space-y-4">
|
||||||
{currentPlan === 'free' ? (
|
{/* Primary Actions */}
|
||||||
<QuickUpgradeButton className="flex items-center">
|
<div className="flex flex-col sm:flex-row gap-3">
|
||||||
<Crown className="w-4 h-4 mr-2" />
|
{currentPlan === 'free' ? (
|
||||||
{t('upgradePlan')}
|
<QuickUpgradeButton className="flex items-center">
|
||||||
</QuickUpgradeButton>
|
<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 && (
|
|
||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="outline"
|
||||||
onClick={() => handleCancelSubscription(subscriptionData.subscriptions[0].id)}
|
onClick={handleManageSubscription}
|
||||||
disabled={actionLoading}
|
disabled={actionLoading}
|
||||||
className="flex items-center"
|
className="flex items-center"
|
||||||
>
|
>
|
||||||
{actionLoading ? <LoadingSpinner className="w-4 h-4 mr-2" /> : <AlertTriangle className="w-4 h-4 mr-2" />}
|
{actionLoading ? <LoadingSpinner className="w-4 h-4 mr-2" /> : <CreditCard className="w-4 h-4 mr-2" />}
|
||||||
{t('cancelSubscription')}
|
{t('manageBilling')}
|
||||||
</Button>
|
</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
|
{/* Secondary Actions */}
|
||||||
variant="outline"
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||||
onClick={() => router.push('/pricing')}
|
<Button
|
||||||
className="flex items-center"
|
variant="ghost"
|
||||||
>
|
onClick={() => router.push('/pricing')}
|
||||||
<Star className="w-4 h-4 mr-2" />
|
className="flex items-center justify-start p-4 h-auto"
|
||||||
View All Plans
|
>
|
||||||
</Button>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user