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

NextJS14接收Stripe CLI的POST请求正常,但无法处理Webhook

问题:Stripe Webhook事件触发但处理逻辑未执行

已确认通过Stripe CLI发送的Webhook POST请求能被正常接收,checkout.session.completed事件也已触发,但Webhook的处理逻辑(包括控制台日志输出、Supabase数据插入等操作)均未执行。以下是./api/webhook/route.ts的代码:

./api/webhook/route.ts
import { createClient } from '@supabase/supabase-js';
import { headers } from 'next/headers';
import { NextRequest, NextResponse } from 'next/server';
import { NextApiRequest, NextApiResponse } from 'next';
import Stripe from 'stripe';
import { Resend } from 'resend';
import EmailTemplate from '@/components/email-template';


const resend = new Resend(process.env.NEXT_RESEND_API_KEY);

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);

const stripe = new Stripe(`${process.env.STRIPE_SECRET_KEY}`, {
  apiVersion: '2023-08-16',
});

//Handling incoming POST req
async function POST(request: NextRequest) {
  try {
    const body = await request.text();
    const endpointSecret = process.env.STRIPE_SECRET_WEBHOOK_KEY!;
    const sig = headers().get('stripe-signature') as string;
    // const sig = headers().get('Stripe-Signature') as string;
    let event: Stripe.Event;
    //read the event data from stripe
    try {
      event = stripe.webhooks.constructEvent(body, sig, endpointSecret);
    } catch (err) {
      return new Response(`Webhook Error: ${err}`, {
        status: 400,
      });
    }
    console.log(`Received event !webhook!: ${event?.type}`);
    // Handle the event: checkout.session.completed
    switch (event.type) {
      case 'checkout.session.completed':
        const session: any = event.data.object;
        console.log('!Checkout session completed!:', session.payment_status);

        // get customer name, email
        const customerEmail = session.customer_details.email;
        const customerName = session.customer_details.name;

        // create the customer in db
        const { data: customer, error: errorCustomer } = await supabase
          .from('Customer')
          .insert({ customerName, customerEmail })
          .select()
          .single();

        if (errorCustomer) throw new Error(errorCustomer.message);

       
        break;
      default:
    }
    return NextResponse.json({ received: true });
  } catch (error) {
    return NextResponse.json({ error }, { status: 400 });
  }
}

async function GET(request: NextApiRequest, response: NextApiResponse) {
  // Bad Request or how ever you want to respond.
  return response.status(400).json({ error: 'Bad Request' });
}

export { POST, GET };

排查关键点:

  • 签名验证失败:Stripe发送的签名头是Stripe-Signature(首字母大写),当前代码使用小写的stripe-signature获取,会导致签名验证失败,直接进入catch返回400,后续逻辑无法执行。建议启用注释里的正确写法。
  • 环境变量配置问题:确认STRIPE_SECRET_WEBHOOK_KEY、STRIPE_SECRET_KEY等环境变量是否在运行环境中正确加载,Next.js中环境变量需要确保在服务器端可访问。
  • Supabase权限不足:当前使用的NEXT_PUBLIC_SUPABASE_ANON_KEY是前端匿名密钥,权限有限,后端操作应使用SUPABASE_SERVICE_ROLE_KEY,否则插入数据会因权限失败,且被外层catch捕获后无日志输出。
  • 日志查看方式错误:Next.js App Router的API路由日志属于服务器端日志,本地开发需查看终端输出,生产环境需查看对应平台的服务器日志,前端控制台无法看到这些日志。
  • 事件类型匹配问题:确认Stripe发送的事件类型确实是checkout.session.completed,可在Stripe Dashboard的Webhook事件详情中核对,避免拼写错误导致switch分支不触发。

内容的提问来源于stack exchange,提问作者Ehtz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 16:12:50