You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 18:07:00