Reputation: 377
Stripe webhook fails and i assume its because Stripe doesnt recieve 'success' response from my webhook api endpoint.
Error is:
Test webhook error: 504
An error occurred with your deployment
FUNCTION_INVOCATION_TIMEOUT
I use Nextjs and its build in pages/api/createOrder folder structure to create a api. This is how my createOrder webhook looks like:
import { buffer } from "micro"
const AWS = require("aws-sdk")
AWS.config.update({
accessKeyId: process.env.MY_AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.MY_AWS_SECRET_ACCESS_KEY,
region: process.env.MY_AWS_REGION,
endpoint: process.env.MY_AWS_ENDPOINT,
})
// Establish Stripe connection
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY)
const endpointSecret = process.env.STRIPE_CREATE_ORDER_SIGNING_SECRET
const createOrder = async session => {
console.log("create order - session.id: ", session.id)
console.log(
"create order - session.metadata.userID: ",
session.metadata.userID
)
console.log(
"create order - session.amount_total / 100: ",
session.amount_total / 100
)
const docClient = new AWS.DynamoDB.DocumentClient()
let date = new Date()
// create Order
let orderParams = {
TableName: process.env.ORDER_TABLE_NAME,
Item: {
id: session.id,
userID: session.metadata.userID,
amount: session.amount_total / 100,
adID: session.metadata.adID,
createdAt: date.toISOString(),
updatedAt: date.toISOString(),
},
}
docClient.put(orderParams, function (err, data) {
if (err) {
console.log("Order put err - " + JSON.stringify(err, null, 2))
} else {
console.log("Order put Success - " + JSON.stringify(data, null, 2))
}
})
// create - TTL
const ninetyDays = 1000 * 60 * 60 * 24 * 90
const currTime = Date.now()
const ttlSeconds = Math.ceil((ninetyDays + currTime) / 1000)
// update Ad
let adParams = {
TableName: process.env.AD_TABLE_NAME,
Key: { id: session.metadata.adID },
UpdateExpression: "set paid = :paid, expdate = :expdate",
ExpressionAttributeValues: {
":paid": true,
":expdate": ttlSeconds,
},
ReturnValues: "UPDATED_NEW",
}
docClient.update(adParams, function (err, data) {
if (err) {
console.log("UPDATE Ad err - " + JSON.stringify(err, null, 2))
} else {
console.log("UPDATE Ad Success - " + JSON.stringify(data, null, 2))
}
})
}
export default async (req, res) => {
if (req.method === "POST") {
const requestBuffer = await buffer(req)
const payload = requestBuffer.toString()
const sig = req.headers["stripe-signature"]
let event
// Verify that the EVENT posted came from stripe
try {
event = stripe.webhooks.constructEvent(payload, sig, endpointSecret)
} catch (err) {
console.log("ERROR", err.message)
return res.status(400).send(`Webhook create Order error: ${err.message}`)
}
// Handle the checkout.session.completed event
if (event.type === "checkout.session.completed") {
const session = event.data.object
// Fulfill update Ad -> paid = true and ttl - expdate
return createOrder(session)
.then(() => res.status(200))
.catch(err =>
res.status(400).send(`Create Order Error - ${err.message}`)
)
}
}
// Notify Stripe that req reached api
res.status(200).json({ received: true })
}
export const config = {
api: {
bodyParser: false,
externalResolver: true,
},
}
I found few solutions how to tell Stripe that the webhook call was recieved but none of them worked. Right now im useing:
res.status(200).json({ received: true })
Maybe the problem is located somewhere else? I want to note that Order is created and Ad is updated - so the webhook works as expected except that it fails.
Upvotes: 1
Views: 1402
Reputation:
It looks like the FUNCTION_INVOCATION_TIMEOUT
error is specific to Vercel, so is your app hosted there? Apparently the timeouts are quite low, and you could try increasing them.
Typically what you'd want to do when receiving a webhook is:
This makes the response time faster and might solve your problem. It sounds like your setTimeout
solution is kind of doing this — it's maybe sending the response before the handler is finished, which will be faster than putting it at the end of the handler.
Upvotes: 0
Reputation: 1
In an event-driven architecture (webhooks), you usually want to return a 200 as fast as possible to the provider and then process the event after. This requires a queue to decouple.
You can check out https://hookdeck.com/. Their product seems to do exactly that (returning 200 to Stripe under 200ms) so you don't get Timeout errors.
Upvotes: 0
Reputation: 377
After 2 month of debugging i found a solution.
I think the problem was that AWS seems to be SLOW. I added setTimeout for notifying Stripe that connection was successfull and it now works!
setTimeout(() => {
// 3. Notify Stripe that event recieved.
res.json({ received: true })
}, 2000)
If some1 has better solution plz tell us :)
Upvotes: -1