如何在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
相关产品推荐
相关产品推荐

