sleighty
sleighty

Reputation: 1075

Verifying Stripe webhook in SvelteKit endpoint: how to get the raw body of a RequestEvent?

Similar to this question, using a more recent version of SvelteKit.

Context: SvelteKit PR #3384 started passing standard Request objects to endpoints and removed rawBody.

The question linked above has a good answer for how to use rawBody to call Stripe's constructEvent to verify the signature of an incoming webhook request, but now that the unmodified body is no longer exposed (as far as I can tell), I'm left wondering how to update my webhook handler.

I've tried calling constructEvent with the results of request.text(), request.json(), request.arrayBuffer() (converted to a string), request.blob().text(), and the plain request.body converted to a string, yet none of them worked. It always throws the same error:

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

Upvotes: 3

Views: 1513

Answers (3)

Madacol
Madacol

Reputation: 4296

await request.text() as the body really works!

But beware! If the webhook's secret is wrong, it gives exactly the same error

No signatures found matching the expected signature for payload.
Are you passing the raw request body you received from Stripe?

Upvotes: 2

Gin Quin
Gin Quin

Reputation: 1113

Since Stripe accepts strings, I would advise to use await request.text() that transforms the body into a raw string. Slightly slower but guaranteed to work in all environments:

export async function POST({ request }) {
    try {
        const body = await request.text();
        const signature = request.headers.get('stripe-signature')|| "";
        const event = stripe.webhooks.constructEvent(
            body,
            signature,
            secret,
        );
    } catch (error) {
        console.error(error);
    }
}

Another way would be to do const body = Buffer.from(await request.arrayBuffer()) but that would work only in Node environments.

Upvotes: 3

abdo1504
abdo1504

Reputation: 26

So I've looked into the node-fetch source code because that's what svelte-kit uses to shim the dev server with and all environments that don't support Fetch and there is a method that the standard Request class doesn't have and that's Request.buffer(). With this method I was able to solve that issue. This will only be available in node or serverless environments that don't support fetch natively (nearly every environment except cloudflare).

export async function post({ request }: RequestEvent) {
    try {
        const body = await request.buffer();
        const sig = request.headers.get('stripe-signature') as string;
        const e = stripe.webhooks.constructEvent(body, sig, secret);
        console.log(e.type);
    } catch (_e) {
        console.error(_e);
    }
}

Result:

charge.succeeded
payment_intent.succeeded
payment_intent.created

Upvotes: 1

Related Questions