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

如何在Stripe Dashboard显示Firestore UID并解决支付无响应问题

修复方案

一、点击按钮无响应的根因与修复

1. 云函数侧缺失对应接口

你当前云函数代码中没有实现客户端调用的createPaymentIntent方法,且存在以下隐含错误:

  • 原有createStripeCharge方法中currency变量未定义
  • reportError方法依赖的logging库未引入

2. 客户端回调未触发

你写的Swift端createPaymentIntent函数拿到云函数返回结果后,没有调用传入的completion回调,导致支付逻辑永远卡在等待返回的状态。

二、Firestore UID与Stripe客户绑定实现

Stripe客户ID为平台自动生成的cus_开头固定格式,无法自定义为你的Firestore UID,我们可以将Firestore UID存入Stripe客户的metadata字段,该字段会在Stripe Dashboard客户详情页直接展示,满足绑定可见需求。

完整修改代码

1. 云函数index.js修改

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {Logging} = require('@google-cloud/logging');
const logging = new Logging();
const stripe = require('stripe')('你的Stripe密钥');
admin.initializeApp();
const currency = 'usd'; // 按需修改为你的结算币种

// 新增createPaymentIntent云函数
exports.createPaymentIntent = functions.https.onCall(async (data, context) => {
  // 校验登录状态
  if (!context.auth) throw new functions.https.HttpsError('unauthenticated', '请先登录');
  const userId = context.auth.uid;
  // 获取该用户对应的Stripe客户ID
  const customerSnap = await admin.firestore().collection('stripe_customers').doc(userId).get();
  if (!customerSnap.exists) throw new functions.https.HttpsError('failed-precondition', '客户信息不存在');
  const customerId = customerSnap.data().customer_id;
  // 创建PaymentIntent
  const paymentIntent = await stripe.paymentIntents.create({
    amount: data.amount,
    currency: currency,
    customer: customerId,
    metadata: {
      firebase_uid: userId
    }
  });
  return {secret: paymentIntent.client_secret};
});

// 修改原有createStripeCustomer函数,添加UID到Stripe metadata
exports.createStripeCustomer = functions.auth.user().onCreate(async (user) => {
  const customer = await stripe.customers.create({
    email: user.email,
    metadata: {
      firebase_uid: user.uid // 该字段会在Stripe Dashboard展示
    }
  });
  return admin.firestore().collection('stripe_customers').doc(user.uid).set({customer_id: customer.id, email: user.email});
});

// 原有其他函数保留,补充currency变量即可
exports.createStripeCharge = functions.firestore.document('stripe_customers/{userId}/charges/{id}').onCreate(async (snap, context) => {
  const val = snap.data();
  try {
    const snapshot = await admin.firestore().collection(`stripe_customers`).doc(context.params.userId).get()
    const snapval = snapshot.data();
    const customer = snapval.customer_id
    const amount = val.amount;
    const idempotencyKey = context.params.id;
    const charge = {amount, currency, customer};
    if (val.source !== null) {
      charge.source = val.source;
    }
    const response = await stripe.charges.create(charge, {idempotency_key: idempotencyKey});
    return snap.ref.set(response, { merge: true });
  } catch(error) {
    console.log(error);
    await snap.ref.set({error: userFacingMessage(error)}, { merge: true });
    return reportError(error, {user: context.params.userId});
  }
});

exports.addPaymentSource = functions.firestore.document('/stripe_customers/{userId}/tokens/{pushId}').onCreate(async (snap, context) => {
  const source = snap.data();
  const token = source.token;
  if (source === null){
    return null;
  }
  try {
    const snapshot = await admin.firestore().collection('stripe_customers').doc(context.params.userId).get();
    const customer = snapshot.data().customer_id;
    const response = await stripe.customers.createSource(customer, {source: token});
    return admin.firestore().collection('stripe_customers').doc(context.params.userId).collection("sources").doc(response.fingerprint).set(response, {merge: true});
  } catch (error) {
    await snap.ref.set({'error':userFacingMessage(error)}, {merge:true});
    return reportError(error, {user: context.params.userId});
  }
});

exports.cleanupUser = functions.auth.user().onDelete(async (user) => {
  const snapshot = await admin.firestore().collection('stripe_customers').doc(user.uid).get();
  const customer = snapshot.data();
  await stripe.customers.del(customer.customer_id);
  return admin.firestore().collection('stripe_customers').doc(user.uid).delete();
});

function reportError(err, context = {}) {
  const logName = 'errors';
  const log = logging.log(logName);
  const metadata = {
    resource: {
      type: 'cloud_function',
      labels: {function_name: process.env.FUNCTION_NAME},
    },
  };
  const errorEvent = {
    message: err.stack,
    serviceContext: {
      service: process.env.FUNCTION_NAME,
      resourceType: 'cloud_function',
    },
    context: context,
  };
  return new Promise((resolve, reject) => {
    log.write(log.entry(metadata, errorEvent), (error) => {
      if (error) {
        return reject(error);
      }
      return resolve();
    });
  });
}

function userFacingMessage(error) {
  return error.type ? error.message : 'An error occurred, developers have been alerted';
}

修改完成后执行firebase deploy --only functions部署云函数。

2. Swift端代码修改

修复createPaymentIntent函数的回调调用,以及displayStatus的拼写错误:

// 修复displayStatus的拼写错误
func displayStatus(_ message: String) {
  DispatchQueue.main.async {
    self.outputTextView.text! += message + "\n"
    self.outputTextView.scrollRangeToVisible(NSMakeRange(self.outputTextView.text.count - 1, 1))
  }
}

// 修复createPaymentIntent函数,添加回调
func createPaymentIntent(completion: @escaping STPJSONResponseCompletionBlock) {
  functions.httpsCallable("createPaymentIntent").call(["amount": 1099]) { result, error in
    if let error = error as NSError? {
      if error.domain == FunctionsErrorDomain {
        let code = FunctionsErrorCode(rawValue: error.code)
        let message = error.localizedDescription
        let details = error.userInfo[FunctionsErrorDetailsKey]
      }
      completion(nil, error)
      return
    }
    if let data = result?.data as? [String: Any] {
      completion(data, nil)
      return
    }
    completion(nil, NSError(domain: "AppError", code: -1, userInfo: [NSLocalizedDescriptionKey: "返回数据格式错误"]))
  }
}

三、已有测试账号绑定说明

你已经手动创建的邮箱为test@again.com的Stripe客户,直接在Stripe Dashboard打开该客户详情页,在Metadata栏添加key为firebase_uid,value为你的Firestore UIDljO8PXBpI6gyqliXOTeqJf08dQ22即可完成绑定,绑定关系直接可见。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 07:39:04