-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Vercel Production API bodyParser (stripe webhooks) #1410
Comments
Interesting. This could be our fault. I think we should determine what AWS lambda is doing with the |
I tried running both of these and it did not work! still errors on vercel production import { stripe } from 'src/lib/stripe'
import { buffer } from 'micro'
export const config = {
api: {
bodyParser: false,
},
}
export const handler = async (event) => {
// Only allow POST method
if (event.httpMethod !== 'POST') {
return { statusCode: 404 }
}
const buf = await buffer(event)
// Check signing signature
const sig = event.headers['stripe-signature']
console.log('==========')
console.log(typeof event.body)
console.log('==========')
let stripeEvent
try {
stripeEvent = stripe.webhooks.constructEvent(
buf.toString(),
sig,
webhookSecret
)
} catch (err) {
// On error, log and return the error message
// console.log(`Webhook Error message: ${err.message}`)
return {
statusCode: 400,
body: `Event Error: ${err.message}`,
}
}
// Successfully constructed event
console.log('Webhook Success:', stripeEvent.id)
return {
statusCode: 200,
body: `Event Success: ${stripeEvent.id}`,
}
} I also tried without buff export const config = {
api: {
bodyParser: false,
},
}
export const handler = async (event) => {
// Only allow POST method
if (event.httpMethod !== 'POST') {
return { statusCode: 404 }
}
// Check signing signature
const sig = event.headers['stripe-signature']
console.log('==========')
console.log(typeof event.body)
console.log('==========')
let stripeEvent
try {
stripeEvent = stripe.webhooks.constructEvent(
process.env.NODE_ENV === 'production'
? JSON.stringify(event.body)
: event.body,
sig,
webhookSecret
)
} catch (err) {
// On error, log and return the error message
// console.log(`Webhook Error message: ${err.message}`)
return {
statusCode: 400,
body: `Event Error: ${err.message}`,
}
}
// Successfully constructed event
console.log('Webhook Success:', stripeEvent.id)
return {
statusCode: 200,
body: `Event Success: ${stripeEvent.id}`,
}
} |
@burnsy can you confirm that you've enabled https://vercel.com/docs/runtimes#advanced-usage/advanced-node-js-usage/aws-lambda-api this (I guess so, because I'd imagine otherwise Redwood wouldn't run on vercel?)
With AWS lambda, exports.handler = async ({ body, headers }) => {
try {
const stripeEvent = stripe.webhooks.constructEvent(
body,
headers['stripe-signature'],
process.env.STRIPE_WEBHOOK_SECRET
);
// [...] I wonder if Vercel is not confirming to this spec. @leerob would you happen to know the shape of Maybe try disabling the Node.js helpers: https://vercel.com/docs/runtimes#advanced-usage/advanced-node-js-usage/disabling-helpers-for-node-js |
hey @thorwebdev, {
"version": 2,
"regions": ["lhr1"],
"github": {
"enabled": true
},
"build": {
"env": {
"NODEJS_HELPERS": "0",
"NODEJS_AWS_HANDLER_NAME": "handler"
}
}
} it still does not work after these config changes.
maybe the answer is in here @vercel/redwood. When I have looked for it I could not find the code. |
@styfle Investigated this issue on our side:
|
I arrived in this issue through Google search and managed to get stripe webhooks working in vercel through this blog post https://maxkarlsson.dev/blog/2020/12/verify-stripe-webhook-signature-in-next-js-api-routes/. |
@burnsy I had this exact same issue with all my functions hosted on Vercel. I found that when working with Redwood locally the body of a function is decoded, however, once deployed to Vercel, the body may be base64 encoded. I have the following code in my Stripe webhook function to handle both local development and Vercel: export const handler = async (req, context) => {
// Retrieve the event by verifying the signature using the raw body and secret.
let event;
const parsedBody = req.isBase64Encoded
? Buffer.from(req.body, 'base64').toString('utf-8')
: req.body;
try {
event = stripeClient.webhooks.constructEvent(
parsedBody,
req.headers['stripe-signature'],
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
console.log(err);
console.log(`⚠️ Webhook signature verification failed.`);
console.log(`⚠️ Check the env file and enter the correct webhook secret.`);
return {
statusCode: 400,
};
}
...
}; Hope that helps as well! |
Thanks, everyone, for the help here!! |
fwiw — this Issue is a good example of something that can make for a great how-to forum post as well. |
@burnsy and @AlecOrtega my webhook verifiers jut got merged into main for the upcoming v0.31 release:
and this is the Stripe verifier that is a timestamp + scheme: I've been testing locally and on Netlify but not yet on Vercel -- and this one I had not yet tested with a real Stripe webhook. It should be available in a canary release and this is an example of a Discourse webhook verification that uses the sha256 verifier (like GitHub uses): import { verifyEvent, VerifyOptions } from '@redwoodjs/api/webhooks'
import { logger } from 'src/lib/logger'
/**
* The handler function is your code that processes http request events.
* You can use return and throw to send a response or error, respectively.
*
* Important: When deployed, a custom serverless function is an open API endpoint and
* is your responsibility to secure appropriately.
*
* @see {@link https://redwoodjs.com/docs/serverless-functions#security-considerations|Serverless Function Considerations}
* in the RedwoodJS documentation for more information.
*
* @typedef { import('aws-lambda').APIGatewayEvent } APIGatewayEvent
* @typedef { import('aws-lambda').Context } Context
* @param { APIGatewayEvent } event - an object which contains information from the invoker.
* @param { Context } context - contains information about the invocation,
* function, and execution environment.
*/
export const handler = async (event, _context) => {
logger.info('Invoked discourseWebhook function')
try {
const options = {
signatureHeader: 'X-Discourse-Event-Signature',
} as VerifyOptions
verifyEvent('sha256Verifier', {
event,
secret: 'i-keep-this-a-secret',
options,
})
} catch (error) {
return {
statusCode: 401,
}
}
logger.debug({ headers: event.headers }, 'Headers')
logger.debug({ payload: JSON.parse(event.body) }, 'Body payload')
// do something with payload
return {
statusCode: 200,
body: JSON.stringify({
data: 'discordWebhook function',
}),
}
} For Stripe, but swap out: const options = {
signatureHeader: 'stripe-signature',
} as VerifyOptions
verifyEvent('timestampSchemeVerifier', {
event,
secret: process.env.STRIPE_WEBHOOK_SECRET,
options,
}) I hadn't run across the base64case yet, but I could see having to do that here: https://github.com/redwoodjs/redwood/blob/main/packages/api/src/webhooks/index.ts#L73 Is there a way you could help me test this? |
@dthyresson Absolutely! I can try this out with the hooks I've seen this issue, deploy it up to a preview environment, and see if I see the above issue or if things come through. I'll try to get to it tomorrow evening or the following day. |
Thanks! After review, I think I am going to add the base64 conversion in the verifyEvent to be safe but prob tomorrow. |
@AlecOrtega you just saved my life with that |
Hello,
When i have been building my application everything worked fine and no problems with stripe passing through webhooks to redwood API. Once Built to Vercel, the webhooks no longer work, and it's critical they do work!
stripe.webhooks.constructEvent wants event.body to be passed into it unparsed, (works fine on dev) but when pushed to production on Vercel. It seems some kind of processing is happening to the event.body meaning that stripe will not accept it.
to what I understand is that stripe wants rawJSON but something is parsing that JSON.
this would be the solution if bodyParser was used with express
The text was updated successfully, but these errors were encountered: