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

如何在Cloud Function中正确查询Firebase实时数据库获取定价数据?

问题描述

我尝试根据存储定价数据所属的用户进行查询,将结果返回到Stripe结账的Cloud Function中,但一直收到变量未赋值的错误。查了相关文档后还是困惑,参考了一些类似实现的资源反而把代码搞乱了。请问如何正确从数据库获取定价信息并填入Stripe的结账参数里?

实时数据库数据结构

studiopick
   studio
     users
        Gcsh31DCGAS2u2XXLuh8AbwBeap1
          email : "Test@gmail.com"
          firstName : "Test"
          lastName : "one"
          phoneNumber : "2223334567"
          prices
          |   roomA
          |     serviceOne
          |       numberInput : "300"
          |       serviceType : "mix n master"
           studioName : "Studio One"
           uid : "Gcsh31DCGAS2u2XXLuh8AbwBeap1"

现有Cloud Function代码

const functions = require("firebase-functions");
const admin = require("firebase-admin");

let price;
let info;

admin.initializeApp(functions.config().firebase);

exports.createStripeCheckout = functions.https.onCall(async (data, context) => {
  const querySnapshot = await ref
    .orderByChild("numberInput, serviceInput")
    .equalTo(price, info)
    .once("value");
  // Stripe init
  const stripe = require("stripe")(functions.config().stripe.secret_key);
  const session = await stripe.checkout.sessions.create({
    payment_method_types: ["card"],
    mode: "payment",
    success_url: "http://localhost:5500/success",
    cancel_url: "http://localhost:5500/cancel",
    shipping_address_collection: {
      allowed_countries: ["US"],
    },
    line_items: [
      {
        quantity: 1,
        price_data: {
          currency: "usd",
          unit_amount: price * 100, // 10000 = 100 USD
          product_data: {
            name: info,
          },
        },
      },
    ],
  });

  return {
    id: session.id,
  };
});

exports.stripeWebhook = functions.https.onRequest(async (req, res) => {
  const stripe = require("stripe")(functions.config().stripe.token);
  let event;

  try {
    const whSec = functions.config().stripe.payments_webhook_secret;

    event = stripe.webhooks.constructEvent(
      req.rawBody,
      req.headers["stripe-signature"],
      whSec
    );
  } catch (err) {
    console.error("⚠️ Webhook signature verification failed.");
    return res.sendStatus(400);
  }

  const dataObject = event.data.object;

  await admin.firestore().collection("orders").doc().set({
    checkoutSessionId: dataObject.id,
    paymentStatus: dataObject.payment_status,
    shippingInfo: dataObject.shipping,
    amountTotal: dataObject.amount_total,
  });

  return res.sendStatus(200);
});

解决方案

原代码核心问题

  1. 全局变量price、info未初始化,也没有从数据库赋值的逻辑
  2. 数据库查询的ref未定义,且orderByChild/equalTo用法错误(Firebase实时数据库不支持同时按多个字段排序/匹配)
  3. 未利用Callable Function的context.auth.uid获取当前用户ID,无法定位到对应用户的定价数据

修复后的完整代码

const functions = require("firebase-functions");
const admin = require("firebase-admin");
// 提前引入Stripe,避免重复require
const stripe = require("stripe")(functions.config().stripe.secret_key);

admin.initializeApp(functions.config().firebase);

exports.createStripeCheckout = functions.https.onCall(async (data, context) => {
  // 验证用户是否已登录
  if (!context.auth) {
    throw new functions.https.HttpsError(
      "unauthenticated",
      "用户未登录,无法创建结账会话"
    );
  }

  const userId = context.auth.uid;
  // 构建数据库引用,定位到当前用户的serviceOne定价节点
  const priceRef = admin.database().ref(`studiopick/studio/users/${userId}/prices/roomA/serviceOne`);
  
  try {
    // 读取定价数据
    const snapshot = await priceRef.once("value");
    const priceData = snapshot.val();

    if (!priceData) {
      throw new functions.https.HttpsError(
        "not-found",
        "未找到该用户的定价信息"
      );
    }

    // 转换价格为数字(数据库中是字符串)
    const unitAmount = parseInt(priceData.numberInput) * 100;
    const serviceName = priceData.serviceType;

    // 创建Stripe结账会话
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ["card"],
      mode: "payment",
      success_url: "http://localhost:5500/success",
      cancel_url: "http://localhost:5500/cancel",
      shipping_address_collection: {
        allowed_countries: ["US"],
      },
      line_items: [
        {
          quantity: 1,
          price_data: {
            currency: "usd",
            unit_amount: unitAmount,
            product_data: {
              name: serviceName,
            },
          },
        },
      ],
    });

    return { id: session.id };
  } catch (error) {
    console.error("创建结账会话失败:", error);
    throw new functions.https.HttpsError(
      "internal",
      "创建结账会话时发生错误",
      error.message
    );
  }
});

exports.stripeWebhook = functions.https.onRequest(async (req, res) => {
  const stripeWebhook = require("stripe")(functions.config().stripe.token);
  let event;

  try {
    const whSec = functions.config().stripe.payments_webhook_secret;
    event = stripeWebhook.webhooks.constructEvent(
      req.rawBody,
      req.headers["stripe-signature"],
      whSec
    );
  } catch (err) {
    console.error("⚠️ Webhook签名验证失败:", err);
    return res.sendStatus(400);
  }

  const dataObject = event.data.object;

  await admin.firestore().collection("orders").doc().set({
    checkoutSessionId: dataObject.id,
    paymentStatus: dataObject.payment_status,
    shippingInfo: dataObject.shipping,
    amountTotal: dataObject.amount_total,
  });

  return res.sendStatus(200);
});

关键修复点说明

  1. 用户身份验证:通过context.auth.uid获取当前登录用户ID,确保只查询该用户的定价数据
  2. 正确的数据库引用:直接定位到用户的serviceOne定价节点,无需复杂查询(因为数据结构是按用户UID组织的)
  3. 数据类型转换:将数据库中存储的字符串价格转换为整数,再乘以100适配Stripe的分单位
  4. 错误处理:添加了用户未登录、定价数据不存在的错误捕获,返回标准化的HttpsError
  5. 代码优化:提前引入Stripe,避免在函数内部重复require

内容的提问来源于stack exchange,提问作者Grmln

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 06:45:36