无法将数据从 Stripe webhook 发送到 Firebase
Posted
技术标签:
【中文标题】无法将数据从 Stripe webhook 发送到 Firebase【英文标题】:Unable to send data from Stripe webhook to Firebase 【发布时间】:2021-09-30 16:37:30 【问题描述】:我创建了一个 Stripe webhook,我想在购买 Stripe 时将数据写入 Firebase,但它无法正常工作,尽管付款总是成功,但数据并未发送到 Firebase 数据库。
在 Stripe 控制台中,我收到以下错误:
Webhook 错误:未找到与有效负载的预期签名匹配的签名。您是否传递了从 Stripe 收到的原始请求正文? https://github.com/stripe/stripe-node#webhook-signing
以下是我的 webhook 的代码:
import buffer from "micro";
import * as admin from "firebase-admin";
// Secure a connection to firebase
const serviceAccount = require("../../../permession.json");
const app = !admin.apps.length
? admin.initializeApp(
credential: admin.credential.cert(serviceAccount),
)
: admin.app();
// Stripe
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
const endpointSecurit = process.env.STRIPE_SIGNING_SECRET;
const fullfillOrder = async (session) =>
console.log("Fullfilling Order!!!");
return app
.firestore()
.collection("users")
.doc(session.metadata.email)
.collection("orders")
.doc(session.id)
.set(
amount: session.amount_total / 100,
amount_shipping: session.total_details_amount_shipping / 100,
images: JSON.parse(session.metadata.images),
title: JSON.parse(session.metadata.titles),
timestamp: admin.firestore.FieldValue.serverTimestamp(),
)
.then(() =>
console.log(`SUCCESS: Order $session.id has been added to DB!`);
);
;
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 (came from stripe)
try
event = await stripe.webhooks.constructEvent(
payload,
sig,
endpointSecurit
);
catch (e)
console.log("ERROR", e.message);
return res.status(400).send( message: "Webhook error: " + e.message );
if (event.type === "checkout.session.completed")
const session = event.data.object;
// Fullfill the order
return fullfillOrder(session)
.then(() => res.status(200))
.catch((e) =>
res.status(400).send( message: "WEBHOOK_ERROR: " + e.message )
);
;
export const config =
api:
bodyParser: false,
externalResolver: true,
,
;
感谢您的帮助。
【问题讨论】:
你可以试试const payload = req.rawBody
吗?
@Dharmaraj 我试过了,但它给出了同样的错误。
你能与 req.rawBody 分享更新的代码吗?
const event = stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret);
这应该可以。
在 Node 中获取实际的原始主体非常困难,因为主体很容易在您获得它之前进行修改。如果上面提出的解决方案不起作用,这里有很多不同的可能解决方案来解决这个问题:github.com/stripe/stripe-node/issues/341
【参考方案1】:
如果此代码已托管在 Google Cloud Functions 上,您必须考虑在处理请求之前由 Functions Framework(GCF docs、Firebase docs)完成的自动正文解析转到 Next.js。因为 body 已经被消费了,bodyParser: false
指令对于已经处理的类型是没有意义的。
为了帮助依赖它的模块,框架将请求未解析主体的Buffer
附加为属性req.rawBody
。
如果您使用的是 linter 或 TypeScript,req
对象可能会被键入为 express.Request
对象,这可能会报告此 rawBody
属性不存在,因为它不再是 Express 核心的一部分.您需要将其合并到类型中或将类型覆盖为 express.Request | rawBody: Buffer
以使其静音。
从您的代码中提取相关行:
const requestBuffer = await buffer(req);
const payload = requestBuffer.toString();
const sig = req.headers["stripe-signature"];
const event = await stripe.webhooks.constructEvent(
payload,
sig,
endpointSecurit
);
应该替换为
const sig = req.headers["stripe-signature"];
const event = await stripe.webhooks.constructEvent(
req.rawBody.toString(),
sig,
endpointSecurit
);
确保还检查您的环境变量是否已正确配置。
const endpointSecurit = process.env.STRIPE_SIGNING_SECRET;
if (typeof endpointSecurit !== "string")
console.error("Unexpected value for STRIPE_SIGNING_SECRET");
// potentially throw an error here
旁注
如果使用 Cloud Functions 的 Firebase 分支,您还可以使用 functions.config()
来处理您的机密,而不是使用环境变量。
const STRIPE_SECRET = functions.config().stripe.secret;
const STRIPE_WEBHOOK_SECRET = functions.config().stripe.webhook_secret;
您不应默默地忽略不是使用POST
发送的请求,而应以相应的错误进行响应:
if (req.method !== "POST")
res.status(405).set('Allow', 'POST').send("");
return;
不要忘记使用.end()
、.send()
等来结束您的请求!
res.status(200).send("");
如果您开始处理多种 webhook 事件类型,请考虑改用事件处理程序对象来帮助组织您的代码,而不是使用大型 if
-else
/switch
树:
// before the route handler
const eventHandlers = ; // Record<string, (event, req, res) => Promise<unknown> | unknown>
eventHandlers["checkout.session.completed"] = async (event, req, res) =>
const session = event.data.object;
return fullfillOrder(session)
.then(() => res.status(200))
.catch((e) =>
res.status(400).send( message: "WEBHOOK_ERROR: " + e.message )
);
// in the route handler
const handler = eventHandlers[event.type];
if (!handler)
console.error('Ignored unexpected event type of ' + event.type);
res.status(200).send("");
return;
try
await Promise.resolve(handler(event, req, res))
catch (err)
console.error("Unexpected error in event handler for event type of ' + event.type, err);
res.status(500).send( message: "Internal server error" );
【讨论】:
以上是关于无法将数据从 Stripe webhook 发送到 Firebase的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 ngrok 测试接收 Stripe webhook
curl 作为 Zapier Webhook 到 Stripe