我使用Github的Firebase功能样本成功设置了条带付款。但是当我向用户收费时,我无法将结果或条带错误传递给Android客户端。在创建的电荷和条带的响应之间存在至少3秒的延迟。我的应用用户将不知道收费是否被拒绝。 我添加了一个singleventlistener来监听firebase响应,但由于条带的延迟响应,它总是返回null值。
简而言之,我如何向客户端发送条带Firebase云功能错误(例如,您的卡因x原因而被拒绝)。
您的建议将不胜感激。
答案 0 :(得分:0)
Stripe电荷创建是异步的,这就是延迟的原因。
我猜你正在使用以下Cloud Functions for Firebase示例:https://github.com/firebase/functions-samples/blob/master/stripe/functions/index.js。 (样本中的代码粘贴在下面,供将来参考)。
在此示例中,成功的电荷创建在第44行的then()
内处理,第47行使用catch()
处理错误
在这两种情况下,您应该在Cloud Function中执行的操作是使用创建费用的结果在数据库中编写(或更新)记录。在样本中,它在第46行用return snap.ref.set(response);
完成,在第50行用于错误处理(return snap.ref.child('error').set(userFacingMessage(error));
)。
然后,在您的Android应用中,您设置了一个监听器,例如addValueEventListener()
将监听此数据库节点的更改,并根据Cloud Function写入的内容(成功或错误),相应地更新应用程序GUI。
例如,在示例中,如果出现错误,将在与error
对应的节点(即snap
)下创建(或可能更新)子节点/stripe_customers/{userId}/charges/{id}
。通过侦听此节点,您可以检测到电荷的响应并知道它是一个错误,它的类型是什么(来自error.type
)
FOR REFERENCE:只是一个"确切的"样本的副本,以及答案所指的行的标识
/**
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const logging = require('@google-cloud/logging')();
const stripe = require('stripe')(functions.config().stripe.token);
const currency = functions.config().stripe.currency || 'USD';
// [START chargecustomer]
// Charge the Stripe customer whenever an amount is written to the Realtime database
exports.createStripeCharge = functions.database.ref('/stripe_customers/{userId}/charges/{id}')
.onCreate((snap, context) => {
const val = snap.val();
// Look up the Stripe customer id written in createStripeCustomer
return admin.database().ref(`/stripe_customers/${context.params.userId}/customer_id`)
.once('value').then((snapshot) => {
return snapshot.val();
}).then((customer) => {
// Create a charge using the pushId as the idempotency key
// protecting against double charges
const amount = val.amount;
const idempotencyKey = context.params.id;
const charge = {amount, currency, customer};
if (val.source !== null) {
charge.source = val.source;
}
return stripe.charges.create(charge, {idempotency_key: idempotencyKey});
}).then((response) => { // LINE 44
// If the result is successful, write it back to the database
return snap.ref.set(response); //LINE 46 -> success, the Cloud Function writes to the database
}).catch((error) => { //LINE 47
// We want to capture errors and render them in a user-friendly way, while
// still logging an exception with StackDriver
return snap.ref.child('error').set(userFacingMessage(error)); //LINE 50 -> error, the Cloud Function writes to the database
}).then(() => {
return reportError(error, {user: context.params.userId});
});
});
// [END chargecustomer]]
// When a user is created, register them with Stripe
exports.createStripeCustomer = functions.auth.user().onCreate((user) => {
return stripe.customers.create({
email: user.email,
}).then((customer) => {
return admin.database().ref(`/stripe_customers/${user.uid}/customer_id`).set(customer.id);
});
});
// Add a payment source (card) for a user by writing a stripe payment source token to Realtime database
exports.addPaymentSource = functions.database
.ref('/stripe_customers/{userId}/sources/{pushId}/token').onWrite((change, context) => {
const source = change.after.val();
if (source === null){
return null;
}
return admin.database().ref(`/stripe_customers/${context.params.userId}/customer_id`)
.once('value').then((snapshot) => {
return snapshot.val();
}).then((customer) => {
return stripe.customers.createSource(customer, {source});
}).then((response) => {
return change.after.ref.parent.set(response);
}, (error) => {
return change.after.ref.parent.child('error').set(userFacingMessage(error));
}).then(() => {
return reportError(error, {user: context.params.userId});
});
});
// When a user deletes their account, clean up after them
exports.cleanupUser = functions.auth.user().onDelete((user) => {
return admin.database().ref(`/stripe_customers/${user.uid}`).once('value').then(
(snapshot) => {
return snapshot.val();
}).then((customer) => {
return stripe.customers.del(customer.customer_id);
}).then(() => {
return admin.database().ref(`/stripe_customers/${user.uid}`).remove();
});
});
// To keep on top of errors, we should raise a verbose error report with Stackdriver rather
// than simply relying on console.error. This will calculate users affected + send you email
// alerts, if you've opted into receiving them.
// [START reporterror]
function reportError(err, context = {}) {
// This is the name of the StackDriver log stream that will receive the log
// entry. This name can be any valid log stream name, but must contain "err"
// in order for the error to be picked up by StackDriver Error Reporting.
const logName = 'errors';
const log = logging.log(logName);
// https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/MonitoredResource
const metadata = {
resource: {
type: 'cloud_function',
labels: {function_name: process.env.FUNCTION_NAME},
},
};
// https://cloud.google.com/error-reporting/reference/rest/v1beta1/ErrorEvent
const errorEvent = {
message: err.stack,
serviceContext: {
service: process.env.FUNCTION_NAME,
resourceType: 'cloud_function',
},
context: context,
};
// Write the error log entry
return new Promise((resolve, reject) => {
log.write(log.entry(metadata, errorEvent), (error) => {
if (error) {
return reject(error);
}
return resolve();
});
});
}
// [END reporterror]
// Sanitize the error message for the user
function userFacingMessage(error) {
return error.type ? error.message : 'An error occurred, developers have been alerted';
}