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

使用Google Firebase登录时重复创建用户,Stripe订阅数据丢失求助

Google登录后Firestore用户订阅数据被覆盖问题

技术栈

  • Firebase
  • Next.js
  • Google 登录
  • Firebase Stripe 扩展

Bug复现步骤

  • 使用Google账号登录
  • 通过Stripe完成订阅,Stripe自动将订阅数据保存至Firestore的users集合对应文档中
  • 退出登录
  • 再次使用同一Google账号登录,发现原有订阅信息丢失,用户文档被覆盖

有没有人遇到过类似问题?怀疑是我的登录实现存在问题,以下是相关代码:

Google登录逻辑

const handleGoogleLogin = () => {
  signInWithPopup(auth, googleProvider)
    .then(async result => {
      if (!result.user) return;

      const { displayName, email, uid, providerData, photoURL, phoneNumber } =
        result.user;

      const name = splitName(displayName as string);
      const providerId =
        (providerData.length && providerData[0]?.providerId) || '';

      const data = {
        firstName: name?.firstName || '',
        lastName: name?.lastName || '',
        email,
        photoURL,
        phoneNumber,
        providerId,
      };
      await updateUser(uid, data);
    })
    .catch(error => {
      console.log('Google login error: ', error);
    });
};

用户更新函数

export const updateUser = async (uid: string, data: UpdateUserParams) => {
  try {
    if (!uid) {
      return;
    }

    await setDoc(doc(firestore, 'users', uid), {
      account: {
        ...data,
        initials: `${data.firstName[0]}${data.lastName[0]}`,
      },
    });
  } catch (error) {
    console.error('Error updating user: ', error);
  }
};

问题原因

核心问题出在updateUser函数中的setDoc方法:默认情况下,setDoc如果目标文档已经存在,会完全覆盖整个文档。当用户完成订阅后,Stripe扩展会在users文档中添加subscription(或类似字段);而再次登录时调用setDoc,只传入了account字段,导致原有文档中的订阅数据被彻底覆盖删除。

解决方案

修改updateUser函数,使用setDoc的merge选项(推荐),或者改用updateDoc,确保只更新指定字段,保留文档中已有的其他数据:

方案1:使用setDoc的合并选项

export const updateUser = async (uid: string, data: UpdateUserParams) => {
  try {
    if (!uid) {
      return;
    }

    const userDoc = doc(firestore, 'users', uid);
    // merge: true 会保留文档原有字段,仅更新传入的字段
    await setDoc(userDoc, {
      account: {
        ...data,
        initials: `${data.firstName[0]}${data.lastName[0]}`,
      },
    }, { merge: true });
  } catch (error) {
    console.error('Error updating user: ', error);
  }
};

方案2:区分创建/更新操作

如果需要更明确的逻辑,可以先判断文档是否存在,新用户创建文档,老用户仅更新字段:

export const updateUser = async (uid: string, data: UpdateUserParams) => {
  try {
    if (!uid) {
      return;
    }

    const userDoc = doc(firestore, 'users', uid);
    const docSnapshot = await getDoc(userDoc);

    if (docSnapshot.exists()) {
      // 已有用户,仅更新account字段
      await updateDoc(userDoc, {
        account: {
          ...data,
          initials: `${data.firstName[0]}${data.lastName[0]}`,
        },
      });
    } else {
      // 新用户,创建完整文档,可初始化订阅字段
      await setDoc(userDoc, {
        account: {
          ...data,
          initials: `${data.firstName[0]}${data.lastName[0]}`,
        },
        subscription: null
      });
    }
  } catch (error) {
    console.error('Error updating user: ', error);
  }
};

这样修改后,再次登录时只会更新用户的账号信息,不会覆盖Stripe保存的订阅数据。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 11:15:44