Cannot add metadata to stripe customer subscription creation using nodejs? - javascript

I can't add metadata to the customer object when creating a new subscription/customer with stripe. Update: The problem I'm having is that the metadata does not save to the customer object. I don't see it in stripe in the logs/events.
// Stripe Response Handler
$scope.stripeCallback = function (code, result) {
result.email = $scope.email;
result.metadata = {'team': $scope.team};
if (result.error) {
window.alert('it failed! error: ' + result.error.message);
} else {
$http.post('/charge', result)
.success(function(data, status, headers, config) {
alert('success');
})
.error(function(data, status, headers, config) {
// console.log(status);
alert('error');
});
}
};
//on the server
app.post('/charge', function(req, res) {
var stripeToken = req.body.id;
var email = req.body.email;
var team = req.body.team;
subscribeUser(stripeToken, res, email, team);
});
// for subscriptions:
function subscribeUser(token, res, email, team){
stripe.customers.create({
card: token,
plan: '001',
email: email,
metadata: team
}, function(err, customer) {
var cust = customer.id;
// you'll probably want to store a reference (customer.id) to the customer
if (err) {
res.send({
ok: false, message: 'There was a problem processing your card (error: ' + JSON.stringify(err) + ')'});
} else {
res.send({
ok: true, message: 'You have been subscribed to a plan!'});
}
});
}
Any ideas would be much appreciated.

In case this helps somebody else, I made a few dumb mistakes:
You need to be sure you are added it to the metadata property
result.metadata = {'team': $scope.team};
You need to make sure you grab the metadata
var team = req.body.metadata;
You need to pass it in as metadata
metadata: team

Related

NodeJS user authentication with JWT

I am just started with nodejs with react frontend. But I have some issue while authenticating user. I am trying to fetch user with specific email and password. My api for this is as follows:
I have created three files controller, services and router files for any api request.
//userServices.js
const db = require('./../../db-connection/connection')
userAuth: (params, callback) => {
db.query(`SELECT * FROM Users WHERE email = ?`,[params.email],
(error, result, fields) => {
if(!error) {
console.log('result = ' + result[0]);
return callback(error, result[0])
}
else
return callback(error)
});
}
And this is my userController js file.
//userController.js
const {create, userindex, userAuth} = require('./UserServices');
const {genSaltSync, hashSync, compareSync} = require('bcrypt');
const {sign} = require('jsonwebtoken');
userLoginAuth: (req, res) => {
const body = req.body;
userAuth(body, (error, results) => {
if (error)
console.log(error);
if (!results) {
return res.json({
success: 0,
data: 'Invalid email or password'
})
}
const result = compareSync(body.password, results.password);
if(!result) {
results.password = undefined;
const jsontoken = sign({result: results}, 'choegyel123', {
expiresIn: '1h'
});
return res.json({
success: 1,
message: 'Login successful',
token: jsontoken
})
} else
console.log('password' + result.password)
return res.json({
success: 0,
error: 'Invalid email or password'
});
});
}
But the problem is in userServices.js file. My sql query is correctly executed but in callback for the ' results ' i am getting weird object. I think I should get some array of corresponding data from the database table and in my console log I am getting [object object]. I am not sure what does this actually mean and I am also all sure this is a blocker, since I cannot retrive password with this object in my userController. Any help would be greatly appreciated. Thanks!
Issues
compareSync returns a boolean with true indicating correct password. You're using if(!result) which is the reverse.
Make sure your {} are correct on the else
You're logging result.password which is undefined because result is your compareSync return value. You meant to log results.password. Avoid using result and results for two different things because it makes it easy to make these mistakes. Something like bcryptResult and resultObj might be better.
When you console.log(), pass data as the second argument or make a second log because concatenating objects always shows [object Object].
Updated snippet
const result = compareSync(body.password, results.password);
if(result) {
results.password = undefined;
const jsontoken = sign({result: results}, 'choegyel123', {
expiresIn: '1h'
});
return res.json({
success: 1,
message: 'Login successful',
token: jsontoken
})
} else {
console.log('password', results.password)
return res.json({
success: 0,
error: 'Invalid email or password'
});
}

How to fix "Error: Can't set headers after they are sent" in Express

I have recently been developing a MERN application and I have recently came into the trouble that express is saying that I am setting headers after they are sent.
I am using mongo db and trying to update a user profile.
I have tried to comment out my res.send points to find the issue but I have failed to do so.
Here is my post method for updating the user profile:
app.post("/api/account/update", (req, res) => {
const { body } = req;
// Validating and Checking Email
if (body.email) {
var email = body.email;
email = email.toLowerCase();
email = email.trim();
body.email = email;
User.find(
{
email: body.email
},
(err, previousUsers) => {
if (previousUsers.length > 0) {
return res.send({
success: false,
message:
"Error: There is already another account with that email address"
});
} else {
}
}
);
}
// Validating Names Function
function checkName(name) {
var alphaExp = /^[a-zA-Z]+$/;
if (!name.match(alphaExp)) {
return res.send({
success: false,
message: "Error: Names cannot contain special characters or numbers"
});
}
}
checkName(body.firstName);
checkName(body.lastName);
// Making sure that all fields cannot be empty
if (!body.email && !body.firstName && !body.lastName) {
return res.send({
success: false,
message: "Error: You cannot submit nothing"
});
}
// Getting User ID from the current session
UserSession.findById(body.tokenID, function(err, userData) {
// Finding User ID using the current users session token
if (userData.isDeleted) {
return res.send({
success: false,
message:
"Error: Session token is no longer valid, please login to recieve a new one"
});
}
// Deleting the token ID from the body object as user table entry doesnt store tokens
delete body.tokenID;
// Finding the user profile and updating fields that are present
User.findByIdAndUpdate(userData.userId, body, function(err, userInfo) {
if (!err) {
return res.send({
success: true,
message: "Success: User was updated successfully"
});
}
});
});
});
This is the call that I am doing to the backend of the site:
onUpdateProfile: function(fieldsObj) {
return new Promise(function(resolve, reject) {
// Get Session Token
const obj = getFromStorage("the_main_app");
// Defining what fields are getting updated
fieldsObj.tokenID = obj.token;
// Post request to backend
fetch("/api/account/update", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(fieldsObj)
})
.then(res => {
console.log("Verify Token - Res");
return res.json();
})
.then(json => {
console.log("Verify Token JSON", json);
if (json.success) {
window.location.href = `/manage-account?success=${json.success}`;
} else {
window.location.href = `/manage-account?success=${json.success}`;
}
});
});
}
Here is my error message that I am getting:
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:491:11)
at ServerResponse.setHeader (_http_outgoing.js:498:3)
at ServerResponse.header (C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\express\lib\response.js:767:10)
at ServerResponse.send (C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\express\lib\response.js:267:15)
at ServerResponse.send (C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\express\lib\response.js:158:21)
at C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\routes\api\account.js:270:22
at C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\mongoose\lib\model.js:4641:16
at process.nextTick (C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\mongoose\lib\query.js:2624:28)
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
[nodemon] app crashed - waiting for file changes before starting...
Can anyone help me with this?
EDIT
I have changed my code, this seems to now work however I feel like its a little messy when put together. Any refactoring tips?
Code:
app.post("/api/account/update", (req, res) => {
// Preform checks on data that is passed through
const { body } = req;
var messages = {
ExistedUser:
"Error: There is already another account with that email address",
NameFormat: "Error: Names cannot contain special characters or numbers",
BlankInputs: "Error: You cannot submit nothing",
accountLoggedOut:
"Error: Session token is no longer valid, please login to recieve a new one",
successfullyUpdated: "Success: User was updated successfully"
};
var usersFound;
if (body.email) {
var email = body.email;
email = email.toLowerCase();
email = email.trim();
body.email = email;
User.find(
{
email: body.email
},
(err, UserCount) => {
usersFound = UserCount;
}
);
}
function capitalize(text) {
return text.replace(/\b\w/g, function(m) {
return m.toUpperCase();
});
}
if (body.firstName) {
body.firstName = capitalize(body.firstName);
}
if (body.lastName) {
body.lastName = capitalize(body.lastName);
}
//Making sure that all fields cannot be empty
if (!body.email && !body.firstName && !body.lastName) {
return res.send({
success: false,
message: messages.BlankInputs
});
}
// Getting User ID from the current session
UserSession.findById(body.tokenID, function(err, userData) {
// Finding User ID using the current users session token
if (userData.isDeleted) {
return res.end({
success: false,
message: messages.accountLoggedOut
});
}
if (userData) {
// Deleting the token ID from the body object as user table entry doesnt store tokens
delete body.tokenID;
// Finding the user profile and updating fields that are present
User.findByIdAndUpdate(userData.userId, body, function(err, userInfo) {
if (userInfo) {
if (!usersFound.length > 0) {
return res.send({
success: true,
message: messages.successfullyUpdated
});
} else {
return res.send({
success: false,
message: messages.ExistedUser
});
}
}
});
}
});
});
You're calling res.send() twice. res.send() ends the process. You ought to refactor such that you call res.write() and only call res.send() when you're done.
This StackOverflow link describes the difference in more detail. What is the difference between res.send and res.write in express?
I believe this is happening, as you're trying to send a response after the first / initial response has already been sent to the browser. For example:
checkName(body.firstName);
checkName(body.lastName);
Running this function twice is going to try and yield 2 different "response" messages.
The product of a single route, should ultimately be a single response.
Thanks for all your help on this issue.
Here is my final code that allowed it to work.
I have also tried to "refactor" it too. Let me know if you'd do something else.
app.post("/api/account/update", (req, res) => {
const { body } = req;
console.log(body, "Logged body");
// Defining objects to be used at the end of request
var updateUserInfo = {
userInfo: {},
sessionToken: body.tokenID
};
var hasErrors = {
errors: {}
};
// Checking that there is at least one value to update
if (!body.email && !body.firstName && !body.lastName) {
var blankError = {
success: false,
message: "Error: You cannot change your details to nothing"
};
hasErrors.errors = { ...hasErrors.errors, ...blankError };
} else {
console.log("Normal Body", body);
clean(body);
console.log("Cleaned Body", body);
updateUserInfo.userInfo = body;
delete updateUserInfo.userInfo.tokenID;
}
// Function to check if object is empty
function isEmpty(obj) {
if (Object.keys(obj).length === 0) {
return true;
} else {
return false;
}
}
// Function to remove objects from body if blank
function clean(obj) {
for (var propName in obj) {
if (obj[propName] === "" || obj[propName] === null) {
delete obj[propName];
}
}
}
// Checking and Formatting Names Given
function capitalize(text) {
return text.replace(/\b\w/g, function(m) {
return m.toUpperCase();
});
}
if (body.firstName) {
body.firstName = capitalize(body.firstName);
}
if (body.lastName) {
body.lastName = capitalize(body.lastName);
}
// Checking and formatting email
if (body.email) {
body.email = body.email.toLowerCase();
body.email = body.email.trim();
// Checking for email in database
User.find({ email: body.email }, (err, EmailsFound) => {
if (EmailsFound.length > 0) {
var EmailsFoundErr = {
success: false,
message: "There is already an account with that email address"
};
hasErrors.errors = { ...hasErrors.errors, ...EmailsFoundErr };
}
});
}
// Getting User Session Token
UserSession.findById(updateUserInfo.sessionToken, function(err, userData) {
// Finding User ID using the current users session token
if (userData.isDeleted) {
var userDeletedError = {
success: false,
message:
"Your account is currently logged out, you must login to change account details"
};
hasErrors.errors = { ...hasErrors.errors, ...userDeletedError };
} else {
// Finding the user profile and updating fields that are present
User.findByIdAndUpdate(
userData.userId,
updateUserInfo.userInfo,
function(err, userInfo) {
// userInfo varable contains user db entry
if (err) {
var updateUserError = {
success: false,
message: "Error: Server Error"
};
hasErrors.errors = {
...hasErrors.errors,
...updateUserError
};
}
if (isEmpty(hasErrors.errors)) {
res.send({
success: true,
message: "Success: You have updated your profile!"
});
} else {
res.send({
success: false,
message: hasErrors.errors
});
}
}
);
}
});
});

Updating mongodb in a mean stack

I want to update my mongodb I know what to write on the server side but i dont know how to use it on the client side in angular. can you help ?
here is my server side code
module.exports.updateUser = function (req, res) {
// get a user with ID of 1
User.findById(1, function(err, user) {
if (err) throw err;
// change the users location
user.location = 'uk';
// save the user
user.save(function(err) {
if (err) throw err;
console.log('User successfully updated!');
});
});
}
You need to create a rest api(/users/save)
var users = require('./src/servies/users');
app.post('/users/save', users.updateUser);
that will call your updateUser function.
In angular you can use http module something like below code
<script>
var app = angular.module("app", []);
app.controller("HttpPostController", function ($scope, $http) {
$scope.SendData = function () {
// use $.param jQuery function to serialize data from JSON
var data = $.param({
location: $scope.location
});
var config = {
headers : {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8;'
}
}
// calling post /users/save api from angular code
$http.post('/users/save', data, config)
.success(function (data, status, headers, config) {
})
.error(function (data, status, header, config) {
});
};
});
</script>

$window.sessionStorage for login and logout ( token based);

I create a login and logout function with Node.js and Angular.js which is token based. The token I am saving into window storage.
The problem is if I logout it just logout for one browser and also if I loggedin it not recognize if I am already loggedin. I think I have to extend my programm.
My question is how can I delete the storage for every open browser where I loggedin? Or schould I ask within my code if I am loggedin and how could I do this?
Thanks in advance!
NODE.JS CODE
app.post('/logout', function(req, res){
jwt.verify(req.body.token, 'secretKey', function(err, decoded) {
console.log("Decoded " + decoded);
if(decoded._id != null){
User.findOne({
_id : decoded._id
}, function(err, user) {
if (err) {
console.log('Error occured', err);
} else {
if (user) {
res.end();
}
}
});
}else{
Console.log("Could not logout");
}
});
});
app.post('/login', function(req, res) {
User.findOne({
email : req.body.email
}, function(err, user) {
if (err) {
console.log('Error occured', err);
} else {
if (user) {
// check if password matches
if (req.body.password != undefined) {
var hashPWCheck = bcrypt.compareSync(req.body.password, user.password);
// true
//console.log(hashPWCheck);
if (!(hashPWCheck)) {
res.json({
success : false,
message : 'Authentication failed. Wrong password.'
});
console.log('Authentication failed. Wrong password.');
} else {
var token = jwt.sign(user, 'secretKey', {
expiresInMinutes : 60 // expires in 1 Minute
});
res.json({token : token, email : user.email});
console.log("Token created & sent to Client(UserCtrlLogin): " + token);
}
} else {
console.log("Password is required!");
}
} else {
console.log("Incorect E-Mail");
}
}
});
});
ANGULAR.js Code
app.controller('UserCtrlLogin', function($scope, $http, $window, $location, $rootScope) {
$scope.logout = function(){
var sessionlogout = $window.sessionStorage.getItem('token');
var formData = {
token : sessionlogout
};
$http.post('/logout', formData).success(function(data, status, headers, config) {
if(status == 200){
$rootScope.isAlive = false;
$rootScope.ali = false;
$window.sessionStorage.removeItem('token');
}else{
$window.sessionStorage.removeItem('token');
$rootScope.isAlive = false;
}
});
};
$scope.signin = function() {
var formData = {
email : $scope.email,
password : $scope.password
};
// $window.sessionStorage.removeItem('token');
$http.post('/login', formData).success(function(data, status, headers, config) {
console.log('Data: ' + data.email);
//console.log('Status: ' + status);
if (status == 200) {
if(data.email == "goekguel.ali#gmail.com"){
$rootScope.ali = true;
}
$rootScope.isAlive = true;
$window.sessionStorage.setItem('token', data.token);
console.log("Token saved into Storage from Server(Node.js function /login)");
}
}).error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
$window.sessionStorage.removeItem('token');
});
};
});
You need to save tokens in the database, and if you log in or log out in one browser you have to mark token as valid/invalid, and in another browser it's required to check token status on backend.
P.s. See satellizer, it's just my recommendation for front-end auth module.

stripe params using javascript

I'm using Stripe in conjunction with Parse Cloud Code.
I can pass the token ID and the customer email, but I need their name too. Stripe doesn't have any straight JS docs so it's difficult to understand. How can I pass the name?
Here's my client side code:
Parse.Cloud.run('createCustomer',{
token: token.id,
email: token.email,
}, {
// Success handler
success: function(message) {
alert('Success: ' + message);
},
// Error handler
error: function(message) {
alert('Error: ' + message);
}
})
and the backend Cloud Code:
Parse.Cloud.define("createCustomer", function(request, response) {
console.log(request.params)
Stripe.Customers.create({
account_balance: 0,
email: request.params.email,
description: "stripe customer",
metadata: {
userId: request.params.objectId, // e.g PFUser object ID
createWithCard: true
}
}, {
success: function(httpResponse) {
response.success(name + userId); // return customerId
},
error: function(httpResponse) {
console.log(httpResponse");
response.error("Cannot create a new customer.");
}
});
});
This works for me. And yes, there is little documentation on this. :)
Parse.Cloud.define("createCustomer", function(request, response) {
Stripe.Customers.create({
card: request.params.cardToken, // the token id should be sent from the client
// account_balance: 0,
email: request.params.email,
description: 'new stripe user',
metadata: {
name: request.params.name
}
},
{
success: function(httpResponse) {
response.success(httpResponse); // return customerId
},
error: function(httpResponse) {
console.log(httpResponse);
response.error(httpResponse);
}
});
});

Categories

Resources