无需Firebase Auth在Firestore中使用Stripe支付的实现问题
背景与核心问题
我正在开发一个通过WhatsApp售卖产品的Node.js后端服务,使用了Stripe的Firebase扩展stripe/firestore-stripe-payments@0.3.3,并按文档编写了以下代码:
const docRef = await db .collection('customers') .doc(currentUser.uid) .collection('checkout_sessions') .add({ price: 'price_1GqIC8HYgolSxxxxxxxxx', success_url: window.location.origin, cancel_url: window.location.origin, }); // Wait for the CheckoutSession to get attached by the extension docRef.onSnapshot((snap) => { const { error, url } = snap.data(); if (error) { alert(`An error occured: ${error.message}`); } if (url) { window.location.assign(url); } });
但云控制台报错:❗️[Error]: Checkout session creation failed for doc [maxxxxxxxxxA]: There is no user record corresponding to the provided identifier.
由于服务运行在Cloud Functions后端,没有客户端用户在Firebase Auth系统内,需要找到无需依赖Firebase Auth生成独立Stripe结账链接的方案。
已尝试的无效方案及错误
尝试直接调用Firebase Auth获取用户:
import { getAuth } from "firebase/auth"; const auth = getAuth(); const user = auth.currentUser;报错:
FirebaseError: Firebase: Need to provide options, when not being deployed to hosting via source. (app/no-options)尝试第三方方案后报错:
TypeError: Cannot read properties of undefined (reading 'getProvider')
可行解决方案
方案1:直接使用Stripe官方SDK生成Checkout链接(绕过Firebase扩展)
放弃依赖Firebase的Stripe扩展,直接在Cloud Functions中调用Stripe Node.js SDK创建结账会话,完全脱离Firebase Auth限制。
代码示例:
const Stripe = require('stripe'); const stripe = new Stripe('你的Stripe秘钥'); // 从环境变量读取更安全 exports.generateCheckoutLink = async (req, res) => { try { const session = await stripe.checkout.sessions.create({ payment_method_types: ['card'], line_items: [ { price: 'price_1GqIC8HYgolSxxxxxxxxx', // 你的产品价格ID quantity: 1, }, ], mode: 'payment', success_url: `${process.env.FRONTEND_URL}/success`, // 替换为你的成功跳转地址 cancel_url: `${process.env.FRONTEND_URL}/cancel`, // 替换为你的取消跳转地址 }); res.status(200).json({ checkoutUrl: session.url }); } catch (err) { res.status(500).json({ error: err.message }); } };
部署该HTTP函数后,直接通过接口调用即可获取结账链接,无需任何Auth用户。
方案2:创建临时匿名Firebase用户(适配原扩展逻辑)
如果必须沿用Firebase的Stripe扩展,可以在后端自动创建匿名用户,用其UID完成会话生成:
代码示例:
const admin = require('firebase-admin'); if (!admin.apps.length) { admin.initializeApp(); } exports.createCheckoutViaAnonymousUser = async () => { // 创建匿名用户 const anonymousUser = await admin.auth().createUser(); const uid = anonymousUser.uid; // 调用原扩展逻辑生成会话 const docRef = await admin.firestore() .collection('customers') .doc(uid) .collection('checkout_sessions') .add({ price: 'price_1GqIC8HYgolSxxxxxxxxx', success_url: process.env.SUCCESS_URL, cancel_url: process.env.CANCEL_URL, }); // 等待会话生成并获取链接 const snap = await docRef.get(); const { error, url } = snap.data(); if (error) throw new Error(error.message); // 可选:添加定时任务清理过期匿名用户,避免资源浪费 return url; };
内容的提问来源于stack exchange,提问作者Chrissy

