获取发送到SvelteKit端点的请求原始正文

9

我在SvelteKit应用程序中有一个端点,处理来自Stripe的Webhook请求。每个请求都进行了签名,以便可以验证其来自Stripe。

我用于验证事件是否来自Stripe的代码大致如下:

import Stripe from "stripe";

const WEBHOOK_SECRET = process.env["STRIPE_WH_SECRET"];

const stripe = new Stripe(process.env["STRIPE_SECRET"], {
  apiVersion: "2020-08-27",
});

export async function post({ headers, body }) {
  let event: Stripe.Event;
  try {
    event = stripe.webhooks.constructEvent(
      body,
      headers["stripe-signature"],
      WEBHOOK_SECRET
    );
  } catch (err) {
    return {
      status: 400,
      body: err,
    };
  }

  // Do stuff with the event
}

但是当它从Stripe接收到事件时,我会得到这个错误:

No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? https://github.com/stripe/stripe-node#webhook-signing

经过一些研究,我发现在调用 该函数 将请求体解析成JSON之前,甚至在调用 SvelteKit钩子函数 之前,就已经完成了解析。这意味着无法直接获取原始请求体,因此我的最佳选择是尝试重构原始请求体。
event = stripe.webhooks.constructEvent(
  JSON.stringify(body),
  headers["stripe-signature"],
  WH_SECRET
);

我不是完全确定为什么会出现问题,因为在深入研究Stripe库中相关代码时,它似乎可以很好地处理字符串。我最好的猜测是某个地方的编码出了问题。

如果能提供任何帮助,将不胜感激,因为我真的希望避免放弃SvelteKit,毕竟我已经基本完成了项目(回想起来并不是一个好主意)。


我对SvelteKit不够熟悉,无法评论如何获取原始正文。但是,为了验证有效载荷,您绝对需要来自Stripe请求的原始(未重构的)正文。也许值得通过官方文档中的链接直接向SvelteKit团队询问(如何避免解析请求)。 - taintedzodiac
@taintedzodiac 我确实联系过那里,但没有得到任何答案。此外,除非我漏掉了什么非常奇怪的东西,否则似乎没有办法获取原始正文,除非编写自定义适配器,即使这样也无法在开发服务器中工作。感谢确认我需要获取实际的原始正文。 - Max Niederman
小心!如果你的 WEBHOOK_SECRET 错误,它会给你同样的错误提示。 - Madacol
3个回答

9
这是在Svelte Kit中获取请求原始主体的方法:
// routeName/+server.js
export async function POST({ request }) {
    const rawBody = await request.text()
    ...
}

在处理Stripe和Svelte Kit时,我也遇到了这个问题。


无法工作...我们收到了TypeError:Body is unusable错误。 - Alan Daniel

3
stripe.webhooks.constructEvent(payload, signature, secret)只接受string | Buffer类型的payload,但在SvelteKit请求中接收到的rawBody是Uint8Array类型的。
将Uint8Array的rawBody或JSON.stringify的rawBody作为payload传递会导致来自Stripe的以下错误。请参考:https://github.com/stripe/stripe-node#webhook-signing 错误代码:message: 'No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? https://github.com/stripe/stripe-node#webhook-signing' 我们需要将rawBody转换为stringBuffer类型,而不改变其内容。为此,我们可以使用: Buffer.from(rawBody)
因此,您的端点应该像这样:
...
const stripeWebhookSecret = process.env['STRIPE_WEBHOOK_SECRET'];

export const post: RequestHandler = async (request) => {
  const rawBody = Buffer.from(request.rawBody);
  const signature = request.headers['stripe-signature'];

  try {
    event = stripe.webhooks.constructEvent(
      rawBody,
      signature,
      stripeWebhookSecret
    );
...

5
自从rawBody不再被公开,有什么方法可以在 https://github.com/sveltejs/kit/pull/3384 之后实现这个? - sleighty

2
我发现以下代码对我有效。
 const body = await request.text();
 const signature = request.headers.get('stripe-signature')|| "";
 const event = stripe.webhooks.constructEvent(
     body,
     signature,
     secret,
 );

这最初给了我一个“TypeError: Body is unusable”错误,但那是因为我在调用“await request.text()”之前调用了“await request.json()”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接