#juco

1 messages Β· Page 1 of 1 (latest)

opaque harnessBOT
brisk karma
#

Hi πŸ‘‹ I'm not too familiar with nextjs specifically, but typically when we see that error encountered it is for one of two reasons:

  1. The signing secret being used in the event handler code does not match the signing secret of the webhook endpoint. Each webhook endpoint will generate their own unique signing secret, if you've created a new endpoint you will need to update the signing secret in your code.

  2. The contents of the request body being passed to constructEvent do not exactly match the contents of the request body that we sent. Our function is very sensitive, and even changes to the amount of whitespace will cause it to fail validation. If your framework is performing any adjustments to the request body (trimming, converting to json, etc) then you will need to remove those processes and work with the raw request body.

severe summit
#

hi, thanks for the quick response! just to make sure i did the first step correctly:
the first image is where you get the webhook_secret, and the second image is where you get the secret key right?

severe summit
#

but this isnt working

severe summit
digital hornet
#

@severe summit This works for me if you need it Next.JS. ```if (req.method === 'POST'){
const buf = await buffer(req)
const sig = req.headers['stripe-signature']
const webhookSecret = process.env.STRIPE_WEBHOOK_SIGNING_SECRET

let event;

try {
  if (!sig || !webhookSecret) return;

  event = stripe.webhooks.constructEvent(buf, sig, webhookSecret);

} catch(error:any) {
  console.log(`Webhook Error: ${error.message}`)
  return res.status(400).send(`Webhook ErrorL ${error.message}`)
}
severe summit
digital hornet
#

Yep!```
import Stripe from 'stripe';
import { buffer } from 'micro'
import { PrismaClient } from '@prisma/client';

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

#

You can ignore Prisma thats just my Database connecter

brisk karma
#

@digital hornet thank you so much for sharing your approach!

severe summit
# digital hornet Yep!``` import Stripe from 'stripe'; import { buffer } from 'micro' import { Pri...

its still not working for meπŸ₯²

  const stripe = new Stripe(endpointsConfig.STRIPE_SECRET_KEY, {
    apiVersion: "2022-11-15",
  });
  if (req.method === "POST") {
    const buf = await buffer(req);
    const sig = req.headers["stripe-signature"];
    const webhookSecret: string = endpointsConfig.STRIPE_WEBHOOK_SECRET;
    let event;
    try {
      if (!sig || !webhookSecret) return;
      event = stripe.webhooks.constructEvent(buf, sig, webhookSecret);
    } catch (error: any) {
      return res.status(400).send(`Webhook ErrorL ${error.message}`);
    }

this my code, do you see anything that could be wrong maybe?

digital hornet
#

Are you deployed to vercel and using environment variables there? Im confused to where the endpointsConfig are coming from. Also make sure you are using the right API secrets.

severe summit
#

yeah it is deployed to vercel

#

endpointsConfig is just the way i structured my .env:

export default {
process.env.NEXT_PUBLIC_STRIPE_SECRET_KEY ?? "",
  STRIPE_WEBHOOK_SECRET: process.env.NEXT_PUBLIC_STRIPE_WEBHOOK_SECRET ?? "",
};
#

but ive checked, theyre stored correctly in the env variables on vercel

digital hornet
#

Give me one sec im looking through my code base

severe summit
#

ofc, really appreciate your help!

digital hornet
#

I see one error. The stripe secret key should NOT be prefixed with Next_public. it should be STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SIGNING_SECRET

severe summit
#

logging the webhooksecret and secret key gives me this:

#

which looks correct to me

digital hornet
#

This is a server function, you are not using a client component.

severe summit
#

like i said, logging the values does give the output i'd expect

digital hornet
#

Yes but for the grand scheme of things, only the stripe publishable key should have NEXT_PUBLIC

#

And yes those two look right

#

Your initial problem really looks like the signing sig isnt matching. Is your webhook in livemode by chance?

severe summit
#

you mean instead of testmode right?

#

if so, then no testmode is enabled

digital hornet
#

If there is nothing confidential can you post the full TS or JS for your webhook page

severe summit
#

yes hold up let me clean it a bit

digital hornet
#

My next is deployed locally, so i dont use vercel but have you tried restarting the instance?

severe summit
#
import Stripe from "stripe";
import { NextApiRequest, NextApiResponse } from "next";
import endpointsConfig from "../../../endpoints.config";
import { sendWebhook } from "@/utils/sharedFunctions";
import { buffer } from "micro";

const handler = async (
  req: NextApiRequest,
  res: NextApiResponse
): Promise<void> => {
  const stripe = new Stripe(endpointsConfig.STRIPE_SECRET_KEY, {
    apiVersion: "2022-11-15",
  });
  if (req.method === "POST") {
    const buf = await buffer(req);
    const sig = req.headers["stripe-signature"];
    const webhookSecret: string = endpointsConfig.STRIPE_WEBHOOK_SECRET;
    let event;
    try {
      await sendWebhook(
        `3 pls, ${webhookSecret}, ${endpointsConfig.STRIPE_SECRET_KEY}`
      );
      if (!sig || !webhookSecret) return;
      event = stripe.webhooks.constructEvent(buf, sig, webhookSecret);
      await sendWebhook(`4 pls`);
    } catch (error: any) {
      return res.status(400).send(`Webhook ErrorL ${error.message}`);
    }
    await sendWebhook(`βœ… Success: ${event.id}`);

    // Cast event data to Stripe object
    if (event.type === "payment_intent.succeeded") {
      const stripeObject: Stripe.PaymentIntent = event.data
        .object as Stripe.PaymentIntent;
      await sendWebhook(`πŸ’° PaymentIntent status: ${stripeObject.status}`);
    } else if (event.type === "charge.succeeded") {
      const charge = event.data.object as Stripe.Charge;
      await sendWebhook(`πŸ’΅ Charge id: ${charge.id}`);
    } else {
      console.warn(`πŸ€·β€β™€οΈ Unhandled event type: ${event.type}`);
    }
    res.status(200).json({ received: true });
  } else {
    res.setHeader("Allow", "POST");
    res.status(405).end("Method Not Allowed");
  }
};

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

export default handler;
#

im using the sendWebhook function as console.log

digital hornet
#

Like restarting the server.

severe summit
#

i dont know how i'd do that tbh. but im redeploying after each change, so i' would think that isnt an issue?

severe summit
digital hornet
#

so sendWebhook is just an async console.log()?

severe summit
#

yeah, it sends messages to my discord server

#

since i cant just console.log on the server

digital hornet
#

I'm just spitballing but because JS runs sequentially try putting the bodyparser config at the top of the file.

severe summit
#

haha i had the same thought earlier

#

but when i tried it my setup was a bit different, so let me try again

digital hornet
#

Also im not really seeing any differences besides your sendWebhook and you dont need NextAPIRequest and Response. They can just be req and res

#

Are you on next 13 or 12?

severe summit
#

13.4.3

severe summit
digital hornet
#

I don't think it would matter that much but you never know

#

I've run into hurdles like that many times working with JS so anythings possible

severe summit
#

i changed the order btw, did not help

severe summit
#

no, no difference

#

god this is annoying

digital hornet
#

I really wish I could provide more insight. I develop locally and then push to a remote repo so I can use the Stripe CLI and VScode extension to debug. You might find some more help in the Next.Js discord. There are people more familiar with the Vercel deployments than I am. Im sorry

severe summit
#

oh yeah, thats a good idea.

#

no worries, thanks a ton for all your help and effort!

digital hornet
#

Hope you find a solution!

opaque harnessBOT
severe summit
#

hi @ruby sandal

severe summit
ruby sandal
#

Can you summarize what you're currently blocked on and the error you're receiving? In many threads right now

severe summit
#

toby has confirmed my keys should be correct, but still not working

#
      event = stripe.webhooks.constructEvent(
        buf,
        sig,
        webhookSecret
      );

this line always fails

ruby sandal
#

Can you log buf?

#

Are you sure that's the raw request body?

severe summit
#
{
  "id": "evt_1N9UffCPrZGPY6dAHpG70HXN",
  "object": "event",
  "api_version": "2022-11-15",
  "created": 1684507963,
  "data": {
    "object": {
      "id": "cs_test_a1xdeLXWC7D81PmdOAvSqSLpIgHkKoXn0lBHnfOjISCqmxIbTL2NqMAAxH",
      "object": "checkout.session",
      "after_expiration": null,
      "allow_promotion_codes": false,
      "amount_subtotal": 100,
      "amount_total": 100,
      "automatic_tax": {
        "enabled": true,
        "status": "complete"
      },
      "billing_address_collection": "auto",
      "cancel_url": "https://stripe.com",
      "client_reference_id": null,
      "consent": null,
      "consent_collection": {
        "promotions": "none",
        "terms_of_service": "none"
      },
      "created": 1684507925,
      "currency": "eur",
      "currency_conversion": null,
      "custom_fields": [

      ],
      "custom_text": {
        "shipping_address": null,
        "submit": null
      },
      "customer": null,
      "customer_creation": "if_required",
      "customer_details": {
        "address": {
          "city": null,
          "country": "NL",
          "line1": null,
          "line2": null,
          "postal_code": null,
          "state": null
        },
        "email": "justusvk@hotmail.com",
        "name": "jhkj",
        "phone": null,
        "tax_exempt": "none",
        "tax_ids": [

        ]
      },
      "customer_email": null,
      "expires_at": 1684594325,
      "invoice": null,
      "invoice_creation": {
        "enabled": false,
        "invoice_data": {
          "account_tax_ids": null,
          "custom_fields": null,
          "description": null,
          "footer": null,
          "metadata": {
          },
          "rendering_options": null
        }
      },
      "livemode": false,
      "locale": "auto",
      "metadata": {
      },
      "mode": "payment",
      "payment_intent": "pi_3N9UfTCPrZGPY6dA1d4hc0my",
      "payment_link": "plink_1N9RUDCPrZGPY6dA75J5yXb6",
      "payment_method_collection": "always",
      "payment_method_options": {
      },
      "payment_method_types": [
        "card",
        "bancontact",
        "eps",
        "giropay",
        "ideal",
        "link"
      ],
      "payment_status": "paid",
      "phone_number_collection": {
        "enabled": false
      },
      "recovered_from": null,
      "setup_intent": null,
      "shipping_address_collection": null,
      "shipping_cost": null,
      "shipping_details": null,
      "shipping_options": [

      ],
      "status": "complete",
      "submit_type": "auto",
      "subscription": null,
      "success_url": "https://stripe.com",
      "total_details": {
        "amount_discount": 0,
        "amount_shipping": 0,
        "amount_tax": 0
      },
      "url": null
    }
  },
  "livemode": false,
  "pending_webhooks": 1,
  "request": {
    "id": null,
    "idempotency_key": null
  },
  "type": "checkout.session.completed"
}

this is buf

#

i hope this is safe to share lol

brisk karma
#

Yup, that's fine to share (still keeping an eye on the thread as bandwidth permits). It looks a little odd there is what appears to be the last portion of an Event appended to the beginning of another, were multiple objects being logged out there?

severe summit
#

oh you're right, thats my bad

#

i edited it, i think it should be okay now

brisk karma
#

Cool cool, just wanted to make sure multiple events weren't getting concatenated together

severe summit
#

@ruby sandal, do you see anything wrong with this?

ruby sandal
#

Looks fine

#

Can you check that your signing secret doesn't have any whitespace in it

#

Or newline characters

#

Make sure it's just the string retrieved from the dashboard

severe summit
#

oh my god

#

you are amazing

severe summit
#

const secretKey = endpointsConfig.STRIPE_SECRET_KEY.replace(
/\s/g,
""
).replace(/\n/g, "\n");
i added this, and now it works

ruby sandal
#

Ah yeah that's more common than you would think haha

severe summit
#

damn, no clue how thats possible even, there are no spaces or newlines in my env variables

#

it would be nice if that could be mentioned in the docs i think

#

but im happy, thanks a lot!