KeystoneJS - Stripe Integration - node.js

I am trying to setup a stripe checkout page on a KeystoneJS webpage.
The view.on('post'... function doesn't seem to work and I am not able to find the source of the issue:
Here is my code:
public/js/stripe.js
// Handle form submission
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
event.preventDefault();
stripe.createToken(card).then(function(result) {
if (result.error) {
// Inform the user if there was an error
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server
onReceiveToken(result.token);
}
});
});
var onReceiveToken = function(token, args) {
// Submit token to server so it can charge the card
$.ajax({
url: '/checkout',
type: 'POST',
data: {
action: 'charge',
stripeToken: token.id
},
success: function(data) {
console.log('Returns the HTML content of checkout.html');
}
})
};
routes/views/checkout.js
var keystone = require('keystone');
var stripe = require('stripe')("STRIPE_KEY");
exports = module.exports = function (req, res) {
var view = new keystone.View(req, res);
var locals = res.locals;
// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'checkout';
// Render the view
view.render('checkout');
console.log(JSON.stringify(req.body)); <-- Is rendered correctly
//Create Subscription
view.on('post', { "action":"charge" }, function (next) {
console.log(JSON.stringify(req.body)); <-- Isn't rendered
stripe.customers.create({
description: 'Customer for elizabeth.williams#example.com',
source: res.token,
}, function(err, customer) {
// asynchronously called
});
next()
});
};
I would be grateful for any help :-)

Found the answer just after writing the question:
You must render the view last.
var keystone = require('keystone');
var stripe = require('stripe')("STRIPE_KEY");
exports = module.exports = function (req, res) {
var view = new keystone.View(req, res);
var locals = res.locals;
// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'checkout';
//Create Subscription
view.on('post', { "action":"charge" }, function (next) {
console.log(JSON.stringify(req.body)); <-- Isn't rendered
stripe.customers.create({
description: 'Customer for elizabeth.williams#example.com',
source: res.token,
}, function(err, customer) {
// asynchronously called
});
next()
});
// Render the view
view.render('checkout');
};

Related

Backbone and Express: concatinating (duplicating) routes on res.redirect

I have an action where I need to update MongoDB entry including _id field, which requires deleting old entry and making a new one, here is server side:
exports.update = function(req, res, next){
var outcome = [];
outcome.previousId = req.params.id;
outcome.newId = req.body.name;
var getPreviousRecord = function(callback) {
req.app.db.models.AccountGroup
.findOne({ _id: req.params.id })
.lean()
.exec(function(err, accountGroups) {
if (err) {
return callback(err, null);
}
outcome.accountGroups = accountGroups;
return callback(null, 'done');
});
};
var makeNewRecord = function(callback) {
var permissions = outcome.accountGroups.permissions;
var fieldsToSet = {
_id: outcome.newId.toLowerCase(),
name: outcome.newId,
permissions: permissions
};
req.app.db.models.AccountGroup
.create(fieldsToSet, function(err, record) {
if (err) {
return callback(err, null);
}
outcome.record = record;
return callback(null, 'done');
});
};
var deletePreviousRecord = function() {
req.app.db.models.AccountGroup
.findByIdAndRemove(outcome.previousId)
.exec(function(err) {
if (err) {
return next(err);
}
res.redirect('admin/account-groups/' + outcome.newId + '/');
});
};
var asyncFinally = function(err) {
if (err) {
return next(err);
}
};
require('async').series([getPreviousRecord, makeNewRecord, deletePreviousRecord], asyncFinally);
};
It works fine, but I can't make this work normally on the front-end, it returns me both old route and a new route, for example:
PUT /admin/account-groups/customers22/admin/account-groups/Customers2233/ 404 213.749 ms - 31
where customers22 is old _id and customers2233 is new _id. If I navigate from another page to new entry it gets route normally.
On client side:
(function() {
'use strict';
app = app || {};
app.Details = Backbone.Model.extend({
idAttribute: '_id',
defaults: {
success: false,
errors: [],
errfor: {},
name: ''
},
url: function() {
return '/admin/account-groups/'+ app.mainView.model.id +'/';
},
parse: function(response) {
if (response.accountGroup) {
app.mainView.model.set(response.accountGroup);
delete response.accountGroup;
}
return response;
}
});
app.DetailsView = Backbone.View.extend({
el: '#details',
events: {
'click .btn-update': 'update'
},
template: Handlebars.compile( $('#tmpl-details').html() ),
initialize: function() {
this.model = new app.Details();
this.syncUp();
this.listenTo(app.mainView.model, 'change', this.syncUp);
this.listenTo(this.model, 'sync', this.render);
this.render();
},
syncUp: function() {
this.model.set({
_id: app.mainView.model.id,
name: app.mainView.model.get('name')
});
},
render: function() {
this.$el.html(this.template( this.model.attributes ));
for (var key in this.model.attributes) {
if (this.model.attributes.hasOwnProperty(key)) {
this.$el.find('[name="'+ key +'"]').val(this.model.attributes[key]);
}
}
},
update: function() {
this.model.save({
name: this.$el.find('[name="name"]').val()
});
}
});
app.MainView = Backbone.View.extend({
el: '.page .container',
initialize: function() {
app.mainView = this;
this.model = new app.AccountGroup( JSON.parse( unescape($('#data-record').html()) ) );
// ...
app.detailsView = new app.DetailsView();
}
});
$(document).ready(function() {
app.mainView = new app.MainView();
});
}());
It probably requires to trigger both model.save and model.destroy or prevent URL being used. Any advice on how to do it is appreciated, thank you.
Edit
Just a typo mistake here that is not related to the question, recklessly checking routes, see as cancelled
I believe the problem is here:
res.redirect('admin/account-groups/' + outcome.newId + '/');
That's a relative path so it'll be appended onto the current URL. I suspect you want something like this:
res.redirect('/admin/account-groups/' + outcome.newId + '/');

Web Digits Fabric Authentication

I am using digits web. I am using the cannonball example. I am running the below code on my local comptuter.
Heres my code of client side
<script>document.getElementById('digits-sdk').onload = function() {
Digits.init({ consumerKey: 'my consumer key' });
Digits.embed({
container: '#my-digits-container',
theme: {
/* Input fields borders */
},
phoneNumber: '+91',
})
.done(onLogin) /*handle the response*/
.fail(onLoginFailure);
};
function onLoginFailure(loginResponse) {
console.log('Digits login failed.');
//setDigitsButton('Verify again');
}
/* Validate and log use in. */
function onLogin(loginResponse){
// Send headers to your server and validate user by calling Digits’ API
//var oAuthHeaders = loginResponse.oauth_echo_headers;
var oAuthHeaders = parseOAuthHeaders(loginResponse.oauth_echo_headers);
$.ajax({
type: 'POST',
url: '/digits',
data: oAuthHeaders,
success: onDigitsSuccess
});
// setDigitsButton('Step 2.....');
}
function parseOAuthHeaders(oAuthEchoHeaders) {
var credentials = oAuthEchoHeaders['X-Verify-Credentials-Authorization'];
var apiUrl = oAuthEchoHeaders['X-Auth-Service-Provider'];
console.log(apiUrl);
return {
apiUrl: apiUrl,
credentials: credentials
};
}
function onDigitsSuccess(response) {
console.log(response.phoneNumber);
setDigitsNumber(response.phoneNumber);
}
function setDigitsNumber(phoneNumber) {
document.getElementById('notr').value = phoneNumber;
console.log('Digits phone number retrieved.');
}
</script>
In the above code I have changed the consumer key only. So ignore that.
And heres my server code
var express = require("express");
var app = express();
var router = express.Router();
var path = __dirname + '/static/';
app.use(express.static(__dirname + '/static'));
router.use(function (req,res,next) {
console.log("/" + req.method);
next();
});
router.get("/",function(req,res){
res.sendFile(path + "homepage9.html");
});
router.get("/about",function(req,res){
res.sendFile(path + "about.html");
});
router.get("/contact",function(req,res){
res.sendFile(path + "contact.html");
});
app.use("/",router);
app.use("*",function(req,res){
res.sendFile(path + "404.html");
});
app.listen(3000,function(){
console.log("Live at Port 3000");
});
var fs = require('fs');
var nconf = require('nconf');
var url = require('url');
var request = require('request');
router.post('/digits', function (req, res) {
console.log("digits entered")
var apiUrl = req.body['apiUrl']
var credentials = req.body['credentials']
var verified = true;
var messages = [];
if (credentials.indexOf('oauth_consumer_key="' + 'my consumer key' + '"') == -1) {
verified = false;
messages.push('The Digits API key does not match.');
}
var hostname = url.parse(req.body.apiUrl).hostname;
if (hostname != 'api.digits.com' && hostname != 'api.twitter.com') {
verified = false;
messages.push('Invalid API hostname.');
}
// Do not perform the request if the API key or hostname are not verified.
if (!verified) {
return res.send({
phoneNumber: "",
userID: "",
error: messages.join(' ')
});
}
// Prepare the request to the Digits API.
var options = {
url: apiUrl,
headers: {
'Authorization': credentials
}
};
// Perform the request to the Digits API.
request.get(options, function (error, response, body) {
if (!error && response.statusCode == 200) {
// Send the verified phone number and Digits user ID.
var digits = JSON.parse(body)
return res.send({
phoneNumber: digits.phone_number,
userID: digits.id_str,
error: ''
});
} else {
// Send the error.
return res.send({
phoneNumber: '',
userID: '',
error: error.message
});
}
});
});
But on the node console i am getting
cannot read property 'apiUrl' of undefined.
on google chrome console i am getting
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
Can any one help of what I am doing wrong.
Also in the cannon ball example i found that it nowhere uses the consumer secret key. Why is that?

Loopback + Socket.io custom notifications

I have models: Team, Project, Task. Tasks in projects, projects in teams, teams consists users.
I'm creating my app like in example - https://docs.strongloop.com/display/MSG/Building+a+real-time+app+using+socket.io+and+AngularJS
In my example:
server/server.js
...
app.use(loopback.token({ model: app.models.accessToken }));
// Bootstrap the application, configure models, datasources and middleware.
// Sub-apps like REST API are mounted via boot scripts.
boot(app, __dirname, function(err) {
if (err) throw err;
// start the server if `$ node server.js`
if (require.main === module) {
//Comment this app.start line and add following lines
//app.start();
app.io = require('socket.io')(app.start());
require('socketio-auth')(app.io, {
authenticate: function (socket, value, callback) {
var AccessToken = app.models.AccessToken;
//get credentials sent by the client
var token = AccessToken.find({
where:{
and: [{ userId: value.userId }, { id: value.id }]
}
}, function(err, tokenDetail){
if (err) throw err;
if(tokenDetail.length){
callback(null, true);
} else {
callback(null, false);
}
}); //find function..
} //authenticate function..
});
app.io.on('connection', function(socket){
console.log('a user connected');
socket.on('disconnect', function(){
console.log('user disconnected');
});
});
}
});
server/pubsub.js
'use strict';
var loopback = require('loopback');
//Writing pubsub module for socket.io
module.exports = {
//Publishing a event..
publish: function(socket, options ){
var ctx = loopback.getCurrentContext();
if(options){
var collectionName = options.collectionName;
var method = options.method;
var data = options.data;
var modelId = options.modelId;
if(method === 'POST'){
//console.log('Posting new data');
var name = '/' + collectionName + '/' + method;
socket.emit(name, data);
}
else{
var name = '/' + collectionName + '/' + modelId + '/' + method;
socket.emit(name, data);
}
}else{
throw 'Error: Option must be an object type';
}
}, //End Publish..
isEmpty:function (obj) {
var hasOwnProperty = Object.prototype.hasOwnProperty;
// null and undefined are "empty"
if (obj == null) return true;
// Assume if it has a length property with a non-zero value
// that that property is correct.
if (obj.length > 0) return false;
if (obj.length === 0) return true;
// Otherwise, does it have any properties of its own?
// Note that this doesn't handle
// toString and valueOf enumeration bugs in IE < 9
for (var key in obj) {
if (this.hasOwnProperty.call(obj, key)) return false;
}
return true;
} //isEmpty function..
}
common/models/task.js
var pubsub = require('../../server/pubsub.js');
var loopback = require('loopback');
module.exports = function(Task) {
//Task after save..
Task.observe('after save', function (ctx, next) {
console.log('Task after save');
var socket = Task.app.io;
if(ctx.isNewInstance){
//Now publishing the data..
pubsub.publish(socket, {
collectionName : 'Task',
data: ctx.instance,
method: 'POST'
});
}else{
//Now publishing the data..
pubsub.publish(socket, {
collectionName : 'Task',
data: ctx.instance,
modelId: ctx.instance.id,
method: 'PUT'
});
}
//Calling the next middleware..
next();
}); //after save..
//TaskDetail before delete..
Task.observe("before delete", function(ctx, next){
var socket = Task.app.io;
//Now publishing the data..
pubsub.publish(socket, {
collectionName : 'Task',
data: ctx.instance.id,
modelId: ctx.instance.id,
method: 'DELETE'
});
//move to next middleware..
next();
}); //before delete..
}; //Module exports..
I want deliver task, project, team changes via sockets. Some projects or tasks can be private. It means that only invited to project/task members can see them. Where can I put my logic witch determines who will receive notification? In general, all team members have to receive changes in tasks, projects and teams, but in private tasks and projects is another logic.
What is the best way to do it? Create namespace or room like team/team_id for common case and send individual notification in private case. Or is it better to create namespace or room for each connected user and on task change check who have to receive changes and send to them?
In my example, when I save a task all users receives this task via sockets...
Thanks.

Why rewire not inject mock for testing in nodejs?

I'm trying to inject a mock for testing using mocha. But it looks like the mock is not picked up and the test still uses the real data from server. I'm trying to get the data from foursquare.
Here's my code.
var foursquare = require('foursquarevenues'),
Promise = require('promise'),
_ = require('underscore');
var Foursquare = function(client_id, client_secret) {
this.the_4sqr_lib = foursquare(client_id, client_secret);
};
Foursquare.prototype.getVenue = function(id) {
var self = this;
return new Promise(function(resolve, reject) {
self.the_4sqr_lib.getVenue({'venue_id' : id}, function(error, response) {
if(error) {
reject(error);
}
var venueData = response.response.venue;
var firstPhoto = venueData.photos.groups[0].items[0];
var theVenue = {
id: venueData.id,
name: venueData.name,
photo: firstPhoto.prefix + firstPhoto.width + 'x' + firstPhoto.height + firstPhoto.suffix,
url: venueData.canonicalUrl
};
resolve(theVenue);
});
});
};
module.exports = Foursquare;
And here's my test
var rewire = require("rewire"),
Foursquare = rewire('../../lib/foursquare.js');
var client_id, client_secret, foursquare;
beforeEach(function() {
client_id = process.env.FOURSQUARE_CLIENT_ID;
client_secret = process.env.FOURSQUARE_CLIENT_SECRET;
foursquare = new Foursquare(client_id, client_secret);
});
it('should get venue without photo', function(done) {
var mockFoursquare = {
getVenue : function(id, cb) {
var response = {
response : {
response : {
venue : {
photos : {
count:0,
groups : []
}
}
}
}
}
cb(null, response);
}
};
Foursquare.__set__('foursquarevenues', mockFoursquare);
var venue = foursquare.getVenue('430d0a00f964a5203e271fe3');
venue.then(function(venue) {
venue.id.should.equal('');
venue.name.should.equal('');
venue.photo.should.equal('');
venue.url.should.equal('');
done();
}).catch(done);
});
I'm expecting the test to fail because of undefined but it's still getting the real data.
I had the same problem when using var self = this;. The methods called like self.someMethod() didn't get mocked.
I've partially solved it by assigning the mock without rewire:
MyModule = rewire('../lib/MyModule');
MyModule.__set__({"someMethodNotUsingSelf": function(){...}});
MyModule.someMethodThatUsesSelf = function() { //some mock code };
someValue.should.equal('something');
//...
Hope it helps!

simple social network using node.js and mongodb

I am trying to build simple social network and I am following this book(Building Node Applications with MongoDB and Backbone)(https://github.com/Swiftam/book-node-mongodb-backbone/tree/master/ch10). However, I just realized that the node.js version has been updated.
I tied to solve some the issue however I got problem in chat.js that states this is the error:
ch10/routes/chat.js:27
data.sessionStore.load(data.sessionID, function(err, session) {
TypeError: Cannot read property 'load' of undefined
module.exports = function(app, models) {
var io = require('socket.io');
var utils = require('connect').utils;
var cookie = require('cookie');
this.io = io;
//var Session = require('connect').middleware.session.Session;
var sio = io.listen(app.server);
sio.configure(function() {
// Utility methods to see if the account is online
app.isAccountOnline = function(accountId) {
var clients = sio.sockets.clients(accountId);
return (clients.length > 0);
};
sio.set('authorization', function(data, accept) {
var signedCookies = cookie.parse(data.headers.cookie);
// var cookies = utils.parseSignedCookies(signedCookies, app.sessionSecret);
// data.sessionID = cookies['express.sid'];
data.sessionStore = app.sessionStore;
data.sessionStore.load(data.sessionID, function(err, session) {
if (err || !session) {
accept("Error", false);
} else {
data.session = session;
accept(null, true);
}
});
});
sio.sockets.on('connection', function(socket) {
var session = socket.handshake.session;
var accountId = session.accountId;
var sAccount = null;
socket.join(accountId);
io.use(function (socket, next) { next(); });
// Immediately trigger the login event
// of this account
app.triggerEvent('event:' + accountId, {
from: accountId,
action: 'login'
});
var handleContactEvent = function(eventMessage) {
socket.emit('contactEvent', eventMessage);
};
var subscribeToAccount = function(accountId) {
var eventName = 'event:' + accountId;
app.addEventListener(eventName, handleContactEvent);
console.log('Subscribing to ' + eventName);
};
// Find the account contacts and subscribe
models.Account.findById(accountId, function subscribeToFriendFeed(account) {
var subscribedAccounts = {};
sAccount = account;
account.contacts.forEach(function(contact) {
if (!subscribedAccounts[contact.accountId]) {
subscribeToAccount(contact.accountId);
subscribedAccounts[contact.accountId] = true;
}
});
// Subscribed to my feed as well
if (!subscribedAccounts[accountId]) {
subscribeToAccount(accountId);
}
});
// Remove listeners if socket disconnects
socket.on('disconnect', function() {
sAccount.contacts.forEach(function(contact) {
var eventName = 'event:' + contact.accountId;
app.removeEventListener(eventName, handleContactEvent);
console.log('Unsubscribing from ' + eventName);
});
app.triggerEvent('event:' + accountId, {
from: accountId,
action: 'logout'
});
});
var cookieParser = require('cookie-parser')(SESSION_SECRET);
// ### Cookie parser
// Wrapper arround Express cookie parser, so we can use the same cookie parser for socket.io.
// Parse Cookie header and populate `socket.request.cookies` with an object keyed by the cookie names.
// Uses signed cookies by passing a secret string, which assigns `socket.request.secret` so it may be used by other middleware.
function cookieParserWrapper (socket, next) {
// request, response and callback
cookieParser(socket.request, {}, next);
}
// Handle incoming chats from client
socket.on('chatclient', function(data) {
sio.sockets.in(data.to).emit('chatserver', {
from: accountId,
text: data.text
});
});
});
});
}
Without testing the code myself or anything.
"TypeError: Cannot read property 'load' of undefined"
That particular error means that data.sessionStore is undefined and that "load" does not exists as a property, since there is literally nothing defined in data.sessionStore.
So the problem in my opinion is that your session system is not working properly. Hope that helps a bit!

Resources