setup STPpaymentMethodsViewController Stripe Xcode

时间:2018-03-12 20:39:30

标签: ios json swift alamofire stripe-payments

我正在尝试使用条带文档设置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数据库结构:

(我从图片中删除了我的电子邮件,写了功能)

Firebase Database Structure

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.}))

1 个答案:

答案 0 :(得分:1)

如果没有更多背景,很难说这里发生了什么。但是,我的猜测是:您还没有在Firebase后端定义一个临时密钥方法,因此createCustomerWithKey的JSON请求失败:它期望您的后端从Stripe API接收的确切临时密钥JSON。您需要使用以下代码在后端应用程序中实现“/ ephemeral_keys”端点:

https://stripe.com/docs/mobile/ios/standard#prepare-your-api