我正在尝试使用条带文档设置STPpaymentmethodsviewcontroller。它说我需要设置STPcustomercontext但它没有指定位置。下面是我当前的MYAPICLIENT代码,STPpaymentmethodsviewcontroller的viewcontroller内的代码,后端函数代码(客户是在创建帐户时创建的,其中firebase函数正常工作,将客户ID写入数据库),firebase数据库结构,以及在尝试加载STPpaymentmethodsviewcontroller时出现的错误。
MYAPICLIENT:
class MyAPIClient: NSObject, STPEphemeralKeyProvider {
static let sharedClient = MyAPIClient()
var baseURLString: String? = "https://fresh-9883c.firebaseio.com/"
var baseURL: URL {
if let urlString = self.baseURLString, let url = URL(string: urlString) {
return url
} else {
fatalError()
}
}
func createCustomerKey(withAPIVersion apiVersion: String, completion: @escaping STPJSONResponseCompletionBlock) {
let url = self.baseURL.appendingPathComponent("ephemeral_keys")
Alamofire.request(url, method: .post, parameters: [
"api_version": apiVersion,
])
.validate(statusCode: 200..<500)
.responseJSON { responseJSON in
switch responseJSON.result {
case .success(let json):
completion(json as? [String: AnyObject], nil)
case .failure(let error):
completion(nil, error)
}
}
}
func completeCharge(_ result: STPPaymentResult, amount: Int, completion: @escaping STPErrorBlock) {
// 1
let url = baseURL.appendingPathComponent("charge")
// 2
let params: [String: Any] = [
"source": result.source.stripeID,
"amount": amount,
"currency": Constants.defaultCurrency,
"description": Constants.defaultDescription
]
// 3
Alamofire.request(url, method: .post, parameters: params)
.validate(statusCode: 200..<300)
.responseString { response in
switch response.result {
case .success:
completion(nil)
case .failure(let error):
completion(error)
}
}
}
}
CartViewController:
func handlePaymentMethodsButtonTapped(){
let customerContext = STPCustomerContext(keyProvider: MyAPIClient.sharedClient)
let paymentMethodsViewController = STPPaymentMethodsViewController(configuration: STPPaymentConfiguration.shared(), theme: STPTheme.default(), customerContext: customerContext, delegate: self)
let navigationController = UINavigationController(rootViewController: paymentMethodsViewController)
present(navigationController, animated: true)
}
@IBAction func confirmOrder(_ sender: UIButton) {
// append cart to database and dequeue in my orders (active , completed)
guard CartSingleton1.sharedInstance.orderDict.count > 0 else {
let alertController = UIAlertController(title: "Warning", message: "Cart is Empty", preferredStyle: .alert)
let alertAction = UIAlertAction(title: "OK", style: .default)
alertController.addAction(alertAction)
present(alertController, animated: true)
return
}
handlePaymentMethodsButtonTapped()
}
}
extension CartViewController: STPPaymentMethodsViewControllerDelegate, STPPaymentContextDelegate {
func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) {
self.navigationController?.popViewController(animated: true)
}
func paymentContextDidChange(_ paymentContext: STPPaymentContext) {
}
func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: @escaping STPErrorBlock) {
MyAPIClient.sharedClient.completeCharge(paymentResult, amount: 100, completion: { (error: Error?) in
if let error = error {
completion(error)
} else {
completion(nil)
}
})
}
func paymentContext(_ paymentContext: STPPaymentContext, didFinishWith status: STPPaymentStatus, error: Error?) {
switch status {
case .error:
print(error as Any)
case .success:
print("success")
//self.showReciept()
case .userCancellation:
return
}
}
func paymentMethodsViewController(_ paymentMethodsViewController: STPPaymentMethodsViewController, didSelect paymentMethod: STPPaymentMethod) {
let selectedPaymentMethod = paymentMethod
print("selectedPaymentMethod",selectedPaymentMethod)
}
func paymentMethodsViewController(_ paymentMethodsViewController: STPPaymentMethodsViewController, didFailToLoadWithError error: Error) {
print("did fail to load paymentmethodsview",error)
dismiss(animated: true)
}
func paymentMethodsViewControllerDidFinish(_ paymentMethodsViewController: STPPaymentMethodsViewController) {
dismiss(animated: true)
}
func paymentMethodsViewControllerDidCancel(_ paymentMethodsViewController: STPPaymentMethodsViewController) {
dismiss(animated: true)
}
}
Firebase Cloud功能:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const logging = require('@google-cloud/logging')();
admin.initializeApp(functions.config().firebase);
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}').onWrite((event) => {
const val = event.data.val();
if (val === null || val.id || val.error) return null;
return admin.database().ref(`/stripe_customers/${event.params.userId}/customer_id`).once('value').then((snapshot) => {
return snapshot.val();
}).then((customer) => {
const amount = val.amount;
const idempotency_key = event.params.id;
let charge = {amount, currency, customer};
if (val.source !== null) charge.source = val.source;
return stripe.charges.create(charge, {idempotency_key});
}).then((response) => {
return event.data.adminRef.set(response);
}).catch((error) => {
return event.data.adminRef.child('error').set(userFacingMessage(error));
}).then(() => {
return reportError(error, {user: events.params.userId});
});
});
// [end chargecustomer]]
// when user is created register them with stripe
exports.createStripeCustomer = functions.auth.user().onCreate((event) => {
const data = event.data;
return stripe.customers.create({
email: data.email,
}).then((customer) => {
return admin.database().ref(`/stripe_customers/${data.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((event) => {
const source = event.data.val();
if (sourve === null) return null;
return admin.database.ref(`/stripe_customers/${event.params.userId}/customer_id`).once('value').then((snapshot) => {
return snapshot.val();
}).then((customer) => {
return stripe.customers.createSource(customer, {source});
}).then((response) => {
return event.data.adminRef.parent.set(response);
}, (error) => {
return event.data.adminRef.parent.child('error').set(userFacingMessage(error));
}).then(() => {
return reportError(error, {user: event.params.userId});
});
});
// when a user deletes their account, clean up after the
exports.cleanupUser = functions.auth.user().onDelete((event) => {
return admin.database().ref(`/stripe_customers/${event.data.uid}`).once('value').then((snapshot) => {
return snapshot.val();
}).then((customer) => {
return stripe.customers.del(customer.customer_id);
}).then(() => {
return admin.database().ref(`/stripe_customers/${event.data.uid}`).remove();
});
});
function reportError(err, context = {}) {
const logName = 'errors';
const lof = 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) {
reject(error);
}
resolve();
});
});
}
// end [reportError]
// sanitize the error message for the user
function userFacingMessage(error) {
returnerror.type ? error.message : 'an error occurred, developers have been altered';
}
Firebase数据库结构:
(我从图片中删除了我的电子邮件,写了功能)
Xcode控制台中的错误:
did fail to load paymentmethodsview
responseSerializationFailed(Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.}))
答案 0 :(得分:1)
如果没有更多背景,很难说这里发生了什么。但是,我的猜测是:您还没有在Firebase后端定义一个临时密钥方法,因此createCustomerWithKey的JSON请求失败:它期望您的后端从Stripe API接收的确切临时密钥JSON。您需要使用以下代码在后端应用程序中实现“/ ephemeral_keys”端点:
https://stripe.com/docs/mobile/ios/standard#prepare-your-api