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

React+Firebase+Stripe订阅功能报错:addDoc()传入无效price值

问题分析与修复

核心问题

  1. 拼写错误:在获取价格数据时,你把 priceId 拼写成了 prceId,导致后续调用 loadCheckout 时传入的 priceId 是 undefined,触发 Firebase 的数据无效错误。
  2. 异步迭代问题:使用 forEach 结合 async/await 会导致 setProducts 在所有价格数据加载完成前就执行,可能造成数据不完整。

修复后的代码

import React, { useEffect, useState } from 'react'
import db from '../firebase'
import './PlansScreen.css'
import { useSelector } from 'react-redux'
import { selectUser } from '../features/userSlice'
import { loadStripe } from "@stripe/stripe-js"

function PlansScreen() {
    const [products, setProducts] = useState({}) // 初始化为对象更合理
    const user = useSelector(selectUser)

    useEffect(() => {
        const fetchProducts = async () => {
            const querySnapshot = await db.collection("products")
                .where("active", "==", true)
                .get();
            
            const products = {};
            // 使用 Promise.all 等待所有价格数据加载完成
            await Promise.all(querySnapshot.docs.map(async productDoc => {
                products[productDoc.id] = productDoc.data();
                const priceSnap = await productDoc.ref.collection('prices').get();
                // 假设每个产品对应一个价格,取第一个即可(如有多个价格可调整逻辑)
                const price = priceSnap.docs[0];
                products[productDoc.id].prices = {
                    priceId: price.id, // 修复拼写错误
                    priceData: price.data()
                };
            }));
            
            setProducts(products);
        };

        fetchProducts();
    }, [])

    const loadCheckout = async (priceId) => {
        // 先校验 priceId 是否存在,避免无效调用
        if (!priceId) {
            alert("无法获取订阅价格,请刷新页面重试");
            return;
        }

        const docRef = await db
            .collection('customers')
            .doc(user.uid)
            .collection("checkout_sessions")
            .add({
                price: priceId,
                success_url: window.location.origin,
                cancel_url: window.location.origin,
            });
        
        docRef.onSnapshot(async (snap) => {
            const { error, sessionId } = snap.data();

            if (error) {
                alert(`发生错误:${error.message}`);
            }

            if (sessionId) {
                const stripe = await loadStripe('pk_test_51NSki0SGjMohrz4zbSg4JN71tlTFUkGLWemybtRS31PP94Tzd08kFLUgzTgmxUGjoPQ36S4orri0J2tT8EY5GQZk00NThIU3fu');
                stripe.redirectToCheckout({ sessionId });
            }
        });
    };

    return (
        <div className='plansScreen'>
            {Object.entries(products).map(([productId, productData]) => (
                <div key={productId} className="plansScreen__plan">
                    <div className="planScreen__info">
                        <h3>{productData.name}</h3>
                        <h5>{productData.description}</h5>
                    </div>
                    <button onClick={() => loadCheckout(productData.prices?.priceId)}>
                        Subscribe
                    </button>
                </div>
            ))}
        </div>
    )
}

export default PlansScreen;

关键修复点说明

  • 拼写修正:将 prceId 改为 priceId,确保价格ID能正确传递到结账函数。
  • 异步逻辑优化:用 Promise.all 替代 forEach 处理异步请求,保证所有产品的价格数据加载完成后再更新状态。
  • 安全校验:在 loadCheckout 中增加 priceId 存在性检查,避免无效的 Firebase 调用。
  • 可选链操作:使用 productData.prices?.priceId 防止因数据未加载完成导致的报错。

关于 Firebase 的疑问

Firebase 本身没有问题,错误是因为前端传入了 undefined 的 price 参数。只要确保 priceId 有效,Firebase 的 checkout session 创建就能正常工作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 10:27:41