Stripe webhook失败- webhook错误:504



Stripe webhook失败,我认为这是因为Stripe没有从我的webhook api端点收到'success'响应。

错误是:

Test webhook error: 504
An error occurred with your deployment
FUNCTION_INVOCATION_TIMEOUT

我使用Nextjs及其构建在pages/api/createOrder文件夹结构来创建api。这是我的createOrder webhook的样子:

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,
},
}

我发现了一些解决方案,如何告诉条纹,webhook调用已收到,但没有一个工作。现在我使用:

res.status(200).json({ received: true })

也许问题出在别的地方?我想指出的是,Order被创建,Ad被更新,所以webhook工作如预期,除了它失败。

在事件驱动的体系结构(webhooks)中,您通常希望尽可能快地返回200给提供者,然后处理事件。这需要一个队列来解耦。

您可以查看https://hookdeck.com/。他们的产品似乎就是这样做的(在200毫秒内返回200到Stripe),所以你不会得到超时错误。

看起来FUNCTION_INVOCATION_TIMEOUT错误是特定于Vercel,所以你的应用程序托管在那里?显然,超时时间相当低,您可以尝试增加它们。


当你收到一个webhook时,你通常想要做的是:

  1. 存储webhook(例如在数据库中)
  2. 发送HTTP响应
  3. 实际处理webhook(例如在后台作业中)

这使响应时间更快,可能解决您的问题。听起来你的setTimeout解决方案是这样做的——它可能在处理程序完成之前发送响应,这将比把它放在处理程序的末尾要快。

经过2个月的调试,我找到了一个解决方案。

我认为问题在于AWS似乎很慢。我添加了setTimeout来通知Stripe连接成功,现在它可以工作了!

setTimeout(() => {
// 3. Notify Stripe that event recieved.
res.json({ received: true })
}, 2000)

如果有人有更好的解决方案,请告诉我们:)

最新更新