Next.js+TypeScript开发Spotify克隆:订阅后用户未添加及TS重载错误
问题解决:TypeScript重载错误 + 订阅状态未更新
一、解决“No Overload matches this call” TypeScript错误
你的代码中stripe.checkout.sessions.create的customer参数类型不匹配,是导致该错误的核心原因:
createOrRetrieveCustomer返回的是完整的Stripe Customer对象,但Stripe Checkout Session创建接口要求customer参数为字符串类型的客户ID,而非整个Customer实例。
修改代码中stripe.checkout.sessions.create的customer字段:
const session = await stripe.checkout.sessions.create({ payment_method_types: ['card'], billing_address_collection: 'required', customer: customer.id, // 改为customer.id,而非整个customer对象 line_items: [ { price: price.id, quantity } ], mode: 'subscription', allow_promotion_codes: true, subscription_data: { trial_from_plan: true, metadata }, success_url: `${getURL()}/account`, cancel_url: `${getURL()}/` });
二、解决订阅后用户状态未更新问题
用户完成支付跳转回应用后状态未变更,是因为缺少Stripe Webhook事件处理逻辑——Stripe不会主动通知应用订阅完成,需要监听并处理对应webhook事件,再更新Supabase中的用户状态。
实现步骤:
- 创建Stripe Webhook处理路由
在app/api/webhooks/stripe/route.ts中添加以下代码:
import { stripe } from '@/libs/stripe'; import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'; import { headers } from 'next/headers'; import { NextResponse } from 'next/server'; import Stripe from 'stripe'; export async function POST(request: Request) { const body = await request.text(); const signature = headers().get('stripe-signature') as string; const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET as string; let event: Stripe.Event; try { // 验证Stripe请求签名,确保请求合法 event = stripe.webhooks.constructEvent(body, signature, webhookSecret); } catch (err) { return new NextResponse(`Webhook Error: ${(err as Error).message}`, { status: 400 }); } // 处理订阅完成事件 if (event.type === 'checkout.session.completed') { const session = event.data.object as Stripe.Checkout.Session; const customerId = session.customer as string; const userId = session.metadata?.userId; if (!userId) return new NextResponse('Missing user ID in metadata', { status: 400 }); // 获取订阅信息 const subscription = await stripe.subscriptions.retrieve(session.subscription as string); // 更新Supabase中用户的订阅状态 const supabase = createRouteHandlerClient({ cookies: () => headers().get('cookie') }); await supabase .from('users') .update({ is_subscriber: true, stripe_subscription_id: subscription.id, stripe_customer_id: customerId, subscription_status: subscription.status }) .eq('id', userId); } return NextResponse.json({ received: true }); }
- 修改Checkout Session创建逻辑,存入用户UUID到metadata
在创建session时,将当前用户的UUID加入subscription_data.metadata,方便webhook中关联用户:
const session = await stripe.checkout.sessions.create({ // ...其他参数 subscription_data: { trial_from_plan: true, metadata: { ...metadata, userId: user?.id // 加入用户UUID } }, // ...其他参数 });
- 配置Stripe Dashboard中的Webhook
- 登录Stripe Dashboard,进入「Webhooks」页面
- 添加端点:填写你的webhook路由URL(比如
https://your-domain.com/api/webhooks/stripe) - 选择需要监听的事件:
checkout.session.completed和customer.subscription.updated - 复制生成的Webhook Secret,存入你的环境变量(
STRIPE_WEBHOOK_SECRET)
额外检查点
- 确保
createOrRetrieveCustomer函数正确返回Stripe Customer对象,且包含id字段 - 验证Supabase中的
users表已添加is_subscriber、stripe_subscription_id、stripe_customer_id、subscription_status等字段
内容的提问来源于stack exchange,提问作者user21437643
相关产品推荐
相关产品推荐

