Meteor: Passing value to database after successful Paypal payment - meteor

I'd like to update the users database after a successful payment. Basically, converting $ to site credits. I've used https://github.com/tirtohadi/meteor-paypal-demo/, basically using his code implementing paypal to the web app. The only idea I have is when the site gets routed to the return page after payment. Code's here.
Router.map(function() {
this.route('/payment_return/:invoice_no/:amount/', {
where: 'server',
onBeforeAction: function() {
console.log("result");
result = paypal_return(this.params.invoice_no,this.params.amount,this.params.query.token,this.params.query.PayerID);
console.log(result);
if (result)
{
this.response.end("Payment captured successfully");
}
else
{
this.response.end("Error in processing payment");
}
}
});
});
I guess my question is, how do I securely update the db after a successful payment. Because I know client side update is dangerous (from what I've read anyway)

Related

Google Calendar API watch channels not stopping

Stopping a watch channel is not working, though it's not responding with an error, even after allowing for propagation overnight.  I'm still receiving 5 notifications for one calendarlist change.  Sometimes 6.  Sometimes 3.  It's sporadic. We're also receiving a second round of notifications for the same action after 8 seconds.  Sometimes 6 seconds.  Sometimes a third set with a random count.  Also sporadic. Received a total of 10 unique messages for a single calendar created via web browser.
You can perform infinite amount of watch requests on specific calendar resource, Google will always return the same calendar resource Id for the same calendar, but the uuid you generate in the request will be different, and because of that, you will receive multiple notifications for each watch request that you've made. One way to stop all notifications from specific calendar resource, is to listen for notifications, pull out "x-goog-channel-id" and "x-goog-resource-id" from notification headers, and use them in Channels.stop request.
{
"id": string,
"resourceId": string
}
Every time you perform a watch request, you should persist the data from the response, and check if the uuid or resource id already exist, if yes don't perform watch request for that resource id again (if you don't want to receive multiple notifications).
e.g.
app.post("/calendar/listen", async function (req, res) {
var pushNotification = req.headers;
res.writeHead(200, {
'Content-Type': 'text/html'
});
res.end("Post recieved");
var userData = await dynamoDB.getSignInData(pushNotification["x-goog-channel-token"]).catch(function (err) {
console.log("Promise rejected: " + err);
});
if (!userData) {
console.log("User data not found in the database");
} else {
if (!userData.calendar) {
console.log("Calendar token not found in the user data object, can't perform Calendar API calls");
} else {
oauth2client.credentials = userData.calendar;
await calendarManager.stopWatching(oauth2client, pushNotification["x-goog-channel-id"], pushNotification["x-goog-resource-id"])
}
}
};
calendarManager.js
module.exports.stopWatching = function (oauth2client, channelId, resourceId) {
return new Promise(function (resolve, reject) {
calendar.channels.stop({
auth: oauth2client,
resource: {
id: channelId,
resourceId: resourceId
}
}, async function (err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return reject(err);
} else {
console.log("Stopped watching channel " + channelId);
await dynamoDB.deleteWatchData(channelId)
resolve(response);
}
})
})
}
Not a google expert but I recently implement it in my application,
I am trying to answer some of your questions for future readers:
It's sporadic
Tha's because you have create more than 1 channels for watching events.
We're also receiving a second round of notifications for the same action after 8 seconds
Google doesn't say anything about the maximum delay for sending a push notification.
Suggestions:
CREATE:
When you create a new channel, always save the channel_id and channel_resource in your database.
DELETE:
When you want to delete a channel just use stop API endpoint with the channel data saved in your database
RENEW:
As you have noticed the channels do expire, so you need to update them once in a while. To do that create a crone in your server that is going to STOP all previous channels and it will create new one.
Comment: Whenever something is going wrong please read the error message sent from the Google API calendar. Most of the time, it tells you what is wrong.
Use Channels.stop which is mentioned in the docs. Supply the following data in your request body:
{
"id": string,
"resourceId": string
}
id is the channel ID when you created your watch request. Same goes with resource ID.
Read this SO thread and this github forum for additional reference.

Meteor - initiate client login automatically

i have a meteor app where i'm using nginx with an internal SSO service to authenticate. I'm able to do this successfully and retrieve user details in the nginx set http headers on the server Meteor.onConnection method.
At this point, i'm not sure what the best approach is to get access to the user details on the client side. I feel like i should use the built in Meteor Accounts but i'm not sure how to initiate the login process from the client since the user will not actually be logging in through the Meteor client but through a redirect that happens through nginx. I feel like i need a way to automatically initiate the login process on the meteor side to set up the meteor.users collection appropriately, but i can't figure out a way to do that.
Checkout the answers here. You can pass the userId (or in whatever you want to pass the user) through nginx to the server then onto the client to login. You can generate and insert the token in a Webapp.connectHandler.
import { Inject } from 'meteor/meteorhacks:inject-initial';
// server/main.js
Meteor.startup(() => {
WebApp.connectHandlers.use("/login",function(req, res, next) {
Fiber(function() {
var userId = req.headers["user-id"]
if (userId){
var stampedLoginToken = Accounts._generateStampedLoginToken();
//check if user exists
Accounts._insertLoginToken(userId, stampedLoginToken);
Inject.obj('auth', {
'loginToken':stampedLoginToken
},res);
return next()
}
}).run()
})
}
Now you can login on the client side with the help of the meteor-inject-initial package
import { Inject } from 'meteor/meteorhacks:inject-initial';
// iron router
Router.route('/login', {
action: function() {
if (!Meteor.userId()){
Meteor.loginWithToken(Inject.getObj('auth').loginToken.token,
function(err,res){
if (err){
console.log(err)
}
}
)
} else {
Router.go('/home')
}
},
});

Meteor - template not updated after user sign in

I built an app with simple user login, using Accounts-unstyled. It all works fine, except that the template showing user data is not re-rendered with the user logs in. However if the user reloads the page, then his data is present.
My code doesn't really contain anything related to user, except for fetching the correct user's data. Do I need to manually trigger a refresh after the login?
The app is at www.barelinks.in. Test login: user=test; password=pwdpwd.
Upon first login nothing changes, but if you reload you will see the user's data.
Here is the code for the subscription:
if(Meteor.isServer) {
Meteor.publish('links', function linksPublication(userId) {
return Links.find({ user: userId });
});
}
And the helper fetching the right data (the Regex is for the search feature):
Template.body.helpers({
links: function() {
var nbSkip = linksPerPage*(Template.instance().page.get() - 1);
var queryRegex = new RegExp(Template.instance().query.get(), 'i');
return Links.find({ user: Meteor.userId(), title: queryRegex }, { sort: { createdAt: -1 }, limit: linksPerPage, skip: nbSkip });
},
});
Thanks!
I recognized that the content is updated when refreshing the page in a logged in state. Than you can switch forth and back and everything works as expected. I came usually across this behavior when the data subscription is not as it should be.
The server automatically updates the data according to a change of logged in state, you need to rely on the this.user property in the publish function.
if(Meteor.isServer) {
Meteor.publish('links', function linksPublication() {
return Links.find({ user: this.userId });
});
}

How to make sure user in app's session and google's session are the same when using accounts-google

I am using accounts-google on my app and I'd like to solve rather odd authentication scenario.
A logs in so now as an app session and a google session
A switches to gmail and logs out there.
Now, mind you that, A is actually still logged in on the meteor app.
B comes along, logs in to Gmail using his account.
Switches to the meteor app to see that he's logged in, but oddly, logged in with A's account.
This scenario leads to lots of confusions and people unknowingly using other users' accounts where they share computers.
So, basically, I need to users in the meteor session and google session to be the same, and if not, ensure that the current meteor session is invalidated and loginWithGoogle() is called again.
How can I solve this?
It seems impossible with Meteor's current accounts package, although one could create a new one using Google's latest googleplus api.
But there seems to exist a workaround by:
1) Set up onBeforeAction hooks on your router to login the user automatically (which asks for credentials if user is not logged in to external service)
var loginWithGoogle = function() {
if (Meteor.isClient) {
Session.set('loginError', undefined);
Meteor.loginWithGoogle({
loginStyle : "redirect",
requestPermissions : ['profile', 'email'],
requestOfflineToken: true
}, function (err) {
if (err)
Session.set('loginError', 'reason: ' + err.reason + ' message: ' + err.message || 'Unknown error');
});
}
}
var requireLogin = function() {
if (! Meteor.user()) {
if (Meteor.loggingIn()) {
this.render(this.loadingTemplate);
} else {
console.log('The app is automatically asking for you to log in.');
loginWithGoogle();
}
} else {
this.next();
}
}
Router.onBeforeAction(requireLogin, {except: ['some-special-public-route']});
2) Log the user out when they are navigating away from every page (caveat: login/logout gets called everytime the user navigates within the app)
Meteor.startup(function(){
$(window).bind('beforeunload', function() {
closingWindow();
});
});
closingWindow = function(){
console.log('The app is automatically logging you out because you are leaving.');
Meteor.logout();
}
3) improvement area: set a session variable to track user's navigation within the app and run the unload event depending on the variable.

How to validate data in onCreateUser without losing the form data?

I'm writing an Appliction using Meteor. In this App I want to implement a server-side validation of the user data using Accounts.onCreateUser. There is some data passed which can only be verified on the server side.
At client side I call:
Template.register.events({
'submit form': function (e) {
e.preventDefault();
var attributes = {
username: $("#inputUsername").val(),
password: $("#inputPassword").val(),
confirmation: $("inputConfirmation").val(),
email: $("#inputEmail").val(),
...
};
Accounts.createUser(attributes, function(err){
if (err) {
throwError(err);
} else {
}
});
}
});
And on the server side:
Accounts.onCreateUser(function(options, user) {
if(!verifyData(options))
throw new Meteor.Error(403, "Wrong input");
return user;
});
After the server side verification fails, all form data is lost. What is the best way to keep the data?
I went ahead and reproduced your code on a Meteorpad and from what I can tell, the form data does still persist. You just need to access it via the attributes variable in the client-side.
There may be something I am missing, but i took what you posted above and put it in there.

Resources