The developer documentation as of July 2019 for the Braintree SDK offers the standard :
let braintreeClient = BTAPIClient(authorization: "<#CLIENT_AUTHORIZATION#>")!
let cardClient = BTCardClient(apiClient: braintreeClient)
let card = BTCard(number: "4111111111111111", expirationMonth: "12", expirationYear: "2018", cvv: nil)
cardClient.tokenizeCard(card) { (tokenizedCard, error) in
// Communicate the tokenizedCard.nonce to your server, or handle error
}
But when the postal code is needed in the initialization the class accepts a NSDictionary of parameters. The problem is the key doesn't match the Braintree SDK properties
I have used:
let cardParameters: [String: Any] = [number:"4111111111111111",expirationMonth: "12", expirationYear: "2018", cvv: "111", postalCode: "94107"]
let card = BTCard.init(parameters: cardParameters)
The errors say : "Must provide postal code" or "cvv must be provided"
The trick is to use the built in properties initializing the BTCard object and match them rather than use a NSDictionary. So this works:
let braintreeClient = BTAPIClient(authorization: "<#CLIENT_AUTHORIZATION#>")!
let cardClient = BTCardClient(apiClient: braintreeClient)
let card = BTCard.init()
card.number = "4111111111111111"
card.expirationMonth = "12"
card.expirationYear = "2018"
card.cvv = "111"
card.postalCode = "94107"
cardClient.tokenizeCard(card) { (tokenizedCard, error) in
// Communicate the tokenizedCard.nonce to your server, or handle error
}
This way the BTCard object returns the acceptable keys...this took me a few hours trying different things to get to this answer. Braintree and their docs don't offer this as a example yet its was the only way it works for me.
(Each of your backends may curate this data prior to sending it to Braintree but this will help when you integrate)
In firebase realtime database, I have data as such:
"users" : {
"37KfZKDwrieIEKI9juAC4Xm8aPi1" : {
"isConnected" : false,
"isGuestUser" : false,
"lastOnline" : 1510250272022,
"profilePicture" : "5a039030515e78653148",
"userID" : "37KfZKDwrieIEKI9juAC4Xm8aPi1",
"username" : "adsfasdfasdf"
},
"4D1GNiRH5NeRxpmTNg6JhJ3iTck1" : {
"isConnected" : false,
"isGuestUser" : true,
"lastOnline" : 1510077502788,
"profilePicture" : "5a01f2648278b6652011",
"userID" : "4D1GNiRH5NeRxpmTNg6JhJ3iTck1",
"username" : "ihoho"
},
"5UA3INZ7i0dnNtgX0ai5ABhjxh43" : {
"isConnected" : false,
"isGuestUser" : true,
"lastOnline" : 1512610102474,
"profilePicture" : "5a14df775a34f2388873",
"userID" : "5UA3INZ7i0dnNtgX0ai5ABhjxh43",
"username" : "jlkjlkjlkj"
},...
I am using an external API that returns a Json that looks like this
"candidates" : [
{
"enrollment_timestamp" : "1510182689539",
"subject_id" : "37KfZKDwrieIEKI9juAC4Xm8aPi1",
},
{
"enrollment_timestamp" : "1513557650425",
"subject_id" : "CKUVZ7XtY9VKJakn1lBV7MVW1702",
},
{
"enrollment_timestamp" : "1507578748901",
"subject_id" : "l7VDdtGFpMe8BRbrlCyAciTvONk1",
},...
The ultimate goal is to get all the users from the json from the external api that are online. This requires listening to the 'isConnected' endpoint in each user, and determining if it is true or false.
Now this is impossible using firebase and my current data structure, because firstly firebase does not support multiple query parameters, so I cannot do where userID = subjectID && where isConnected == true, and secondly, more importantly, firebase does not let me do the equivalent of WHERE IN, i.e. mapping userID's to an array of potential userIDs supplied client side (fyi subjectID === userID)
So what I did was restructure my data like so;
"onlineUsers" : {
"Aze1x7jTZIbPyyPmlUYWVdEyAd73" : true,
"CQdmMxkmqBerRGOQpFblx7SO4D33" : true,
"EptMK62Kt1Sp1EIb5meuKHVpUqs1" : true,
"J2X65KauDlSvkN4Yp5V4IF0sTnx1" : true,
"KnYEqsekY9YXV3ayOx082xw8VQX2" : true,
"aGLrKH31YvRKrB8KYQmZ4sA122m1" : true,
"ab3JZyw9YMdpW3mXkZc2BjYkxej2" : true,
"gpQic1EzSnXL9x5DhaoXxcWrGF22" : true,
"qQaBPMaDOrXrWddsijfMJWusuGG3" : true,
"tDWEUoKS4mUdQ1bWeiLTlhwCSoD3" : true
},
Now all I have to do to get all online users that are in the external api json is query onlineUsers with the condition parameter checking by the key. That way I know if the result is null, the user is not online:
static func queryDatabase(child: String, queryEqual: String, keyOf: Int, completionHandler: #escaping (_ return: AnyObject?, _ error: String?) -> Void){
print("querying db with child ", child)
let ref = databaseReference.child(child).queryOrderedByKey().queryEqual(toValue: queryEqual)
ref.observe(.value, with:{ (snapshot: DataSnapshot) in
print("subjectID: ", queryEqual, "at key ", keyOf)
print("queryResult", snapshot)
if let value = (snapshot.value as? [String: AnyObject])?[queryEqual] {
print("unwrapped snapshot dict value from key: ", value)
completionHandler(value, nil)
}else{
print("no value for key \(queryEqual) so setting return as nil")
completionHandler(nil, nil)
}
}){ (error) in
print(error.localizedDescription)
completionHandler(nil, error.localizedDescription )
}
}
This function would be called simple by looping through the external api json, and calling this function ^^ every iteration, something like:
for (key, valueSubjectID) in arrayOfOrderedMatches.enumerated(){
//call function queryDatabase here
queryDatabase(child: "onlineUsers", queryEqual: valueSubjectID, keyOf: key, completionHandler:{ (response, error) in
//it would return the user if they were online here
)}
}
Now. This works. It is the most efficient way I can possible think of doing it after looking around the internet and considering every possibility. However, now onto the big problem: the json from the api can be thousands and thousands of users long. This means thousands of listeners would be being attached to check each individual user to see if they are online. This, as I have been told by a Firebase developer, is not ideal. I should not be attaching thousands of listeners. Additionally, for some reason, listeners stop being added at around 3500, i'm guessing because it bugs out. I can't remove the listeners if the query returns null (ie offline) because of the way the cache offline persistence works, and even if I could I don't think this would solve the problem. Is there any way this problem can be solved?
Let me restate the objective.
The app is provided a large list of user id's (as JSON) and the goal is to know which of those users are online.
Here's two options:
We start with a users node
users
uid_0
name: "Frank"
online: true
uid_1
name: "Jeff"
online: false
When the JSON of the users we want to check is received, we convert it to an Array for easy access.
Then, we have a couple of options
1) Iterate over the array and observeSingleEvent for each uid to get it's current status. This won't be a query since we can directly access the uid so it will be fairly lightweight by comparison to a query.*
let arrayToTest = [String]() //the array of uid's to test
for uid in arrayToTest {
let thisUserRef = self.ref.child("users").child(uid)
thisUserRef.observeSingleEvent(of: .value, with: { snapshot in
let dict = snapshot.value as! [String: Any]
let status = dict["online"] as! Bool
print("uid: \(uid) online status is: \(status)")
})
}
2) Let Firebase do the heavy lifting by adding a .childAdded event to the users node, which will iterate over all of the users one at a time.
Compare the uid in the snapshot to the uid's in the array and if it matches, get it's status (on/offline) and if it doesn't exist in the array, ignore it.
let usersRef = self.ref.child("users")
let arrayToTest = [String]() //the array of uid's to test
usersRef.observe(.childAdded, with: { snapshot in
let dict = snapshot.value as! [String: Any]
let uid = dict["user_id"] as! String
if arrayToTest.contains(uid) {
let status = dict["online"] as! Bool
print("uid: \(uid) online status is: \(status)")
}
})
Option 2) will generally be better as it's only loading one at a time and Firebase is doing all of the work - we're just checking to see if the uid from the snapshot exists in code.
3)
As a third option we can leverage a query. Note that in the first two options we retrieved our data using the lightweight observe function. Queries are heavier by comparison and utilize more resources - however, the snapshot results are limited to only those users that are logged in, and if there are a significant amount of users may be the best option.
let usersRef = self.ref.child("users")
let queryRef = usersRef.queryOrdered(byChild: "online").queryEqual(toValue: true)
let arrayToTest = [String]() //the array of uid's to test
queryRef.observe(.childAdded, with: { snapshot in
let dict = snapshot.value as! [String: Any]
let uid = dict["user_id"] as! String
if arrayToTest.contains(uid) {
print("uid: \(uid) online status is: true")
}
})
*Firebaser's will frown on 1) as they strongly recommend against doing observes in a tight loop. So 2) if you want to get confirm every user exists and 3) if you're just interested in online users.
I'm building a user registration that connects to firebase. I am unable to get firebase to discern if an email domain is valid or not so I want to provide an array of valid well known email domains which users can have to register for my app. I want to error handle for the occurence of an invalid email domain, so I need to be able to compare the end of the email the user entered with the array of valid emails I will allow. How can I check to confirm that ex: 'apples#gmail.com' is valid but ex: 'apples#gnail.com' is not valid?
let emails: Array = ["gmail.com", "yahoo.com", "comcast.net", "hotmail.com", "msn.com", "verizon.net"]
#IBAction func nextBtnPressed(_ sender: Any) {
let ref: DatabaseReference!
ref = Database.database().reference()
if let email = emailTextField.text, let pwd = passwordTextField.text, let firstName = firstNameTextField.text, let lastName = lastNameTextField.text, let dob = birthdayTextField.text {
if pwd != self.reEnterPassTextField.text {
errorMessageLbl.text = "Passwords do not match"
errorMessageLbl.isHidden = false
return
} else if firstName == "" || lastName == "" || dob == ""{
errorMessageLbl.text = "Cannot leave fields blank"
errorMessageLbl.isHidden = false
return
} else if email.characters.elementsEqual([emails]) {
print("Failure")
One of the way you can do this:
let validDomains = ["gmail.com", "yahoo.com", "comcast.net", "hotmail.com", "msn.com", "verizon.net"]
let emailTextBlockText = "example#gmail.com"
if let domain = emailTextBlockText.components(separatedBy: "#").last, validDomains.contains(domain) {
// Entered email has valid domain.
}
In my app there is a data object named room that consists of the following properties...
class Room: NSObject {
var rid: String?
var owner: String?
var groupChatName: String?
var groupChatDescription: String?
var members: AnyObject?
}
Here is the corresponding JSON
"A632CA68-40D9-4F3A-B8C8-245457057443" : {
"groupChatDescription" : "Test Description",
"groupChatName" : "Test Name",
"members" : {
"WuqCAt4mM3h0P0X1m7hVZ7NQyLC2" : {
"username" : "Steve"
}
},
"owner" : "Steve"
},
When I retrieve these values from my database, I put them in a dictionary
func fetchAllRooms(){
Database.database().reference().child("rooms").observe(.childAdded, with: {(snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let room = Room()
room.rid = snapshot.key
room.setValuesForKeys(dictionary)
self.rooms.append(room)
print("\(room.members)")
DispatchQueue.main.async(execute: {
self.collectionView?.reloadData()
})
}
print("end of room snap")
}, withCancel: nil)
}
The data matches up correctly however handling the members is proving more difficult than I originally expected.
print("\(room.members)") Returns
Optional({
WuqCAt4mM3h0P0X1m7hVZ7NQyLC2 = {
username = "Steve";
};
})
That is a lot of extraneous text. Especially considering that this is only one member and I need to return a list of members in app.
What I believe would be best for my case is to create a dictionary for the members within the current dictionary. considering each member will have additional attributes such as a reference to their profile image.
So I could call something like print("\(room.members.usernames)") and get all the usernames and still be able to use the other attributes later on.
But I don't know if this is the best solution or even where to begin. I was hoping FireBase would have some built in methods to handle this but so far I haven't been able to find anything.
Any answers, suggestions, and or references are greatly appreciated.
I keep getting the following error when viewing stripe test trasactions:
{
"error": {
"type": "invalid_request_error",
"message": "Customer cus_9tW8Cf0Xvm9lRv does not have a linked source with ID tok_19Zj5rAANnhOmz4ex3ri2jtW.",
"param": "source",
"code": "missing"
}
}
my swift code to call the api is as follows:
#IBAction func payButtonWasPressed() {
stripeCard = STPCardParams()
let expirationDate = self.cardExpiryTextField.text!.components(separatedBy: "/")
let expMonth = UInt(expirationDate[0])
let expYear = UInt(expirationDate[1])
stripeCard.number = self.cardNumberTextField.text
stripeCard.cvc = self.cardCVVTextField.text
stripeCard.expMonth = expMonth!
stripeCard.expYear = expYear!
STPAPIClient.shared().createToken(withCard: stripeCard, completion: { (token, error) -> Void in
if error != nil {
self.handleError(error! as NSError)
return
}
self.chargeUsingToken(token!)
})
}
func handleError(_ error: NSError) {
UIAlertView(title: "Please Try Again",
message: error.localizedDescription,
delegate: nil,
cancelButtonTitle: "OK").show()
}
func chargeUsingToken(_ token: STPToken) {
let URL = "https://splitterstripeserver.herokuapp.com/charge"
let params = ["source": token.tokenId,
"stripe_token": token.tokenId,
"amount": total] as [String : Any]
let manager = AFHTTPSessionManager()
manager.post(URL, parameters: params, success: { (operation, responseObject) -> Void in
if let response = responseObject as? [String: String] {
UIAlertView(title: response["status"],
message: response["message"],
delegate: nil,
cancelButtonTitle: "OK").show()
}
}) { (operation, error) -> Void in
self.handleError(error as NSError)
}
}
and I am using the example backend found on the stripe github
On my app I get a pop-up saying Request failed: payment required (402). I have tried a lot of different things but can't seem to get successful response. I cant see what im doing wrong and need a fresh pair of eyes. Any help would be great. Thanks
Once you create the card token with Stripe Checkout or Stripe.js, you send it to your server where you use it to create a customer via the API. That card becomes "saved" with that customer and you can then charge it in the future. If you want to add another card to the same customer you would use the Create Card API to pass the new token and add that card.
When you create a charge, you pass the customer id (cus_XXX) in the customer parameter to charge the default card. If you want to charge a specific card, you would pass the card id (card_YYY) in the source parameter along with the customer.
At the moment, you are trying to pass a customer id and a card token which is not supported. You'll need to save that card on the customer first and then pass the new card id in the source parameter as mentioned above.