Struggling with a Nextjs Stripe issue? The expected signature for payload does not match any found signatures

Struggling to integrate data from Stripe check out events into my orders database, facing issues with finding the signature while testing the web hook.

Check out route: app/api/webhooks/checkout/route.ts

import Cors from "micro-cors";
import { headers } from "next/headers";
import { NextResponse, } from "next/server";
import Stripe from "stripe";
import prisma from "@/prisma/utils";
import {  Users, Orders } from '@prisma/client';
import { sendEmail } from "@/email/utils";
import type { Readable } from 'node:stream';
import type { NextApiRequest } from "next";
import getRawBody from "raw-body";
import { buffer } from "micro";

const stripe = new Stripe(process.env.NEXT_PUBLIC_STRIPE_SECRET_KEY!);

const cors = Cors({
  allowMethods: ["POST", "HEAD"],
});

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

const secret = process.env.STRIPE_WEBHOOK_SECRET || "";

export async function POST(req: NextApiRequest) {
  try {
    const body = await buffer(req);
    
    const sig = req.headers['stripe-signature']! as string;
    
    const event = stripe.webhooks.constructEvent(body.toString(), sig!, secret);
    
    if (event.type === "checkout.session.completed") {
      if (!event.data.object.customer_email) {
        throw new Error(`missing user email, ${event.id}`);
      } else {
        //if email exists then do some stuff

        const id = event.data.object.id
        const createdAt = new Date(event.data.object.created * 1000)

        const order = await prisma.orders.findUnique({
            where: {
                id,
            },
        });

        const email = event.data.object.customer_details!.email!

        const user = await prisma.users.findUnique({
          where: {
            email,
          }
        });

        const line_items = await stripe.checkout.sessions.listLineItems(
          id
        );

        console.log('line items:' + line_items)

        const products = JSON.stringify([
          line_items!.data.map((item: any) => {
            return {
              item: item.description, //defualts to name
              quantity: item.quantity,
              price: item.price,
            }
          })
        ])
    
        if(order){
            throw new Error(`order already exist, ${id}`);
        } else {
            await prisma.orders.create({ 
                data: {
                    id,
                    createdAt,
                    user_id: user!.id,
                    user_name: event.data.object.customer_details!.name!,
                    address: event.data.object.customer_details!.address!.line1!,
                    address_2: event.data.object.customer_details!.address!.line2!,
                    city: event.data.object.customer_details!.address!.city!,
                    state: event.data.object.customer_details!.address!.state!,
                    zip: Number(event.data.object.customer_details!.address!.postal_code!),
                    profit: Number(event.data.object.amount_total), 
                    shipped: false,
                    deliverd: false,
                    products,
                },
            });
        }
      }      
    }
    
    return NextResponse.json({ result: event, ok: true });
  } catch (error) {
    console.error(error);
    return NextResponse.json(
      {
        message: "something went wrong",
        ok: false,
      },
      { status: 500 }
    );
  }
}

Encountering various errors while trying different methods:

Error using raw-body lib: TypeError - argument stream must be a stream.

Error using buffer from micro lib: HttpError - Invalid body and originalError: TypeError - stream.on is not a function.

Error using plain body like req.body: No signatures found matching the expected signature for payload.

Tried using body = req.text() and switching request type from NextApiRequest to Request.
Tried adding bodyParser: false.
Attempted changing

const sig = req.headers['stripe-signature']! as string;
to
const sig = req.headers.get('stripe-signature')!
.
Also explored using
sig = headers.get('stripe-signature')
.

Answer №1

The code has been updated and now it appears to be functioning correctly, although I'm not sure why.


import Cors from "micro-cors";
import { headers } from "next/headers";
import { NextResponse, } from "next/server";
import Stripe from "stripe";
import prisma from "@/prisma/utils";
import {  Users, Orders } from '@prisma/client';
import { sendEmail } from "@/email/utils";
import type { Readable } from 'node:stream';
import type { NextApiRequest } from "next";
import getRawBody from "raw-body";
// import { buffer } from "micro";

const stripe = new Stripe(process.env.NEXT_PUBLIC_STRIPE_SECRET_KEY!);

const cors = Cors({
allowMethods: ["POST", "HEAD"],
});

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

// async function buffer(readable: Readable) {
// const chunks = [];
// for await (const chunk of readable) {
// chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
// }
// return Buffer.concat(chunks);
// }

export async function POST(req: Request, res: Response) {
try {
const body = await req.text();

const secret = process.env.NEXT_PUBLIC_STRIPE__WEBHOOK_SECRET;

// const body = rawbody.toString()

const sig = req.headers.get('stripe-signature')! as string;

let event: Stripe.Event;

event = stripe.webhooks.constructEvent(body, sig!, secret!);

if (event.type === "checkout.session.completed") {
if (!event.data.object.customer_details!.email) {
throw new Error(`missing user email, ${event.id}`);
} else {
//if email exists then do some stuff

const id = event.data.object.id
console.log('id:'+ id)
const createdAt = new Date(event.data.object.created * 1000)

const order = await prisma.orders.findUnique({
where: {
id,
},
});

const email = event.data.object.customer_details!.email!

const user = await prisma.users.findUnique({
where: {
email,
}
});

const line_items = await stripe.checkout.sessions.listLineItems(
event.data.object.id
);

console.log('line items:' + line_items.data)


const products = JSON.stringify([
line_items!.data.map((item: any) => {
return {
item: item.description, //defualts to name
quantity: item.quantity,
price: item.price,
}
})
])

if(order){
throw new Error(`order already exist, ${id}`);
} else {
await prisma.orders.create({ 
data: {
id,
createdAt,
user_id: user!.id,
user_name: event.data.object.customer_details!.name!,
address: event.data.object.customer_details!.address!.line1!,
address_2: event.data.object.customer_details!.address!.line2!,
city: event.data.object.customer_details!.address!.city!,
state: event.data.object.customer_details!.address!.state!,
zip: Number(event.data.object.customer_details!.address!.postal_code!),
profit: Number(event.data.object.amount_total), 
shipped: false,
deliverd: false,
products,
},
});
if(user){
await prisma.users.update({
where: {
email,
},
data: {
points: 10 * products.length,
}
})
}
}
}

return NextResponse.json({ result: event, ok: true });
} catch (error) {

console.error(error);
return NextResponse.json(
{
message: "something went wrong",
ok: false,
},
{ status: 500 }
);
}
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Maintain hook varieties during implementation of array deconstruction

I have been developing a straightforward hook to export animation helper and element reference. import { gsap } from 'gsap'; import { useRef } from 'react'; export function useTween<R extends gsap.TweenTarget>(vars: gsap.TweenVar ...

Inaccurate recommendations for type safety in function overloading

The TypeScript compiler is not providing accurate suggestions for the config parameter when calling the fooBar function with the 'view_product' type. Although it correctly identifies errors when an incorrect key is provided, it does not enforce t ...

Optimal method for saving authenticated user in redux state

I am currently working on developing an application with the following technologies: Next.js An Express API Redux-Toolkit and RTK Query All of the authentication logic has been implemented successfully, but I have encountered a problem. After a successf ...

Creating a unique styleset in styled-jsx using custom ruleset generation

TL;DR What's the best way to insert a variable containing CSS rules into styled-jsx (using styled-jsx-plugin-sass)? In my JSX style, I have the following: // src/pages/index.tsx ... <style jsx> {` .test { height: 100vh; width ...

Tips for implementing a real-time search feature in Angular

I require assistance. I am attempting to perform a live search using the following code: when text is entered into an input, I want my targetListOptions array, which is used in a select dropdown, to update accordingly. The code runs without errors, but not ...

Tips for concealing tick labels in d3 using TypeScript

When trying to hide tick labels by passing an empty string to the .tickFormat("") method, I encountered an issue with Typescript. The error message received was as follows: TS2769: No overload matches this call. Overload 1 of 3, '(format: null): Axi ...

Tips for Fixing Error: "We received a visitor for node type TSSatisfiesExpression that is not recognized as a valid type."

There seems to be an issue with Babel unable to compile my Storybook code. I'm working on setting up a boilerplate with Storybook and Next.js I've searched for a solution to this problem but couldn't find one. Any help would be greatly app ...

Guide to integrating Mongoose typings with Angular 2 webpack starter

As a newcomer, I'm hoping this issue is straight forward. I am currently utilizing the angular2-webpack-starter found on GitHub. Based on the mongoose documentation, it appears that including their JavaScript file allows for accessing a global varia ...

Encountering an error with Next.JS when using the "use client" directive

Recently encountered a bizarre issue with my next.js project. Every time I input "use client"; on a page, it triggers a "SyntaxError: Unexpected token u in JSON at position 0". Here's the code snippet for reference: "use client" ...

Tips for determining the overall percentage breakdown of 100% based on the individual denominator for every column within angular 8

In my code, I have a simple function that calculates the sum of numbers and strings in columns within a table. The sum calculation itself works fine and provides accurate results. However, the problem arises when I attempt to divide the total sum of each c ...

Creating Algorithms for Generic Interfaces in TypeScript to Make them Compatible with Derived Generic Classes

Consider the (simplified) code: interface GenericInterface<T> { value: T } function genericIdentity<T>(instance : GenericInterface<T>) : GenericInterface<T> { return instance; } class GenericImplementingClass<T> implemen ...

Utilizing TypeScript for dynamic invocation of chalk

In my TypeScript code, I am trying to dynamically call the chalk method. Here is an example of what I have: import chalk from 'chalk'; const color: string = "red"; const message: string = "My Title"; const light: boolean = fa ...

Is it more efficient to have deps.ts per workspace or shared among workspaces?

Currently, I am in the process of setting up my very first monorepo for a Deno-based application. In this monorepo, the workspaces will be referred to as "modules" that the API code can import from, with each module having its own test suite, among other t ...

The slides in Swiperjs are having trouble moving smoothly

I am experiencing some challenges with swiperjs where the slides are not snapping to the next slide and I am unable to fetch the active index from them. Below is a snippet of my code where I am using typescript, swiperjs version 6.84, and the Ionic frame ...

Enhancing TypeScript Native Interface Properties in TypeScript 2.4

After discovering an error in the native Typescript interface for HTMLTextAreaElement, I realized the need to make a correction. The current interface looks like this: interface HTMLTextAreaElement { setSelectionRange(start: number, end: number): void; ...

Encountering an issue with Angular virtual scrolling: ViewDestroyedError arises when trying to utilize a destroyed view during detectChanges operation

My implementation involves using virtual scrolling from the cdk within a trigger-opening sidenav on a mat-radio element. Here is the code snippet: ts - ... @Component({ selector: 'app-generic-options-list', templateUrl: './generic-opt ...

Can someone explain how to define "a type of object that must not be empty" in typescript?

I'm working with a function that can operate on any type T, but with the constraint that if T is an object, it cannot potentially be empty. Here's what I've tried: declare function myFunction<T>(param: T extends Record<string, neve ...

Encountering TypeScript error TS2339 while mocking a React component with Jest

When attempting to mock a React component using Jest, I encountered an issue where TypeScript was not recognizing the mocked methods and showing a TS2339 error. Below is the test code snippet causing the problem: jest.mock('./features/News/News' ...

Press the text in a React Native TypeScript component to trigger a render

I am a newcomer to React Native and TypeScript, and I am struggling to figure out how to display something on the page of my app after a button is pressed. Below is the function I'm using: const getRandomNumber = () ={ const number = Math.fl ...

The size of Next.js builds is steadily increasing

After creating a new nextjs project using app routes, I noticed that when I run npm run build, the .next folder ends up being approximately 32mb in size. I'm curious to know if this is a typical size for a nextjs project. During development, I watche ...