#juco
1 messages Β· Page 1 of 1 (latest)
Hi π I'm not too familiar with nextjs specifically, but typically when we see that error encountered it is for one of two reasons:
-
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.
-
The contents of the request body being passed to
constructEventdo 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.
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?
about the second point;
ive tried lots of different ways to get the body. currently im trying with the bodyParser disabled, and then using micro to do buffer(req), and then pass that constructEvent(body.toString()).
but this isnt working
That looks right
also quick question, what is the difference between the secret and publishable key? im assuming i should use the secret
@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}`)
}
appreciate that! are you using micros buffer function?
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
The secret key is using for making API requests from your server, it should be protected and remain a secret. You publishable key is used for making requests from stripe.js, our frontend library, that key isn't as sensitive since by nature it needs to be sent with client-side code.
@digital hornet thank you so much for sharing your approach!
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?
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.
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
Give me one sec im looking through my code base
ofc, really appreciate your help!
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
This is a server function, you are not using a client component.
i think thats just the way its structured in next.js?
like i said, logging the values does give the output i'd expect
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?
If there is nothing confidential can you post the full TS or JS for your webhook page
yes hold up let me clean it a bit
My next is deployed locally, so i dont use vercel but have you tried restarting the instance?
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
im not sure what that means?
Like restarting the server.
i dont know how i'd do that tbh. but im redeploying after each change, so i' would think that isnt an issue?
so its not reaching the sendWebhook(4 pls) btw
so sendWebhook is just an async console.log()?
yeah, it sends messages to my discord server
since i cant just console.log on the server
I'm just spitballing but because JS runs sequentially try putting the bodyparser config at the top of the file.
haha i had the same thought earlier
but when i tried it my setup was a bit different, so let me try again
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?
13.4.3
thats just typing right? that shouldnt matter too much?
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
i changed the order btw, did not help
okay let me try
no, no difference
god this is annoying
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
oh yeah, thats a good idea.
no worries, thanks a ton for all your help and effort!
Hope you find a solution!
hi @ruby sandal
any chance you can see an issue with my code?
Can you summarize what you're currently blocked on and the error you're receiving? In many threads right now
yes, Im trying to use NextJS, but im only getting No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe?.
i followed this example: https://github.com/stripe/stripe-node/blob/master/examples/webhook-signing/nextjs/pages/api/webhooks.ts,
but this isnt working for me
toby has confirmed my keys should be correct, but still not working
event = stripe.webhooks.constructEvent(
buf,
sig,
webhookSecret
);
this line always fails
{
"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
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?
Cool cool, just wanted to make sure multiple events weren't getting concatenated together
yes i checked again, now its correct
@ruby sandal, do you see anything wrong with this?
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
this was itπ
const secretKey = endpointsConfig.STRIPE_SECRET_KEY.replace(
/\s/g,
""
).replace(/\n/g, "\n");
i added this, and now it works
Ah yeah that's more common than you would think haha