为何Firebase无法检测到ProjectId?已配置ServiceAccount仍报错
已配置ServiceAccount但Firebase Admin仍检测不到ProjectId的解决办法
问题情况
已经配置了ServiceAccount信息,但Firebase还是提示找不到ProjectId,报错内容:
Unable to detect a Project Id in the current environment.
对应的代码如下:
import { buffer } from 'micro' import { initializeApp, getApps, getApp, cert} from 'firebase-admin/app' import { getFirestore , FieldValue } from 'firebase-admin/firestore' const serviceAccount = require('../../permissions.json') const app = !getApps().length ? initializeApp({ credentials: cert(serviceAccount), }) : getApp() const db = getFirestore(app) const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY) const endpointSecret = process.env.STRIPE_SIGNING_SECRET const fullfillOrder = async (session) => { return db .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), timestamp: FieldValue.serverTimestamp() }) } export default async function webhook(req, res) { if (req.method === 'POST') { const reqBuffer = await buffer(req) const payload = reqBuffer.toString() const sig = req.headers['stripe-signature'] let event try { event = stripe.webhooks.constructEvent(payload, sig, endpointSecret) } catch (err) { console.log(err.message) return res.status(400).send(`Error : ${err.message}`) } if (event.type === 'checkout.session.completed') { const session = event.data.object return fullfillOrder(session) .then(() => res.status(200)) .catch((err) => { res.status(400) console.log(`Webhook error ${err.message}`) }) } } }
原因分析
这个报错大多是Firebase Admin没法自动从ServiceAccount文件或环境里拿到ProjectId,常见原因有这几个:
- ServiceAccount的JSON文件里缺了
project_id字段,或者字段名写错了 - 初始化App的时候没显式指定
projectId,自动提取失败 - 环境变量
GOOGLE_CLOUD_PROJECT或GCLOUD_PROJECT没设置
解决办法
方法1:初始化时显式指定ProjectId
直接在初始化Firebase Admin App的时候,把ServiceAccount里的project_id传进去,修改初始化部分的代码:
const app = !getApps().length ? initializeApp({ credentials: cert(serviceAccount), projectId: serviceAccount.project_id // 显式指定项目ID }) : getApp()
方法2:检查ServiceAccount文件
打开permissions.json,确认里面包含project_id字段,格式应该类似:
{ "type": "service_account", "project_id": "你的项目ID", // 其他字段... }
如果没有这个字段,就去Firebase控制台重新下载一份ServiceAccount文件,确保导出流程正确。
方法3:设置环境变量
如果部署在云平台(比如Vercel、Cloud Functions),可以设置环境变量GOOGLE_CLOUD_PROJECT或GCLOUD_PROJECT,值为你的Firebase项目ID,Firebase Admin会自动读取这个变量获取ProjectId。
修改后的完整代码
另外原代码里res.status(200)没有返回内容,容易导致请求超时,这里一起修正:
import { buffer } from 'micro' import { initializeApp, getApps, getApp, cert} from 'firebase-admin/app' import { getFirestore , FieldValue } from 'firebase-admin/firestore' const serviceAccount = require('../../permissions.json') // 显式指定projectId解决检测失败问题 const app = !getApps().length ? initializeApp({ credentials: cert(serviceAccount), projectId: serviceAccount.project_id }) : getApp() const db = getFirestore(app) const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY) const endpointSecret = process.env.STRIPE_SIGNING_SECRET const fullfillOrder = async (session) => { return db .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), timestamp: FieldValue.serverTimestamp() }) } export default async function webhook(req, res) { if (req.method === 'POST') { const reqBuffer = await buffer(req) const payload = reqBuffer.toString() const sig = req.headers['stripe-signature'] let event try { event = stripe.webhooks.constructEvent(payload, sig, endpointSecret) } catch (err) { console.log(err.message) return res.status(400).send(`Error : ${err.message}`) } if (event.type === 'checkout.session.completed') { const session = event.data.object return fullfillOrder(session) .then(() => res.status(200).send('处理成功')) .catch((err) => { console.log(`Webhook错误 ${err.message}`) return res.status(400).send(`Webhook错误: ${err.message}`) }) } // 处理其他事件类型 return res.status(200).send('已接收') } else { // 拒绝非POST请求 res.setHeader('Allow', 'POST') return res.status(405).send('仅允许POST请求') } }
内容的提问来源于stack exchange,提问作者Pickez
相关产品推荐
相关产品推荐

