Adding Additional Customer Information to Cloud Function to Create Stripe Customer - swift

I'm trying to add additional information to my cloud function so that way my Stripe customer has all of the data saved in the Firebase Database. However, my question is how can I implement the constants in my cloud function correctly so the information uploads correctly? Without the fullname, username, and profileImage in my cloud function and my registration function in the functions section, it creates the Stripe customer. How do I structure the constants for those three fields so they can upload as well? Or should I create an email and password registration screen, so I can create the stripeID, then create another screen for additional information to add to the reference? Thank you!
Cloud Function:
exports.createStripeCustomer = functions.https.onCall( async (data, context) => {
const email = data.email
const uid = context.auth.uid
const fullname = context.auth.uid.fullname
const username = context.auth.uid.username
const profileImage = context.auth.uid.profileImage
if (uid === null) {
console.log('Illegal access attempt due to unauthenticated attempt.')
throw new functions.https.HttpsError('internal', 'Illegal access attempt')
}
return stripe.customers.create({
email : email,
fullname : fullname,
username : username,
profileImage : profileImage
}).then( customer => {
return customer["id"]
}).then( customerId => {
admin.database().ref("customers").child(uid).set(
{
stripeId: customerId,
email: email,
fullname: fullname,
username: username,
profileImage: profileImage,
id: uid
}
)
}).catch( err => {
throw new functions.https.HttpsError('internal', 'Unable to create Stripe customer.')
})
})
AuthService Function:
static func createCustomer(credentials: CustomerCredentials, completion: #escaping(DatabaseCompletion)) {
guard let imageData = credentials.profileImage.jpegData(compressionQuality: 0.3) else { return }
let filename = NSUUID().uuidString
let storageRef = STORAGE_REF.reference(withPath: "/customer_profile_images/\(filename)")
storageRef.putData(imageData, metadata: nil) { (meta, error) in
if let error = error {
debugPrint(error.localizedDescription)
return
}
storageRef.downloadURL { (url, error) in
guard let profileImageUrl = url?.absoluteString else { return }
Auth.auth().createUser(withEmail: credentials.email, password: credentials.password) { (result, error) in
if let error = error {
debugPrint(error.localizedDescription)
return
}
guard let uid = result?.user.uid else { return }
let values = ["email" : credentials.email,
"fullname" : credentials.fullname,
"username" : credentials.username,
"uid" : uid,
"profileImageUrl" : profileImageUrl] as [String : Any]
CustomerDataService.saveCustomerData(uid: uid, fullname: credentials.fullname, email: credentials.email,
username: credentials.username, profileImagUrl: profileImageUrl)
REF_CUSTOMERS.child(uid).setValue(values, withCompletionBlock: completion)
}
}
}
}
Registration Function:
#objc func handleCreateAccount() {
guard let profileImage = profileImage else {
self.simpleAlert(title: "Error", msg: "Please select a profile image.")
return
}
guard let email = emailTextField.text?.lowercased() , email.isNotEmpty ,
let fullname = fullnameTextField.text , fullname.isNotEmpty ,
let username = usernameTextField.text?.lowercased() , username.isNotEmpty ,
let password = passwordTextField.text , password.isNotEmpty ,
let confirmPassword = confirmPasswordTextField.text , confirmPassword.isNotEmpty else {
self.simpleAlert(title: "Error", msg: "Please fill out all fields.")
return
}
if password != confirmPassword {
self.simpleAlert(title: "Error", msg: "Passwords don't match, please try again.")
return
}
showLoader(true, withText: "Registering Account")
let credentials = CustomerCredentials(email: email, fullname: fullname, username: username,
password: password, profileImage: profileImage)
AuthService.createCustomer(credentials: credentials) { (error, ref) in
if let error = error {
Auth.auth().handleFireAuthError(error: error, vc: self)
self.showLoader(false)
return
}
Functions.functions().httpsCallable("createStripeCustomer").call(["email": credentials.email,
"fullname": credentials.fullname,
"username": credentials.username,
"profileImage": credentials.profileImage]) { result, error in
if let error = error {
Auth.auth().handleFireAuthError(error: error, vc: self)
self.showLoader(false)
return
}
}
self.showLoader(false)
guard let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) else { return }
guard let tab = window.rootViewController as? MainTabController else { return }
tab.setupNavigationControllers()
self.handleDismissal()
}
}

To complete what I was trying to accomplish, I created a screen for customers to create an e-mail and password. This way the StripeID could be created, and then I created another screen to add the full name, username and profile image, and updated the database reference.

Related

Performing two authentication commands only if neither fail in Swift

I am trying to register a user in Firebase, AND add that user to a "users" collection with additional fields. When registering a user, I only want these commands to execute if BOTH of them are successful. For example, I don't want to register a user in Firebase if the user fails to be added to the users collection. But I also don't want the user to be added to the users collection if the firebase createUser function fails.
func register(withEmail email: String, password: String, fullname: String, username: String) {
Auth.auth().createUser(withEmail: email, password: password) { [self] result, error in
if let error = error {
print("Failed to register with error \(error.localizedDescription)")
return
}
guard let user = result?.user else { return }
let data = ["email": email,
"username": username.lowercased(),
"fullname": fullname,
"uid": user.uid,
"listOfUserActions": listOfUserActions]
Firestore.firestore().collection("users")
.document(user.uid)
.setData(data) { _ in
self.didAuthenticateUser = true
}
}
}
The way I have this set up right now, if the user was added to FirebaseAuth but the post to "users" failed, wouldn't this just break the app if I have functionality depending on the "users" collection?
You may use a combination of the methods from the createUser and setData methods to make sure that both of those operations succeed before setting didAuthenticateUser to true.
Here's the modified code:
func register(withEmail email: String, password: String, fullname: String, username: String) {
Auth.auth().createUser(withEmail: email, password: password) { [self] result, error in
if let error = error {
print("Failed to register with error \(error.localizedDescription)")
return
}
guard let user = result?.user else { return }
let data = ["email": email,
"username": username.lowercased(),
"fullname": fullname,
"uid": user.uid,
"listOfUserActions": listOfUserActions]
Firestore.firestore().collection("users")
.document(user.uid)
.setData(data) { error in
if let error = error {
print("Failed to set data with error \(error.localizedDescription)")
return
}
self.didAuthenticateUser = true
}
}
}

Handling Json response with alamofire

I'm working on a IOS login application , but I don't know how to handle the Json response from the server , I want to write a Boolean function that depends on the server response :
this is the server response if the username and the password are right :
SUCCESS: {
users = (
{
email = test;
id = 1;
money = 200;
password = test;
username = test;
}
);
}
And if the username and password are wrong :
SUCCESS: {
users = (
);
}
this is my backend code written in NodeJs:
app.get('/login/:username/:password',(req,res)=>{
let user = req.params;
var sql = "SELECT * FROM users WHERE username = ? And password = ? ";
mysqlConnection.query(sql,[user.username,user.password],
function(err, rows){
if(!err){
res.send(JSON.stringify({"users" : rows}));
}
else {
console.log(err)
}
}
This is my swift function :
class func login(username : String , password : String, _ completion: #escaping (Bool) -> ()) {
let url = "http://127.0.0.1:3000/login/"+username+"/"+password
Alamofire.request(url).responseJSON{response in
switch response.result
{
case .failure:
print(response)
completion(false)
case .success:
//I want to handle the response here
//return true if the username and password are right
//return wrong if not
print(response)
completion(true)
}
}
}
Use above code:-
func CallAPI(){
let parameters: [String: Any] = [
"Username": "Admin",
"Password": "123456",
"Language_Code": "EN"]
Alamofire.request("Your API Url", method: .post, parameters: parameters, encoding: JSONEncoding.default)
.responseJSON { response in
if((response.result.value) != nil) {
let ResultJson = JSON(response.result.value!)
print("ResultJson==",ResultJson)
let UserCount = ResultJson["users"].count
if UserCount > 0 {
// Do with your above code
let Email = ResultJson["users"]["email"].stringValue
let id = ResultJson["users"]["id"].intValue
let money = ResultJson["users"]["money"].intValue
let password = ResultJson["users"]["password"].stringValue
let username = ResultJson["users"]["username"].stringValue
}
}
}
}
In both case you are getting SUCCESS so you can take users as a key and check weather it's containing any element or not like if the username and the password are right you will get
(
{
email = test;
id = 1;
money = 200;
password = test;
username = test;
}
)
But if the username and password are wrong you will get empty value for users key so in that case you can use
if usersDict.count == 0 {
//username and password are wrong
} else {
//username and the password are right
}

Value of type 'AuthDataResult' has no member ‘uid’

I am trying to access a user's uid in Firebase Authentication. I created a createUser completion block in my code and at the end of the block I want to check for the user in which I named firUser. When I try to add firUser.uid in my User I get the error message
"Value of type 'AuthDataResult' has no member ‘uid’"
Below is a copy of the code I wrote hopefully some one can help me.
Auth.auth().createUser(withEmail: email, password: password, completion: { (firUser, error) in
if error != nil {
// report error
} else if let firUser = firUser {
let newUser = User(uid: firUser.uid, username: username, fullName: fullName, bio: "", website: "", follows: [], followedBy: [], profileImage: self.profileImage)
newUser.save(completion: { (error) in
if error != nil {
// report
} else {
// Login User
Auth.auth().signIn(withEmail: email, password: password, completion: { (firUser, error) in
if let error = error {
// report error
print(error)
} else {
self.dismiss(animated: true, completion: nil)
}
})
}
})
}
})
According to the guide, when using .createUser,
If the new account was successfully created, the user is signed in,
and you can get the user's account data from the result object that's
passed to the callback method.
Notice in the sample, you get back authResult, not a User object. authResult contains some information, including the User. You can get to the User using authResult.user.
In addition, when calling the method, if successful, the user is already signed in, so there's no reason to sign them in again. I changed the parameter name to authResult from the sample to help eliminate some of the confusion.
Auth.auth().createUser(withEmail: email, password: password, completion: { authResult, error in
if let error = error {
// report error
return
}
guard let authResult = authResult else { return }
let firUser = authResult.user
let newUser = User(uid: firUser.uid, username: username, fullName: fullName, bio: "", website: "", follows: [], followedBy: [], profileImage: self.profileImage)
newUser.save(completion: { (error) in
if let error = error {
// report
} else {
// not sure what you need to do here anymore since the user is already signed in
}
})
})

Firebase does not create a Facebook account User UID using the Facebook App User ID

I am developing an iOS app in swift that requires a user to create an account by connecting their Facebook account. A month ago, when I was testing by signing up with my own Facebook account, the User UID created by Firebase was in the format "facebook:(facebook app id)". This was what I wanted.
However, lately whenever a user creates a new acconut by connecting their Facebook account with my app, Firebase creates a User UID using a random string. For example: "E9FaL87wRmOKfhen2S6yszhCwtx1". Could this be because of the new Firebase update? Should I go through the migration process?
Here is my code for account creation:
#IBAction func facebookAuthAction(sender: AnyObject) {
let facebookLogin = FBSDKLoginManager()
facebookLogin.logInWithReadPermissions(nil, fromViewController: nil, handler: {(facebookResult, facebookError) -> Void in
if facebookError != nil {
print("Facebook login failed. Error \(facebookError)")
} else if facebookResult.isCancelled {
print("Facebook login was cancelled.")
} else {
let accessToken = FBSDKAccessToken.currentAccessToken().tokenString
FIREBASE_REF.authWithOAuthProvider("facebook", token: accessToken, withCompletionBlock: { error, authData in
if error != nil {
print("Login failed. \(error)")
} else {
print("Logged in! \(authData)")
NSUserDefaults.standardUserDefaults().setValue(authData.uid, forKey: "uid")
FIREBASE_REF.childByAppendingPath("users").observeEventType(.Value, withBlock: {snapshot in
if snapshot.hasChild(authData.uid) == false {
self.createNewFBUser(authData.uid)
}
})
self.performSegueWithIdentifier("loginSegue", sender: self)
}
})
}
})
}
func createNewFBUser(uid: String) {
var emailAddress:String = ""
var firstName:String = ""
var lastName:String = ""
var pictureURL:String = ""
let parameters = ["fields": "email, first_name, last_name, picture.type(large)"]
FBSDKGraphRequest(graphPath: "me", parameters: parameters).startWithCompletionHandler {(connection, result, error) -> Void in
if error != nil {
print(error)
return
}
if let email = result["email"] as? String {
emailAddress = email
}
if let first_name = result["first_name"] as? String {
firstName = first_name
}
if let last_name = result["last_name"] as? String {
lastName = last_name
}
if let picture = result["picture"] as? NSDictionary, data = picture["data"] as? NSDictionary, url = data["url"] as? String {
pictureURL = url
}
let newUser = [
"provider": "facebook",
"firstName": firstName,
"lastName": lastName,
"email": emailAddress,
"picture": pictureURL
]
FIREBASE_REF.childByAppendingPath("users").childByAppendingPath(uid).setValue(newUser)
}
}
When you upgrade your project to the new Firebase Console, your users are migrated to the new authentication back-end.
Newly created users after the upgrade will get a uid in the new format. See this post on the firebase-talk group for more information about the change and when it will also be applied to existing (non-upgraded) Firebase apps.
Note that Firebase has recommended against depending on the format of the uid for years. It is best to treat it as an opaque string that identifies the user.

Firebase create user swift doesn't work

below I just typed is error showing "Cannot cover value of type '(,) -> Void' to expected argument type '(NSERROR!) -> Void!)'
on this line of code: what would be wrong?
FIREBASE_REF.createUser(email, password: password, withCompletionBlock: {(error,authData) -> Void in
#IBAction func creatAccountAction(sender: AnyObject) {
let email = self.emailTextField.text
let password = self.passwordTextField.text
if email != "" && password != ""
{
FIREBASE_REF.createUser(email, password: password, withCompletionBlock: {(error,authData) -> Void in
if error != nil {
FIREBASE_REF.authUser(email, password: password, withCompletionBlock: { (error, authData) -> Void in
if error == nil {
NSUserDefaults.standardUserDefaults().setValue(authData.uid, forKey: "uid")
}
else {
print (error)
}
})
}
else {
print (error)
}
}
)}
else
Try this:
FIREBASE_REF.createUser(email, password: password, withCompletionBlock: {(error) -> Void in
This block has probably only one parameter
There are two options for creating a user, one just creates it and returns an error if it fails (withCompletionBlock), the other also returns the authData (withValueCompletionBlock): that's the one you want.
myRootRef.createUser(email, password: pw, withValueCompletionBlock: { error, result in
if error != nil {
print("error creating user")
} else {
let uid = result["uid"] as! String
NSUserDefaults.standardUserDefaults().setValue(uid, forKey: "uid")
//pass the parameters to another function to auth the user
self.authUserWithAuthData( email, password: pw )
}
})