You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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中的用户状态。

实现步骤:

  1. 创建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 });
}
  1. 修改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
    }
  },
  // ...其他参数
});
  1. 配置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 01:17:19