Basically I'm getting an "Invalid" return when I try to verify a payment made using the IPN Listeners from Paypal and NodeJS, I'm sending back a urlencoded post back to paypal, for them to tell me whether it is valid or not, however it always returns Invalid.
I have a node app, and I'm using expressJS to manage all these server calls.
I already tried 2 different approaches:
app.post('/ipn', bodyParser.text({ type: 'urlencoded' }), function (req, res) {
res.sendStatus(200);
console.log('accedio al ipn');
//console.log(req.body);
var params = req.body;
ipn.verify(string, {'allow_sandbox': true}, function callback(err, msg) {
if (err) {
console.log(string);
console.error(err);
} else {
// Do stuff with original params here
if (params.payment_status == 'Completed') {
// Payment has been confirmed as completed
}
}
});
});
and
app.post('/ipn', bodyParser.text({ type: 'urlencoded' }), function (req, res) {
console.log('Received POST /');
console.log(req.body);
console.log('\n\n');
// STEP 1: read POST data
req.body = req.body || {};
res.status(200).send('OK');
res.end();
// read the IPN message sent from PayPal and prepend 'cmd=_notify-validate'
var postreq = 'cmd=_notify-validate';
for (var key in req.body) {
if (req.body.hasOwnProperty(key)) {
var value = querystring.escape(req.body[key]);
postreq = postreq + "&" + key + "=" + value;
}
}
// Step 2: POST IPN data back to PayPal to validate
console.log('Posting back to paypal');
console.log(postreq);
console.log('\n\n');
var options = {
url: 'https://www.sandbox.paypal.com/cgi-bin/webscr',
method: 'POST',
headers: {
'Connection': 'close'
},
body: postreq,
strictSSL: true,
rejectUnauthorized: false,
requestCert: true,
agent: false
};
request(options, function callback(error, response, body) {
if (!error && response.statusCode === 200) {
// inspect IPN validation result and act accordingly
console.log(body);
if (body.substring(0, 8) === 'VERIFIED') {
// The IPN is verified, process it
console.log('Verified IPN!');
console.log('\n\n');
// assign posted variables to local variables
var item_name = req.body['item_name'];
var item_number = req.body['item_number'];
var payment_status = req.body['payment_status'];
var payment_amount = req.body['mc_gross'];
var payment_currency = req.body['mc_currency'];
var txn_id = req.body['txn_id'];
var receiver_email = req.body['receiver_email'];
var payer_email = req.body['payer_email'];
//Lets check a variable
console.log("Checking variable");
console.log("payment_status:", payment_status)
console.log('\n\n');
// IPN message values depend upon the type of notification sent.
// To loop through the &_POST array and print the NV pairs to the screen:
console.log('Printing all key-value pairs...')
for (var key in req.body) {
if (req.body.hasOwnProperty(key)) {
var value = req.body[key];
console.log(key + "=" + value);
}
}
} else if (body.substring(0, 7) === 'INVALID') {
// IPN invalid, log for manual investigation
console.log('Invalid IPN!');
console.log('\n\n');
}
}
});
});
Both of them return Invalid.
First example is taken from:
https://github.com/andzdroid/paypal-ipn
Second one is taken from:
https://github.com/HenryGau/node-paypal-ipn
As far as I know, I receive a payment notification using IPN on my server, then I resend that request to Paypal so I can verify it it's valid or not, for security purposes, this is all done within the sandbox environment.
I also tried to send the request I get, but this time using POSTMAN, and it also returns Invalid, been stuck here for a while.
Note: I'm trying to use this for suscription payments, I don't know if this is relevant or not, just in case.
Related
I make a lowdb request to update a JSON file, but only the date is updated. When I make a console.log of the information to update, this is what I want to update:
res.on("end", function () {
let body = Buffer.concat(chunks);
let final = JSON.parse(body);
if (final.error !== undefined) {
console.log("Initial authentication:", final.error_description, "Please refresh the authentication grant");
extAuthCallback(84);
} else {
tokens.set('access_token', final.access_token)
.set('expires_in', final.expires_in)
.set('refresh_token', final.refresh_token)
.set('refresh_date', moment())
.write()
console.log(final, tokens.value())
extAuthCallback(1);
}
});
console.log of my final variable:
{
access_token: 'oa_prod_iq00cRPk5Jhh4VffSHlDj7DEDsSIlpCRRczI3l3ASC0',
token_type: 'bearer',
expires_in: 2399,
refresh_token: 'oa_prod_nIjBZs74xGvJXi1B-wdMyITfxGyklpCRRczI3l3ASC0'
}
console.log of my JSON file after the request:
{
access_token: 'oa_prod_pB9Q0FFM9Tk4c5n3HMRBFKAVz6naiJ-jmb3QCeBrT00',
expires_in: 2399,
refresh_token: 'oa_prod_nX3EDs530SM8eHv_fM5BN7-5RLBwkrKoUi6uExBbTY4',
refresh_date: '2020-11-28T23:31:13.855Z',
primary_autorization_date: '2020-11-29T00:40:58.421Z'
}
My JSON file after the modifications:
{
"access_token": "oa_prod_pB9Q0FFM9Tk4c5n3HMRBFKAVz6naiJ-jmb3QCeBrT00",
"expires_in": 2399,
"refresh_token": "oa_prod_nX3EDs530SM8eHv_fM5BN7-5RLBwkrKoUi6uExBbTY4",
"refresh_date": "2020-11-28T23:31:13.855Z",
"primary_autorization_date": "2020-11-29T00:40:58.421Z"
}
So it only has the primary_autorization_date field changing...
You should use set instead of update.
tokens.set('access_token', final.access_token)
.set('expires_in', final.expires_in)
.set('refresh_token', final.refresh_token)
.set('refresh_date', moment())
.write()
The update method is accepted a function like this.
db.update('test1', (n) => 5555)
.update('test2', (n) => n + 1)
.write()
If you use set, you just need to assign the value to it.
db.set('test1', 5555).set('test2', 3333).write()
And when you use moment, there are two ways to you could use.
// Way 1 with moment()
db.set('date', moment()).write()
// Way 2 with moment
db.update('date', moment).write()
So the solution is:
I call my function that contains the HTTP request in another file like this:
app.get('/', async function(req, res) {
const access_token = req.query.code;
if (access_token) {
let authentication = await asyncExtAuth(access_token);
if (authentication == 84)
return res.send({error: "Please give a valid access_token"});
res.sendFile(path.join(__dirname + '/success_page/index.html'));
db.set('access_token', access_token).write()
tokens.set('primary_autorization_date', moment()).write()
console.log("Access Token successfully refreshed")
}
else
res.send({error: "Please specify an access token"})
})
In which I modify a second time my file with the line tokens.set('primary_autorization_date', moment()).write(). By doing this, lowdb doesn't take into account the modification made just before and re-modifies my file with the information it contained before. The solution is to add the line tokens.read() just before modifying the file to update the cache:
app.get('/', async function(req, res) {
const access_token = req.query.code;
if (access_token) {
let authentication = await asyncExtAuth(access_token);
if (authentication == 84)
return res.send({error: "Please give a valid access_token"});
res.sendFile(path.join(__dirname + '/success_page/index.html'));
db.set('access_token', access_token).write()
tokens.read()
tokens.set('primary_autorization_date', moment()).write()
console.log("Access Token successfully refreshed")
}
else
res.send({error: "Please specify an access token"})
})
I'm currently trying to build a firebase cloud function (using express) where:
- I check if a use exists in database
- Sends a message to the telegram API depending on whether it exists or not
The issue is, when I try to run the function, Firebase logs is able to get the console.log telling me if the user exists, but will not send to telegram. The error log says:
[2020-02-15T10:41:34.568Z] #firebase/database: FIREBASE WARNING:
Exception was thrown by user callback. 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 (/srv/node_modules/express/lib/response.js:771:10)
at ServerResponse.send (/srv/node_modules/express/lib/response.js:170:12)
at ServerResponse.json (/srv/node_modules/express/lib/response.js:267:15)
at ServerResponse.send (/srv/node_modules/express/lib/response.js:158:21)
at firebase.database.ref.child.once.snapshot (/srv/index.js:59:40)
at onceCallback (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:4933:51)
at /srv/node_modules/#firebase/database/dist/index.node.cjs.js:4549:22
at exceptionGuard (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:698:9)
Could anyone please help? Thank you!
app.post("/", async (req, res) => {
const isTelegramMessage =
req.body &&
req.body.message &&
req.body.message.chat &&
req.body.message.chat.id &&
req.body.message.from &&
req.body.message.from.first_name &&
req.body.update_id;
const user_id = req.body.message.from.id
firebase.initializeApp(firebaseConfig);
let myUser;
const chat_id = req.body.message.chat.id;
const {
first_name
} = req.body.message.from;
// Check User Exists
firebase
.database()
.ref("/telegramUsers")
.child(req.body.message.from.id)
.once("value", snapshot => {
if (snapshot.exists()) {
myUser = true;
console.log("exists!", myUser);
return res.status(200).send({
method: "sendMessage",
chat_id,
text: `Welcome Back ${first_name}`
});
} else {
myUser = false;
console.log("does not exist!");
return res.status(200).send({
method: "sendMessage",
chat_id,
text: `Hello ${first_name}`
});
}
});
return res.status(200).send({
status: "not a telegram message"
});
});
As others have commented, you're returning and writing a response to the caller twice. Since send starts writing the body of the HTTP response, you can't call status (or even send) after you've already called it on res before.
In code that'd look something like this:
app.post("/", async (req, res) => {
const isTelegramMessage =
req.body &&
req.body.message &&
req.body.message.chat &&
req.body.message.chat.id &&
req.body.message.from &&
req.body.message.from.first_name &&
req.body.update_id;
const user_id = req.body.message.from.id
firebase.initializeApp(firebaseConfig);
let myUser;
const chat_id = req.body.message.chat.id;
const {
first_name
} = req.body.message.from;
// Check User Exists
if (isTelegramMessage) {
return firebase
.database()
.ref("/telegramUsers")
.child(req.body.message.from.id)
.once("value")
.then(snapshot => {
if (snapshot.exists()) {
myUser = true;
console.log("exists!", myUser);
return res.status(200).send({
method: "sendMessage",
chat_id,
text: `Welcome Back ${first_name}`
});
} else {
myUser = false;
console.log("does not exist!");
return res.status(200).send({
method: "sendMessage",
chat_id,
text: `Hello ${first_name}`
});
}
});
} else {
return res.status(200).send({
status: "not a telegram message"
});
}
});
The changes:
Now only checks if the user exists if isTelegramMessage is true.
Now returns the result from the database read operation.
Use once().then(), so that the return res.status().send()... bubbles up. This ensures that Cloud Functions will not terminate the function before both the database load and the sending of the response are done.
Though the second and third bullets are not strictly needed for HTTPS triggered Cloud Functions (as those terminate when you send a response), I still recommend to use them, to make it easier to port/copy-paste the code to Cloud Functions types that are triggered by other events.
I am releasing access to pages using connect-roles and loopback but I have a pertinent question about how I can collect the customer's role and through the connect-roles to read the session and respond to a route.
Example, when the client logs in I load a string containing the client's role and access it in a function that controls access to pages.
I have this doubt because I'm finalizing a large scale service that usually there are multiple client sessions that are accessed instantly using a same storage and check function.
It would be efficient to store the customer's role using app.set() and app.get()?
app.get('/session-details', function (req, res) {
var AccessToken = app.models.AccessToken;
AccessToken.findForRequest(req, {}, function (aux, accesstoken) {
// console.log(aux, accesstoken);
if (accesstoken == undefined) {
res.status(401);
res.send({
'Error': 'Unauthorized',
'Message': 'You need to be authenticated to access this endpoint'
});
} else {
var UserModel = app.models.user;
UserModel.findById(accesstoken.userId, function (err, user) {
// console.log(user);
res.status(200);
res.json(user);
// storage employee role
app.set('employeeRole', user.accessLevel);
});
}
});
});
Until that moment everything happens as desired I collect the string loaded with the role of the client and soon after I create a connect-roles function to validate all this.
var dsConfig = require('../datasources.json');
var path = require('path');
module.exports = function (app) {
var User = app.models.user;
var ConnectRoles = require('connect-roles');
const employeeFunction = 'Developer';
var user = new ConnectRoles({
failureHandler: function (req, res, action) {
// optional function to customise code that runs when
// user fails authorisation
var accept = req.headers.accept || '';
res.status(403);
if (~accept.indexOf('ejs')) {
res.send('Access Denied - You don\'t have permission to: ' + action);
} else {
res.render('access-denied', {action: action});
// here
console.log(app.get('employeeRole'));
}
}
});
user.use('authorize access private page', function (req) {
if (employeeFunction === 'Manager') {
return true;
}
});
app.get('/private/page', user.can('authorize access private page'), function (req, res) {
res.render('channel-new');
});
app.use(user.middleware());
};
Look especially at this moment, when I use the
console.log(app.get('employeeRole')); will not I have problems with simultaneous connections?
app.get('/private/page', user.can('authorize access private page'), function (req, res) {
res.render('channel-new');
});
Example client x and y connect at the same time and use the same function to store data about your session?
Being more specific when I print the string in the console.log(app.get('employeeRole')); if correct my doubt, that I have no problem with simultaneous connections I will load a new variable var employeeFunction = app.get('employeeRole'); so yes my function can use the object containing the role of my client in if (employeeFunction === 'Any Role') if the role that is loaded in the string contain the required role the route it frees the page otherwise it uses the callback of failureHandler.
My test environment is limited to this type of test so I hope you help me on this xD
Instead of using app.set you can create a session map(like hashmaps). I have integrated the same in one of my projects and it is working flawlessly. Below is the code for it and how you can access it:
hashmap.js
var hashmapSession = {};
exports.auth = auth = {
set : function(key, value){
hashmapSession[key] = value;
},
get : function(key){
return hashmapSession[key];
},
delete : function(key){
delete hashmapSession[key];
},
all : function(){
return hashmapSession;
}
};
app.js
var hashmap = require('./hashmap');
var testObj = { id : 1, name : "john doe" };
hashmap.auth.set('employeeRole', testObj);
hashmap.auth.get('employeeRole');
hashmap.auth.all();
hashmap.auth.delete('employeeRole');
In my node js application I have one userlogin api.Above that I created one varibale called customerid in my server side code.Now when user authentication is sucess.Iam storing his userid value in my customerid variable and I want to use that variable through out my application.
But the current issue Iam facing is ,when some one logins first time.It is working as expected userid is stored in customerid and doing related operations.But when another gets login, the first person customerid is overridden by send login customerid which should be the case .Every user should get only his relevant login userid as customerid..how can I make this work..Below is my code
var customerid;
app.post('/api/validate',function(req,res,callback){
customerid = '';
var datareq = req.body;
var data = {};
data.customerid = datareq.customerid;
request.post({
url:'https://databasedata.mybluemix.net/verifycredentials',
headers:{
'Content-Type':'application/json'
},
body:data,
json:true
},function(err,res,body){
verifycreds(body);
});
function verifycreds(data){
if(data.length == 0){
res.send("invalid username");
window.alert('Invalid user');
}
else if(data.length > 0){
if((datareq.customerid === data[0].customerid ) && (datareq.password ==
data[0].password)){
customerid = datareq.customerid;
res.send("valid");
}
else{
res.send("invalid");
}
}
}
});
Install cookie parser dependency and use it.
You can set it in res cookie once and use everywhere in other API.
Like this
var cookieParser = require("cookie-parser");
app.use(cookieParser());
app.post('/api/validate', function(req, res, callback) {
var datareq = req.body;
var data = {};
data.customerid = datareq.customerid;
request.post({
url: 'https://databasedata.mybluemix.net/verifycredentials',
headers: {
'Content-Type': 'application/json'
},
body: data,
json: true
}, function(err, response, body) {
verifycreds(body, response);
});
function verifycreds(data) {
if (data.length == 0) {
res.send("invalid username");
window.alert('Invalid user');
} else if (data.length > 0) {
if ((datareq.customerid === data[0].customerid) && (datareq.password == data[0].pa
ssword)) {
res.cookie('customerid', datareq.customerid);
res.send("valid");
} else {
res.send("invalid");
}
}
}
});
app.get('/api/home', function(req, res, callback) {
var customerid = req.cookies.customerid;
// get customerid as like this in every call
});
In other API take that customer id as req.cookies.customerid from req.
i have a problem regarding cross-file calls and their lifecycles. I want to query the dynamodb for a username to login the entered user.I query in an external file to minimize queries as i need to use the data in another file too. The cycle seems to be off though and I really don't know why. The query call comes after the POST / login although the input in the form is available earlier. It would print the queryparams before the POST/login call too if i'd call a console log on it. The callback does not wait for the actual data. I have browsed other posts containing information regarding asynchronous callback functions but couldn't figure out why the callback completely ignores the query function. The problem is not the communication between browser and server but rather between files/classes in the node script. The data is available for query before the post statement but the query gets executed after. This is What can i do to prevent that?
the console output (for debugging purposes) is:
GET /stylesheets/style.css 304 3ms
callback
undefined
POST /login 500 38ms - 280b
querying ...
information found
[queryResponse Object]
the query file:
const AWS = require("aws-sdk");
var exports = module.exports = {};
const dynamodb = new AWS.DynamoDB({
apiVersion: "2012-08-10",
// accessKeyId: "", //TODO
// secretAccessKey: "", //TODO
// region: "eu-central-1" //? TODO
//testing purposes
"region": "us-west-2",
"accessKeyId": "abcde",
"secretAccessKey": "abcde",
"endpoint": "http://localhost:8000"
});
var dataAfterQuery = null;
exports.query = function(queryParams,callback) {
/*prevent unneccessary queries*/
var queryNow = dynamodb.query(queryParams,
function(err,data) {
if(err) {
console.error(err);
return done(err);
}
console.log('querying ...');
if(data.Count > 0) {
console.log('information found');
} else {
console.log('"' + JSON.stringify(queryParams) + '" is not in the db yet');
}
dataAfterQuery = data;
console.log(JSON.stringify(dataAfterQuery));
return dataAfterQuery;
});
if(typeof callback == 'function') {
console.log("callback");
callback();
return queryNow;
}
}
/*function to recieve queried data*/
exports.getQueriedData = function() {
return dataAfterQuery;
}
the login file:
module.exports = function(passport) {
passport.use("login", new LocalStrategy({
passReqToCallback: true
},
function(req,username,password,done) {
var queryParams = {
TableName: "users",
KeyConditionExpression: "username = :user",
ExpressionAttributeValues: {
//username entered in jade form
":user":{"S":username}
}
};
queryFile.query(queryParams,function(err,data){
if(err) console.log(data);
//console.log(data);
//kommt vor information found?
console.error(data);
/* response can only be null or not null*/
if(data != null) {
console.error('error, more than one user with username: "' + username + '" in the db');
console.error("Entries :" + data.Count);
return done(null,false,req.flash("message", "more than version of the username in the db"));
} else {
//only one user exists in db, query for username was successful
var parsedQueryPw = data.Items[0].password.S;
userAfterQuery = data.Items[0];
//checking if entered password is wrong
if(!isValidPassword(parsedQueryPw, password)) {
console.error("invalid password");
return done(null,false,req.flash("message","invalid user-password combination"));
}
//successful login - user and password match
console.log("login successful");
//return user object for serialization
return done(null,data);
}