Parse Server + Stripe Connect - iOS - ios

How do you setup Parse Server with Stripe Connect? I'm having a miserable time...
I'm trying to integrate my Parse Server (hosted on Heroku) with Stripe Connect (this is different that standard Stripe, in it allows you (the app) to transfer payments to a third party while taking a 'processing fee' while ONLY USING Parse Server + Xcode (as this is all that I'm familiar with).
For example, Lyft charges the customer's credit card, takes a percentage of the ride, and the remaining balance is transferred to the driver. How do I do this in Stripe automatically?!

Stripe's documentation did not give me an explicit example and I struggled for hours... Well, I finally got it and wanted to share it with you. Hope you all find this useful:
Assumptions:
You have an account on Stripe
You've added Stripe to your Parse Server example here. If you don't understand, message me for details.
You've added Stripe SDK to your Xcode project
You've setup Cloud Code on your Parse Server (again message if confused)
OK, so we are going to charge a credit card, pay the third party, but keep a 'fee'. First you'll go to the Stripe.com dashboard (click the top right of the screen, to view all of the options). Then click CONNECT and fill out the info.
IMPORTANT: YOU DO NOT NEED TO FILL OUT THE FIELDS "REDIRECT URI".
OK so now we need to create a CONNECTED STRIPE ACCOUNT. We do this by cloud code:
Parse.Cloud.define("createConnectedAccount", function(request, response) {
var stripe = require('stripe')('YOUR_SECRET_KEY');
stripe.accounts.create({
managed: false,
country: 'US',
email: 'example#gmail.com' //THIS IS YOUR THIRD PARTY ACCOUNT EMAIL ADDRESS
}, function(err, account) {
// asynchronously called
if (err) {
//other errror
response.error(err); // return error
} else {
//no error
response.success(account); // return charge success
}
});
});
This account is managed by the THIRD PARTY. When you run this code, it will create a Stripe account for this third party and send them an email (to the email listed). Basically, the email instructs them to Login, enter a password, and enter a bank account. When they activate the account, it will be 'connected' to your account.
Once connected, now it's time to write the "charge the card" method:
Parse.Cloud.define("charge", function(request, response) {
var stripe = require('stripe')('YOUR_SECRET_KEY');
stripe.charges.create({
amount: 100, //in CENTS
currency: "usd",
customer: request.params.customer, //customer is the id given by stripe when you create a customer. example: cus_EXAMPLE398FMFJKEP876
description: "example for people",
application_fee: 25, //again, in CENTS
}, {stripe_account: "3RD_PARTY_ACCOUNT_NUMBER"}, function(err, charge) { //the third party account number looks something like this acct_EXAMPLE352JFLE3207ME and can be found by clicking "Connected Accounts" (left side pane option after you set it up).
// asynchronously called
if (err && err.type === 'StripeCardError') {
// The card has been declined
response.error(err); // card declineded
} else if (err) {
//other errror
response.error(err); // return error
} else {
//no error
response.success(charge); // return charge success
}
});
});
Lastly, a quick picture of the "connected accounts" option on the left navigation pane:
Walah. You are done.
Hope this helps. Let me know if you have any questions.

Related

Re-Send Firebase email verification

I have a question which looks silly but I can't find answer anywhere.
I have a simple signup iOS procedure which relies on Firebase Authentication SDK.
At a certain point after the user is created with:
FIRAuth.auth()?.createUser(withEmail: userName!, password: password!)
right after that I sent my user a verification email:
FIRAuth.auth()?.currentUser?.sendEmailVerification(completion:
{(error) in
if error == nil
{self.showSuccessPopUp()}
else
{self.showErrorPopUp()}
})
Everything works more than fine, no problem at all.
My question is: my user receives the email and - for any reason - didn't click on the autogenerated confirmation link.
Later on he open the app again and - forgetting that he already register once - tries to signup with the same email address.
Firebase just says that there's already an user created with that email address - as per documentation the user is created even if not 'active' -, therefore I'd like to give my users the option to have a "Resend verification email".
I've been digging into Firebase API documentation without a solution.
Does anyone have ever had the same 'issue' ?
Thanks for any help
Though late I would answer this in two scenarios:
1: You successfully called createUser, but when the user opens the app again, firebase.auth() says they are not signed in
In this case, the account exists with a password, so you will need to send a 'reset password' email, not an authentication email
2: You successfully called createUser, but when the user opens the app again, firebase.auth() says they ARE signed in
In this case, they are logged in, but not verified. Use
firebase.auth().currentUser.reload() // reloads user fields, like emailVerified:
if (!firebase.auth().currentUser.emailVerified) {
//resend verification email
} else {
//login
}
pardon my use of javascript but should be easy enough to translate
This question already has a short answer but I'll add my answer as I was facing the same problem. So there's already an user created with that email address. Later on the user opens the app again and wants to resend the email verification, but this time you don't create the user with email as there's already an user created in firebase with same email address, instead you can signing the user through firebase first as firebase already has user's details, if successful then get the current user from firebase. Now you can give your users the option to have a "Resend verification email" if they haven't verified yet. See the code below to get clear idea.
The following code assumes that user receives the email and - for any reason - didn't click on the autogenerated confirmation link. Later on he open the app again and - forgetting that he already register once - tries to signup with the same email address. Therefore you want give users the option to have a "Resend verification email".
firebaseAuth.signInWithEmailAndPassword(username, password)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
firebaseUser = firebaseAuth.getCurrentUser();
firebaseUser.reload(); // Here you finally get the user, now you can send verification mail again.
if(firebaseUser.isEmailVerified()) {
// TODO
}else {
// TODO
Toast.makeText(LoginActivity.this, "Please verify your email first!", Toast.LENGTH_LONG).show();
}
}
}
});
Now set a button Resend or something
Here's the code for that
resend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(firebaseUser!=null){
firebaseUser.reload();
if(!firebaseUser.isEmailVerified()) {
firebaseUser.sendEmailVerification();
Toast.makeText(LoginActivity.this, "Email Sent!", Toast.LENGTH_LONG).show();
}else {
Toast.makeText(LoginActivity.this, "Your email has been verified! You can login now.", Toast.LENGTH_LONG).show();
}
}
}
});
This can be done using a firebase function with an email sending module (Nodemailer or Firebase Trigger Email extension for example)
The solution is in the firebase documentation
See section Generate email verification link

How to use Braintree V Zero on IOS app?

I am coding an IOS app with Payment feature.
I decided to use Braintree V Zero.
At the very beginning, I use their excellent DropIn UI feature, and everything works fine.
But when payment happen, the Drop In UI required end-user to input his credit card or Paypal information every time.
Does any expect know how to implement one automatic charge solution by BrainTree V zero?
Like Uber's charge solution.
I guess maybe need to mark the user's credit card information from app side or service side?
router.get('/token', function (req, res) {
console.log('Kevin in token be called %s', req.param('aCustomerId'));
var aCustomerId = req.param('aCustomerId');
console.log('Kevin %s', aCustomerId);
gateway.clientToken.generate({customerId: aCustomerId}, function (error, response) {
res.send(response.clientToken);
console.log(response.clientToken);
});
});
Thank you in advanced!
Full disclosure: I work for Braintree.
The Braintree drop-in will display previously used payment methods for a customer, if you pass the customer_id in when generating a client token on your server.
​
Here's an example of how to do it in Node:
gateway.clientToken.generate({
customerId: aCustomerId
}, function (err, response) {
var clientToken = response.clientToken
});
​
Once a payment method is used, it will be saved in the drop-in and the customer will not have to enter it again. Pass the token of the saved payment method when creating a transaction:
​
gateway.transaction.sale({
amount: "10.00",
paymentMethodToken: theToken,
options: {
submitForSettlement: true
}
}, function (err, result) {
});
If you have any further questions, please feel free to contact Braintree support.
3133e

How to force Google OAuth popup in Firebase when user already authenticated previously?

Whenever a user has previously authenticated with Google, it automatically defaults to logging them in with THAT account on subsequent attempts. I want to eliminate this and force the popup/redirect so that a user with multiple google accounts can choose which one to use. How?
Background:
The automatic logging in feature is proving problematic for me as I have a whitelisted set of e-mails for users allowed to use my app. If a Google user chooses the wrong account when first logging in, they can't go back and choose the one associated to their whitelisted e-mail.
Just as #nvnagr said, you can do this with the following code:
var provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({
'prompt': 'select_account'
});
But I think you need to update the firebase version to 3.6.0 something.
Google supports a parameter in authentication url to deal with this issue.
If you add prompt=select_account in your request to Google authentication, it'll force the user to do an account selection. See the details and other values of prompt.
https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters
I'm not sure if there is an easy way to add this parameter through firebase api.
When you're calling the oAuth function, you can pass a third options parameter to make the authentication last for the session only. This should solve your problem. Docs
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
ref.authWithOAuthPopup("google", function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
}
}, {
remember: 'sessionOnly'
});

Stripe - check if a customer exists

In the stripe documentation they have this:
customer = Stripe::Customer.create(email: params[:stripeEmail], card: params[:stripeToken])
charge = Stripe::Charge.create(customer: customer.id, amount: price, description: '123', currency: 'usd')
But I think it's wrong as for each payment we have to check if a customer exists first, I just tried it with a test account and it turned out there were created a number of different customers with the same email but different ids.
How do I check if a customer already exists?
There is no check on Stripe's end to ensure uniqueness of customers (email, card, name, etc,) and this is something you have to do on your end.
Usually, when you create a customer with a specific email address you need to associate the customer id you got back from the API with the email address. Then next time, you check whether this email address is already in your system and either create a new customer or re-use the customer id from the previous time.
You may check if the customer exists or not by calling GET /customers with the email as a form-urlencoded parameter. You will get 200 OK response but the returned data[] will be empty if this customer email is not there.
https://stripe.com/docs/api/customers/list
I think a simple "exists" test is essential to avoid unwanted exceptions being generated.
If your customer is new then they won't have a stripe ID, which is fine. However you might get a situation where you are using an existing customer with an existing stripe ID, but have performed some sort of dormant clean-up Stripe side. In which case its good practice to test the supplied stripeID to see if its still valid.
For example I store the stripeID with the customer profile. When I make a payment I check to see if the stripeID field in the profile exists. If it does I fire off a API request to my backend to check its existence.
Something like this (.net backend example).
[HttpGet]
public ActionResult<bool> CheckCustomer(string id)
{
var service = new CustomerService();
Customer cust;
try
{
service.Get(id);
}
catch
{
return Ok(false);
}
if (cust.Deleted ?? false)
{
return Ok(false);
}
return Ok(true);
}
I don't need the result, just it not throwing an exception is good enough. However, when you delete customers in Stripe live it keeps a copy of the ID stub and sets the delete property to true, so you need to check for that too.
The front end then can either use the stripeId for the payment or create a new stripe customer first then take the payment.
You can search for the customer by email address...
public async Task<Customer> GetCustomerByEmail(string oneEmail)
{
var service = new CustomerService();
try
{
StripeList<Customer> customerList = await service.ListAsync(new CustomerListOptions() { Email=oneEmail },null);
Debug.WriteLine(customerList.ToList().Count);
return customerList.ToList().FirstOrDefault();
}
catch
{
return null;
}
}

Subscribing email address to MailChimp from iOS app

I have added a contact form to my app that allows users to send feedback to me directly via email. I'm using Mandrill and Parse, and it works well!
On the contact form is an "Add me to mailing list…" option, and I'm looking for a way to add the user's email to MailChimp automatically if this option is checked.
I understand that there's a MailChimp API that's accessible by Objective C through a wrapper, though I'm wondering if there's not a more straightforward way to simply add an email to a MailChimp mailing list in iOS/Objective C?
Thanks for reading.
EDIT #1: Progress, but not yet success.
1) I've added cloud code from this answer to Parse (substituting in the two keys, where KEY2 is last three characters of MailChimp key):
var mailchimpApiKey = "MY_MAILCHIMP_KEY";
Parse.Cloud.define("subscribeUserToMailingList", function(request, response) {
if (!request.params ||
!request.params.email){
response.error("Must supply email address, firstname and lastname to Mailchimp signup");
return;
}
var mailchimpData = {
apikey : mailchimpApiKey,
id : request.params.listid,
email : {
email : request.params.email
},
merge_vars : request.params.mergevars
}
var url = "https://KEY2.api.mailchimp.com/2.0/lists/subscribe.json";
Parse.Cloud.httpRequest({
method: 'POST',
url: url,
body: JSON.stringify(mailchimpData),
success: function(httpResponse) {
console.log(httpResponse.text);
response.success("Successfully subscribed");
},
error: function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
console.error(httpResponse.text);
response.error('Mailchimp subscribe failed with response code ' + httpResponse.status);
}
});
});
2) And I've added this Objective-C code to my iOS project (adding in my MailChimp list ID):
[PFCloud callFunctionInBackground:#"subscribeUserToMailingList" withParameters:#{#"listid":#"MY_LIST_ID",#"email":userEmail,#"mergevars":#{#"FNAME":firstName,#"LNAME":lastName}}
block:^(NSString *result, NSError *error){
if (error) {
//error
} else {
}
}];
The result? This error:
Error Domain=Parse Code=141 "The operation couldn’t be completed. (Parse error 141.)" … {error=Mailchimp subscribe failed with response code 500, code=141}
EDIT #2: More progress, but not yet success.
The previous error was being caused by an attempt to add an email address to the mailing list that was already there. I am now getting no errors and a "Successfully subscribed" result in the block above. However, logging in to MailChimp, the new address is still not there.
OK, turns out the code is fine! Please use, share, and enjoy.
The issue was that MailChimp (smartly) requires double opt-in for mailing lists.
The first opt-in is running this code with a specific userEmail, and it results in an email being sent to your to-be-added user.
The email asks them to confirm their subscription, and if they do so (it's a link in the email), that's the second opt-in. Then, their email is added to your list.
So, bottom line is that the code doesn't automatically add a user to your mailing list—their confirmation is still required. It's a nice way to make sure people on your mailing list actually want to be there (i.e., have a chance of reading your emails)!

Resources