PassportJS and Stripe - node.js

I am attempting to charge a user when they create an account. Everything is set up and working perfect with the code before. When a user sign's up for a "premium" section of the website they have an account created and are charged with the code below.
The problem: If a user's credit card ends up getting declined for various reasons... their user account is STILL created. How would I change my code below to not reach that part of the code if the credit card fails?
Note: this DOES work for when the user trys to create an account with a username that is taken. The web app redirects them to /buy to select a new username. However it does not work to handle the credit card errors because the user is created first.
Thank you for the help!
user.save(function(err) {
console.log('this is the problem' + ' ' + err)
if(err){
return res.redirect('/buy')
}
var token = req.body.stripeToken; // Using Express
var charge = stripe.charges.create({
amount: 749,
currency: "usd",
description: "Website.com Premium - One time Payment",
source: token,
}, function(err, charge) {
if(err) {
console.log(err);
return res.redirect('/buy')
}
console.log('charged')
req.logIn(user, function(err) {
if(err) {
console.log(err);
}
console.log('all looks good')
res.redirect('/results');
});
});
});
});

The problem is you are handling the payment after you save the user to your database. There are two ways you could solve this. Either you could delete the user from your database if the payment fails. Or you could handle the payment THEN save the user to the database. So basically switching the order and which callbacks are nested inside of which callbacks.
I'd personally suggest the second solution because you will be making less calls to your database which will reduce the stress and load on your database.
Here is a basic example of how to achieve that. I don't know all of the in and outs of your code so you might have to make a few adjustments to fit how you are doing things but the basic idea is the there.
var token = req.body.stripeToken; // Using Express
var charge = stripe.charges.create({
amount: 749,
currency: "usd",
description: "Website.com Premium - One time Payment",
source: token,
}, function(err, charge) {
if (err) {
console.log(err);
return res.redirect('/buy')
}
console.log('charged')
user.save(function(err) {
console.log('this is the problem' + ' ' + err);
if (err) {
return res.redirect('/buy')
}
req.logIn(user, function(err) {
if (err) {
console.log(err);
}
console.log('all looks good')
res.redirect('/results');
});
});
});
So you are basically creating the stripe charge, and if it is successful then you create the user in your database.
Hopefully this helps!

Related

Error when creating a customer using test card

When attempting to begin a Subscription for a newly created Customer, I receive the following error from Stripe:
invalid_request_error
Error: This customer has no attached payment source
The customer seems to be created just fine. I am using Stripe Checkout to collect the card token. For testing, I am using Stripe's 4242 4242 4242 4242 card number with random information. The token seems to be getting created and passed to my server just fine. Below is my server side code:
stripe.plans.retrieve(
"basic-monthly",
function(err, plan) {
if (err) {
console.error(err)
res.sendStatus(500)
} else {
stripe.customers.create({
email: owner,
source: token.id,
}, function(err, customer) {
if (err) {
console.error(err)
res.sendStatus(500)
} else {
stripe.subscriptions.create({
customer: customer.id,
items: [
{
plan: "basic-monthly",
quantity: 1
},
],
}, function(err, subscription) {
if (err) {
console.error(err)
console.log('##### UNABLE TO CREATE SUBSCRIPTION ####')
res.sendStatus(500)
} else {
console.log('Subscription created.')
console.dir(subscription)
res.sendStatus(200);
}
});
}
});
}
});
##### UNABLE TO CREATE SUBSCRIPTION #### is logged, along with the errors described above. I understand what the error means, but I am not sure how it is occurring. As you can see above, I am passing in the Token Id when creating a customer, source: token.id,.
What is the issue here?
The most likely cause here is that token.id is empty, so the Customer is being created without a Source. I'd suggest logging the contents of token and see what you get.

MEAN 2 Multi Tenancy (Multiple collection saves that reference each other)

So whilst learning JS and specifically the MEAN 2 stack i'm trying to build out a basic multi tenanted app. Im building out sign up routes in express and the flow i'm trying to achieve would be:
Sign up with company name, email and password. The info would go to save a new tenant, then return the _id of the new tenant and then use this new id, the email and the password to save a new user.
The closest is:
router.post('/', function (req, res, next) {
var tenant = new Tenant({
name: req.body.name
});
var newTenant;
tenant.save(function (err, tenant) {
if (err) {
return res.status(500).json({
title: 'An error has occured',
error: err
});
}
res.status(201).json({
message: 'Tenant created',
obj: tenant
});
return(tenant._id);
newTenant = tenant;
});
Tenant.findById(newTenant._id, function(err, tenant) {
if (err) {
return res.status(500).json({
title:'An error occured',
error: err
});
}
var user = new User({
email: req.body.email,
password: bcrypt.hashSync(req.body.password, 10),
active: req.body.active,
tenant: tenant
});
user.save(function (err, user) {
if (err) {
return res.status(500).json({
title: 'An error has occured',
error: err
});
}
res.status(201).json({
message: 'User created',
obj: user
});
});
});
});
module.exports = router;
I'm getting an error: cant set headers after they've been sent.
I think I know where i'm wrong, with returning the tenant info. I think Async is the answer but cant figure out how to implement it. Sorry if this is a stupid question or i'm missing something obvious, I'm super new to this and callbacks are doing my head in.
This is happening because res.status() sets headers as soon as it has fired. You try to do this multiple times both when checking for errors, and then you try to set the status code again in Tenant.findById().
You end up with a flow like:
if (err) set headers
set headers (again)
findById()
if (err) set headers
set headers (again)
You have to be careful when writing out your response that you only do it at the last point in your logic flow. You can also could set up a global err handler and throw new Error() and stop the flow of logic and handle the output immediately. If you don't, your code will continue to execute even though it encountered an error.
Another tip: callbacks don't work well with returns. And although you can arrange them to work, or implement a promise architecture instead, the simplest fix (and the easiest to learn) is to make your functions all asynchronous.
Try instead something like:
tenant.save(function (err, tenant, callback) {
// add a callback param to your fn ^
if (err) {
throw({
code: 500,
title: 'An error has occured',
error: err
});
} else {
// ^ add an else statement so you don't set the headers twice
// (because res.status() sets headers)
res.status(201).json({
message: 'Tenant created',
obj: tenant
});
}
callback(err, tenant);
// call your async function instead of return,
// and pass both err and tenant as params
// (one or the other may be undefined though, if it fails/succeeds)
});
... Create additional isolated functions (or even modules) for the rest of your tasks, then you can then call your function like this:
tenant.save(function(err, tenant) {
Tenant.findById(tenant._id, function(err, tenant) {
var user = new User({...})
user.save()
});
});

Handling Stripe errors with Node.JS

I am attempting to charge a user when they create an account. Everything is set up and working perfect with the code before. When a user sign's up for a "premium" section of the website they have an account created and are charged with the code below.
The problem: If a user's credit card ends up getting declined for various reasons... their user account is STILL created. How would I change my code below to not reach that part of the code if the credit card fails?
Note: this DOES work for when the user trys to create an account with a username that is taken. The web app redirects them to /buy to select a new username. However it does not work to handle the credit card errors because the user is created first.
Thank you for the help!
user.save(function(err) {
console.log('this is the problem' + ' ' + err)
if(err){
return res.redirect('/buy')
}
var token = req.body.stripeToken; // Using Express
var charge = stripe.charges.create({
amount: 749,
currency: "usd",
description: "Website.com Premium - One time Payment",
source: token,
}, function(err, charge) {
if(err) {
console.log(err);
return res.redirect('/buy')
}
console.log('charged')
req.logIn(user, function(err) {
if(err) {
console.log(err);
}
console.log('all looks good')
res.redirect('/results');
});
});
});
});

NodeJS Return Back

I'm trying to figure out the best way to handle errors with NodeJS. I need to return back (Or display an error and not redirect) if there is an error with either the user signing up or their credit card being accepted. The problem is I get the error "Cannot set headers after they're already set" if I try to use more than one res.redirect. How do I get around this. I've shown the locations where I want to redirect below.
Thanks!
app.post('/quiz', function(req, res) {
var token = req.body.stripeToken; // Using Express
var charge = stripe.charges.create({
amount: 749,
currency: "usd",
description: "Example charge",
source: token,
}, function(err, charge) {
if(err) {
console.log('Did not charge or create error' + err);
// return res.redirect('/');
//I WANT TO RETURN TO HOME HERE IF THERES AN ERROR
//I WANT TO RETURN TO HOME HERE IF THERES AN ERROR
//I WANT TO RETURN TO HOME HERE IF THERES AN ERROR
//I WANT TO RETURN TO HOME HERE IF THERES AN ERROR
}
console.log('charged')
var user = new User({
username: req.body.username,
email: req.body.email,
password: req.body.password,
datapoint: req.body.datapoint
})
user.save(function(err) {
console.log('this is the problem' + ' ' + err)
// return res.redirect('/');
//I WANT TO RETURN TO HOME HERE IF THERES AN ERROR
//I WANT TO RETURN TO HOME HERE IF THERES AN ERROR
//I WANT TO RETURN TO HOME HERE IF THERES AN ERROR
//I WANT TO RETURN TO HOME HERE IF THERES AN ERROR
});
req.logIn(user, function(err) {
if(err) {
console.log(err);
}
console.log('all looks good')
// If everything looks good I want to redirect to the next page
// If everything looks good I want to redirect to the next page
// If everything looks good I want to redirect to the next page
// If everything looks good I want to redirect to the next page
// res.redirect('/jobquiz');
});
});
});

Redirect after error?

I have a set up that let's a user create an account using PassportJS and charges them at the same time using Stripe. This all works.
The issue is this is currently allowing user's to sign up using the same username. the function catches the error in the user.save part. However even though it catches the error it still finishes creating the new user and charging them. I need to return back there. However I have been unable to figure out how to res.redirect or something at that part.
How would I change my code to redirect there if theres an error?
app.post('/quiz', function(req, res) {
var user = new User({
username: req.body.username,
email: req.body.email,
password: req.body.password,
datapoint: req.body.datapoint
})
user.save(function(err) {
console.log('this is the problem' + ' ' + err)
if(err){
// I NEED TO REDIRECT BACK HERE IF THERE IS AN ERROR
// I NEED TO REDIRECT BACK HERE IF THERE IS AN ERROR
// I NEED TO REDIRECT BACK HERE IF THERE IS AN ERROR
// I NEED TO REDIRECT BACK HERE IF THERE IS AN ERROR
// I NEED TO REDIRECT BACK HERE IF THERE IS AN ERROR
}
var token = req.body.stripeToken; // Using Express
var charge = stripe.charges.create({
amount: 749,
currency: "usd",
description: "Example charge",
source: token,
}, function(err, charge) {
if(err) {
console.log(err);
}
console.log('charged')
req.logIn(user, function(err) {
if(err) {
console.log(err);
}
console.log('all looks good')
res.redirect('/jobquiz');
});
});
});
});
use return before res.redirect(). It will stops the execution.
if (err)
return res.redirect('/anywhere');
First of all, you need to unique validate your username so that if there is an attempt to use same username, uniqueness error will be thrown. As you are using mongoose, I suggest npm install mongoose-unique-validator. Set your username as unique. more info.
Secondly, catch the error and redirect them to the current form (the form before submission)
if (err) {
// clear cache to prevent 304 response
res.header('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');
return res.redirect('back');
}
'back' is base on value of req.get('Referrer') which in your case the submission form.

Resources