Stripe subscription not updating quantity - node.js

I have a fairly simple application that just allows you to create a single admin user that can create sub users under their account. When an admin creates another user I want them to update their subscription on stripe with their current number of users which is gets stored at user.company.subUserCount. But when I do this the user model is not updated with the correct value and stripe will not update at all.
Was hoping someone could take a look at my code snippets and see what is wrong with it that is causing it not to update Stripe.
Route
// POST USER/NEW
app.post('/user/new',
isAuthenticated,
company.postNewUserPlan,
sessions.postSignupSub,
(req, res) => {
User.findById(req.user.id, function(err, user) {
user.company.subUserCount = req.user.company.subUserCount + 1;
user.save();
});
});
company.postNewUserPlan
exports.postNewUserPlan = function(req, res, next){
var plan = req.user.company.stripe.plan;
var coupon = null;
var stripeToken = null;
plan = plan.toLowerCase();
if(req.body.stripeToken){
stripeToken = req.body.stripeToken;
}
User.findById(req.user.id, function(err, user) {
if (err) return next(err);
var quantity = user.company.subUserCount + 1;
user.setPlan(plan, coupon, quantity, stripeToken, function (err) {
var msg;
if (err) {
if(err.code && err.code == 'card_declined'){
msg = 'Your card was declined. Please provide a valid card.';
} else if(err && err.message) {
msg = err.message;
} else {
msg = 'An unexpected error occurred.';
}
req.flash('errors', msg);
return res.redirect('/user/create');
}
});
});
next();
};
sessions.postSignupSub passport code
passport.use('signup-sub', new LocalStrategy({
usernameField: 'email',
passReqToCallback : true
},
function(req, email, password, done) {
User.findOne({ email: req.body.email }, function(err, existingUser) {
if(err){
console.log(err);
}
if (existingUser) {
req.flash('form', {
email: req.body.email
});
return done(null, false, req.flash('error', 'An account with that email address already exists.'));
}
var preRole = req.body.role;
var role = ''
if (preRole === undefined) {
role = 'manager';
} else if (preRole === 'on') {
role = 'employee';
}
// edit this portion to accept other properties when creating a user.
var user = new User({
email: req.body.email,
password: req.body.password, // user schema pre save task hashes this password
role: role,
companyID: req.user.companyID,
isVerified: true
});
user.save(function(err) {
if (err) return done(err, false, req.flash('error', 'Error saving user.'));
var time = 14 * 24 * 3600000;
req.session.cookie.maxAge = time; //2 weeks
req.session.cookie.expires = new Date(Date.now() + time);
req.session.touch();
return done(null, user, req.flash('success', `Your new ${role} has been created`));
});
});
})
);

I managed to solve the issue it was related to code that was not included in the OP after a bit of testing I figured out the subscription was not being updated with a quantity but instead was only being created with it.
Code
schema.methods.setPlan = function(plan, coupon, quantity, stripe_token, cb) {
var user = this;
var subscriptionHandler = function(err, subscription) {
if(err) return cb(err);
user.company.stripe.plan = plan;
user.company.stripe.subscriptionId = subscription.id;
user.company.subUserCount = quantity;
user.save(function(err){
if (err) return cb(err);
return cb(null);
});
};
var createSubscription = function(){
stripe.customers.createSubscription(
user.company.stripe.customerId,
{plan: plan, coupon: coupon, quantity: quantity},
subscriptionHandler
);
};
if(stripe_token) {
user.setCard(stripe_token, function(err){
if (err) return cb(err);
createSubscription();
});
} else {
if (user.company.stripe.subscriptionId){
// update subscription
stripe.customers.updateSubscription(
user.company.stripe.customerId,
user.company.stripe.subscriptionId,
{ plan: plan, coupon: coupon, ***quantity: quantity*** }, <-- Part I had to change
subscriptionHandler
);
} else {
createSubscription();
}
}
};

Related

How to fix "TypeError: cb is not a function" error for comparing passwords

I've been starting to add user authentication into my app and when adding the login route, i've been getting the error "TypeError: cb is not a function". I know it is coming from my login route as all my other routes work fine.
I have tried researching the issue and trying a few fixes that i've found but none have worked. So i'm starting to believe i've messed up somewhere and I can't find where.
Login Route:
router.post('/login', function (req, res) {
User.findOne({ username: req.body.username }, function (err, user) {
if (err || user == null) {
console.log(err);
res.redirect('/login');
}
if (!user.comparePassword(req.body.password)) {
req.flash('invalidDetails', 'Wrong username or password!');
res.redirect('/login');
} else {
req.session.userId = user._id;
req.flash('loggedIn', 'User ' + req.body.username + ' has been logged in!');
return res.redirect('/');
}
});
});
User Model:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
bcrypt = require('bcrypt'),
SALT_WORK_FACTOR = 10;
var UserSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
trim: true
},
username: {
type: String,
unique: true,
required: true,
trim: true
},
password: {
type: String,
required: true
}
});
UserSchema.statics.authenticate = function (email, password, callback) {
User.findOne({ email: email }).exec(function (err, user) {
if (err) {
return callback(err);
} else if (!user) {
var err = new Error('User not found.');
err.status = 401;
return callback(err);
}
bcrypt.compare(password, hash, function (err, result) {
if (result === true) {
return callback(null, user);
} else {
return callback();
}
});
});
};
UserSchema.pre('save', function (next) {
var user = this;
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
// generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if (err) return next(err);
// hash the password along with our new salt
bcrypt.hash(user.password, salt, function (err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
next();
});
});
});
UserSchema.methods.comparePassword = function comparePassword(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function (err, isMatch) {
if (err) {
return cb(err);
}
cb(null, isMatch);
});
};
var User = mongoose.model('User', UserSchema);
module.exports = User;
I'm expecting it to compare the password with the password that has been entered into the login form with the password that is stored in the database and log in the user if the password is correct or redirect back to the login page with the invalidDetails flash message if the password is incorrect.
But what i'm actually getting is the "TypeError: cb is not a function" error when trying to login.
You are not passing a callback that's why.
If you want a promise based method you can write something like this
AuthSchema.methods.comparePassword = function(candidatePassword) {
const currentPassword = this.password;
return new Promise((resolve, reject) => {
bcrypt.compare(candidatePassword, currentPassword, function(err, isMatch) {
if (err) return reject(err);
resolve(isMatch);
});
})
};
And now you can call that with await

passport-local-mongoose set new password after checking for old password

I am using passportjs to handle auth of my app.
Once the user is logged in, I want to add the possibility to change the password from inside the app.
this is in my controller:
$http.post('/change-my-password',{oldPassword: $scope.user.oldpassword, newPassword: $scope.user.newpassword})
.then(function (res) {
if (res.data.success) {
// password has been changed.
} else {
// old password was wrong.
}
});
and this is my route handler in express nodejs in backend:
router.post('/change-my-password', function (req, res) {
if (!req.isAuthenticated()) {
return res.status(403).json({
success: false
});
}
UserSchema.findById(req.user._id, function(err, user){
if (err) return res.status(200).json({success: false});
user.validatePassword(req.body.oldPassword, function(err) {
if (err){
return res.status(200).json({
success: false
});
}
user.setPassword(req.body.newPassword, function() {
if (err || !user) {
return res.status(200).json(
{
success: false
}
)
}
user.save(function(err) {
if (err) return res.status(200).json({success: false});
req.login(user, function (err) {
if (err) return res.status(200).json({success: false});
return res.status(200).json({success: true});
});
});
});
});
});
});
here is my user schema model:
// user model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');
var bcrypt = require('bcrypt-nodejs');
var UserSchema = new Schema({
email: String,
password: String,
confirmStatus: Boolean,
token: String,
registerAt: Number
});
UserSchema.methods.validatePassword = function (password, callback) {
this.authenticate(password, callback);
};
UserSchema.plugin(passportLocalMongoose,
{
usernameField: 'email'
});
module.exports = mongoose.model('users', UserSchema);
the problem:
I find my user by Id in my mongoose schema UserSchema then I should check if the oldPassword is valid or not, and then I set the new password.
I successfully find the user and the set the new password. But the part that should check for comparison of the old password field, doesn't work at all. Whatever I enter in the old password field gets accepts as OK and that step is skipped. Whereas, it should throws an error saying that the old password is wrong.
I am also advised to use sanitizedUser in order not to show my salt and etc.
Question is: how can I first do the comparison check of the old password and then do the set new password step? If possible, how can I use the sanitize? And how can I check if the user is not entering the same password as the new password? or if possible, saying that the new password is very similar to the old one?
You can implement the it using the new feature added 3 days ago:
just use the changePassword method, and it handles it through this:
schema.methods.changePassword = function(oldPassword, newPassword, cb) {
if (!oldPassword || !newPassword) {
return cb(new errors.MissingPasswordError(options.errorMessages.MissingPasswordError));
}
var self = this;
this.authenticate(oldPassword, function(err, authenticated) {
if (err) { return cb(err); }
if (!authenticated) {
return cb(new errors.IncorrectPasswordError(options.errorMessages.IncorrectPasswordError));
}
self.setPassword(newPassword, function(setPasswordErr, user) {
if (setPasswordErr) { return cb(setPasswordErr); }
self.save(function(saveErr) {
if (saveErr) { return cb(saveErr); }
cb(null, user);
});
});
});
};
so in your code, you need to replace the validatePassword method by this:
user.changePassword(req.body.oldPassword,req.body.newPassword, function(err) {
if (err){
return res.status(200).json({
success: false
});
}
hope this works for you.

PassportJS authentication

So, I have everything working but it is not showing it is an authenticate user even though it arrives at the proper places...
javascript code from the page to validate login
var UserManager = {
validateLogin : function (username, password) {
var userData = {
username: username,
password: password
}
return new Promise(function(resolve, reject) {
$.ajax({
url: "/musicplayer/users/api/login",
dataType: "json",
data: userData,
type: "POST",
success: function loginSuccess(result, status, xhr) {
resolve(null);
},
error: function loginError(xhr, status, result) {
reject(new Error(result));
},
});
});
}
}
function userLogin(){
UserManager.validateLogin($('#loginEmail').val(), $('#loginPassword').val()).then(function(response) {
window.location = '/musicplayer/library'
},
function(error){
$("#msgBox").messageBox({"messages" : error.message, "title" : "Warning", boxtype: 4 });
$("#msgBox").messageBox("show");
});
return false;
}
local.strategy.js
var passport = require('passport');
var localStrategy = require('passport-local').Strategy;
var userLibrary = require('../../classes/music/userlibrary.js');
module.exports = function () {
passport.use(new localStrategy(
{
usernameField: 'username',
passwordField: 'password'
},
function(username, password, done) {
//validating user here
var userManager = new userLibrary.UserManager();
userManager.login(username, password).then(
function (user){
done(null, user);
},
function (reason){
if (reason.err) {
done(err, false, info);
}
else {
done(null, false, {message: reason.message});
}
}
);
})
);
};
Router
/******* validate the user login ********/
usersRouter.post('/api/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
console.log("Login Failed", err.message + " - " + err.stack);
if (req.xhr){
res.status(500).send({ error: 'Internal Error' });
}
else {
next(err);
}
}
else if (!err && !user){
err = new Error();
err.message = info.message;
err.status = 401;
console.log("Invalid Data", err.message);
if (req.xhr){
res.status(401).send({ error: err.message });
}
else {
next(err);
}
}
else if (user){
console.log("Successful Login:", user);
res.status(200).send({message: "successful"});
}
}
)(req, res, next);
});
passport.js file which has my Middleware...
var passport = require("passport");
module.exports = function (app) {
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done){
done(null, user);
});
passport.deserializeUser(function(user, done){
done(null, user);
});
require('./strategies/local.strategy')();
app.all('/musicplayer/*', function (req, res, next){
// logged in
//need function for exceptions
if (req.user || req.url === '/musicplayer/users/api/login' || req.url === '/musicplayer/users/signin') {
next();
}
// not logged in
else {
// 401 Not Authorized
var err = new Error("Not Authorized");
err.status = 401;
next(err);
}
});
}
Userlibrary/UserManager
I am using promises to be able to utilize the creation of a library and to deal with sync versus async issues that I ran into early on...
var sqlite3 = require('sqlite3').verbose();
function User() {
this.email = "";
this.password = "";
this.userid = "";
};
function UserManager () {
this.user = new User();
};
UserManager.prototype.login = function (email, password) {
var db = new sqlite3.Database('./data/MusicPlayer.db');
params = {
$email: email,
$password: password
}
var self = this;
return new Promise(function(resolve, reject){
db.serialize(function () {
db.get("SELECT * FROM users WHERE email = $email and password = $password", params, function (err, row) {
db.close();
if (!err && row) {
//log in passed
self.user.userid = row.userid;
self.user.email = row.email;
self.user.password = row.password;
resolve(self.user);
}
else if (!err) {
//log in failed log event
reject({
err: err,
message: null
});
}
else {
//error happened through out an event to log the error
reject({
message : "Email and/or Password combination was not found",
err : null
});
}
});
});
});
};
module.exports = {
User : User,
UserManager : UserManager
}
Now, I have debugged this and it is for sure getting to "successful Login"
Returns to the browser with success, the browser says okay let me redirect you to the library page (which is really just a blank page). When it goes to my library page I get a 401 unauthorized.
So if I debug inside the middleware to ensure authentication. I look at req.user and it is undefined and I try req.isAuthenticated() it returns a false.
I think I must be missing something...
What I want is a global authentication saying hey is this person logged in. And then I will set up the route/route basis say okay do they have permission for this page or web service call.
Right now I am sticking with session for everything as it is not useful to me to learn web tokens at this point and time.
Any help would be appreciated... I been around and around on this looking at examples out there. But the examples I find are the "basic" examples no one calling a library to validate from database or they are not trying to set up the authorization globally but rather on a route by route basis.
Upon searching I found this article
https://github.com/jaredhanson/passport/issues/255
then I found this in documentation
app.get('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
and that worked for me... I basically forgot to do the req.logIn method itself when using the custom callback.... I knew it was something simple... Hope this helps someone in the future.

SPA Authentication AngularJS -> PassportJS -> MongoDB

I am trying for days to setup Passport on SPA page. On Client Side I am using AngularJS $http and generally it's working.
angular.module('Factory', []).factory('MainFactory', ['$http',function($http) {
return {
RegisterNewAccount : function(AccountInformation) {
return $http.post('/api/PageSignup', AccountInformation);
}
}
}]);
angular.module('Controllers', [])
.controller('MainPage', ['$scope','MainFactory', function($scope, MainFactory) {
$scope.Signup = function(){
var account = {
email: $scope.RegisterEmail,
password: $scope.RegisterPassword
};
MainFactory.RegisterNewAccount(account)
.success(function(data) {
console.log(data);
});
};
}]);
My problem is on server side code, i am new to passport and still cant figure it out how to make it work without redirect thing. My passport setup is below:
var passport = require('passport');
var LocalStrategy= require('passport-local').Strategy;
passport.use('local-signup', new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
},
function(req, email, password, done) {
if (email) email = email.toLowerCase();
process.nextTick(function() {
if (!req.user) {
User.findOne({ 'local.email' : email }, function(err, user) {
if (err) return done(err);
if (user) return done(null, false, { message: 'That email is already taken.' });
else {
var newUser = new User();
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
newUser.save(function(err) {
if (err) return done(err);
return done(null, newUser);
});
}
});
} else if ( !req.user.local.email ) {
User.findOne({ 'local.email' : email }, function(err, user) {
if (err) return done(err);
if (user) {
return done(null, false, { message: 'That email is already taken.'});
} else {
var user = req.user;
user.local.email = email;
user.local.password = user.generateHash(password);
user.save(function (err) {
if (err)
return done(err);
return done(null,user);
});
}
});
} else {
return done(null, req.user);
}
});
}));
var app = express();
app.post('/api/PageSignup', function(req, res, next) {
passport.authenticate('local-signup', function(err, user, info) {
if (err) { return next(err) }
if (!user) {
return res.send(info.message);
}
req.logIn(user, function(err) {
return res.send(user);
});
})(req, res, next);
});
If there are clear example on SPA Authentication with passport please show me.
Overwise can you show me where I am mistaking in my server side code?

Q-promises and error handling

I am trying to understand Q Promises and how to handle two different errors thrown from two different then blocks.
Here is the function I would like to "Promisfy":
router.post('/user', function(req, res) {
var user = new User(req.body);
User.findOne({ email: req.body.email }, function(error, foundUser) {
if(foundUser) {
res.send("Error: User with email " + foundUser.email + " already exists.", 409);
} else {
User.create(user, function(err, createdUser) {
if (err) {
res.send(err, 400);
} else {
res.json({ id: createdUser.id }, 201);
}
});
}
});
});
It takes some User details, and tries to create a new User if one doesn't exist already with the same email. If one does, send a 409. I also handle the normal mongoose error with a 400.
I've tried using mongoose-q to convert it over, and I end up with this:
router.post('/user', function(req, res) {
var user = new User(req.body);
User.findOneQ({email : req.body.email})
.then(function(existingUser) {
if (existingUser) {
res.send("Error: User with email " + existingUser.email + " already exists.", 409);
}
return User.create(user);
})
.then(function(createdUser) {
res.json({ id: createdUser.id }, 201);
})
.fail(function(err) {
res.send(err, 400)
})
});
Is this correct? Is there anyway to push that existing user check into a fail block? I.e. throw an Error, and then catch it and deal with it?
Something like this perhaps:
router.post('/user', function(req, res) {
var user = new User(req.body);
User.findOneQ({email : req.body.email})
.then(function(existingUser) {
if (existingUser) {
throw new Error("Error: User with email " + existingUser.email + " already exists.");
}
return User.create(user);
})
.then(function(createdUser) {
res.json({ id: createdUser.id }, 201);
})
.fail(function(duplicateEmailError) {
res.send(duplicateEmailError.message)
})
.fail(function(mongoError) {
res.send(mongoError, 400)
})
});
I'm not experienced enough with Q. However, I can answer with bluebird.
bluebird supports typed catches. Which means you can create new Error subclasses, throw them, and handle them accordingly.
I've used newerror in my example to simplify the creation of Error subclasses.
var newerror = require('newerror');
var DuplicateEmailError = newerror('DuplicateEmailError');
router.post('/user', function(req, res) {
var user = new User(req.body);
User.findOneAsync({ email: req.body.email }).then(function(existingUser) {
if (existingUser) {
throw new DuplicateEmailError('Error: User with email ' + existingUser.email + ' already exists.');
}
return User.createAsync(user);
}).then(function(createdUser) {
res.json({ id: createdUser.id }, 201);
}).catch(DuplicateEmailError, function(err) {
res.send(err.message, 409);
}).catch(function(err) {
res.send(err.message, 500);
});
});

Resources