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

Next.js中Stripe Webhook签名匹配失败问题求助

Next.js Pages API路由Stripe Webhook签名不匹配问题解决思路

核心排查与修复步骤

1. 先禁用Next.js自动解析请求体(关键第一步)

Pages API路由默认会自动解析JSON请求体,这会破坏Stripe要求的原始字节流,直接导致签名匹配失败。在你的API路由文件顶部加这段配置即可——Vercel完全支持pages路由的config配置,放心用:

export const config = {
  api: {
    bodyParser: false,
  },
};

2. 正确读取原始请求体的两种靠谱方法

方法一:用raw-body库(省心高效)

先安装依赖:

npm install raw-body

读取时必须指定encoding: null,确保拿到的是原始Buffer而非字符串:

import getRawBody from 'raw-body';
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
  apiVersion: '2024-04-10',
});

export const config = {
  api: {
    bodyParser: false,
  },
};

export default async function handler(req, res) {
  if (req.method !== 'POST') return res.status(405).end();

  try {
    const rawBody = await getRawBody(req, {
      length: req.headers['content-length'],
      limit: '2mb',
      encoding: null, // 必须返回Buffer,禁止转字符串
    });

    const signature = req.headers['stripe-signature'];
    const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;

    const event = stripe.webhooks.constructEvent(rawBody, signature, webhookSecret);

    // 写入你的事件处理逻辑,比如支付成功后的业务操作
    switch (event.type) {
      case 'payment_intent.succeeded':
        // 处理逻辑示例
        break;
      default:
        console.log(`未处理的事件类型:${event.type}`);
    }

    res.status(200).json({ received: true });
  } catch (err) {
    console.error(err);
    return res.status(400).send(`Webhook错误:${err.message}`);
  }
}

方法二:手动拼接Buffer(无需第三方库)

不想装依赖的话,直接手动读取请求流拼接:

import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
  apiVersion: '2024-04-10',
});

export const config = {
  api: {
    bodyParser: false,
  },
};

export default async function handler(req, res) {
  if (req.method !== 'POST') return res.status(405).end();

  try {
    const rawBody = await new Promise((resolve, reject) => {
      const chunks = [];
      req.on('data', chunk => chunks.push(chunk));
      req.on('end', () => resolve(Buffer.concat(chunks)));
      req.on('error', reject);
    });

    const signature = req.headers['stripe-signature'];
    const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;

    const event = stripe.webhooks.constructEvent(rawBody, signature, webhookSecret);

    // 事件处理逻辑...

    res.status(200).json({ received: true });
  } catch (err) {
    console.error(err);
    return res.status(400).send(`Webhook错误:${err.message}`);
  }
}

3. 环境变量与Stripe控制台配置检查

  • 确认STRIPE_WEBHOOK_SECRET是Stripe控制台生成的Webhook签名密钥,不是普通API密钥,测试/生产环境密钥要对应,不能混用。
  • 检查Stripe控制台中Webhook的端点URL,必须和部署后的API路由URL完全一致,生产环境必须使用HTTPS。
  • 确保Stripe控制台Webhook的API版本设置为2024-04-10,和代码中的配置完全对齐。

4. Vercel部署后的额外检查

  • 进入Vercel项目的环境变量页面,确认STRIPE_SECRET_KEY和STRIPE_WEBHOOK_SECRET配置正确,无拼写错误。
  • 若使用了Vercel中间件或Edge Functions,禁止提前解析请求体,避免原始数据被修改。

5. 本地测试验证

用Stripe CLI模拟Webhook事件,排查本地是否正常:

# 启动转发,将Stripe事件转发到本地API路由
stripe listen --forward-to localhost:3000/api/webhook
# 触发测试事件
stripe trigger payment_intent.succeeded

如果本地能正常解析,部署后出问题,大概率是环境变量或Stripe控制台的URL/密钥配置错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 12:37:16