How to integrate soap xml API of unicommerce in nodejs - node.js

I am trying to hit xml Api of unicommerce by soap module in nodejs. It is working fine in soapUI.But is not integrated in node Application.
I am getting this error.
ERROR: {Error: Cannot parse response}.
var express = require("express");
var soap = require("soap");
var http = require('http');
var app = express();
var router = express.Router();
var username = "*********";
var password = "************";
var auth = "Basic " + new Buffer(username + ":" + password).toString("base64");// not working
var wsdlUrl = 'https://lensclues.unicommerce.com/services/soap/uniware16.wsdl?facility=01';
const Autentication = '<soapenv:Header>' +
'<wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">' +
'<wsse:UsernameToken wsu:Id="UsernameToken-D6BE484999DA5E97D4148483888689316">' +
'<wsse:Username>**********</wsse:Username>' +
'<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">*******************</wsse:Password>'+
'<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">$$$$$$$$$$$</wsse:Nonce>'+
'<wsu:Created>2017-01-19T15:14:46.893Z</wsu:Created>'+
'</wsse:UsernameToken>'+
'</wsse:Security>' +
'</soapenv:Header>'
soap.createClient(wsdlUrl, function (err, soapClient) {
soapClient.addSoapHeader(Autentication);
const data = '<soapenv:Envelope xmlns:ser="http://uniware.unicommerce.com /services/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">' + '<soapenv:Body>' +
'<ser:GetInventorySnapshotRequest>' +
'<ser:ItemTypes>' +
'<ser:ItemType>' +
'<ser:ItemSKU>LCSGDB555xD15165S4PURx124</ser:ItemSKU>' +
'</ser:ItemType>' +
'</ser:ItemTypes>' +
'<ser:UpdatedSinceInMinutes>600</ser:UpdatedSinceInMinutes>' +
'</ser:GetInventorySnapshotRequest>' +
'</soapenv:Body>' +
'</soapenv:Envelope>'
if (err) {
console.log("err", err);
}
soapClient.GetInventorySnapshot({
data
}, function (err, result) {
if (err) {
console.log("ERROR:", err);
}
else {
console.log("result", result);
}
});
The question is: How do I send the request and print the answer?
Could you have any clues about this kind of issue?
Thanks a lot!

Try this send data xml format
xw = new XMLWriter;
xw.startDocument();
xw.startElement('soapenv:Envelope');
xw.writeAttribute('xmlns:ser', 'http://uniware.unicommerce.com/services/');
xw.writeAttribute('xmlns:soapenv', 'http://schemas.xmlsoap.org/soap/envelope/');
xw.startElement('soapenv:Header');
xw.startElement('wsse:Security');
xw.writeAttribute('soapenv:mustUnderstand', '1');
xw.writeAttribute('xmlns:wsse', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd');
xw.startElement('wsse:UsernameToken');
xw.startElement('wsse:Username');
xw.text('******');
xw.endElement('wsse:Username');
xw.startElement('wsse:Password');
xw.text('*******password');
xw.endElement('wsse:Password');
xw.endElement('wsse:UsernameToken');
xw.endElement('wsse:Security');
xw.endElement('soapenv:Header');
xw.startElement('soapenv:Body');
xw.startElement('ser:GetInventorySnapshotRequest');
xw.startElement('ser:ItemTypes');
xw.startElement('ser:ItemType');
xw.startElement('ser:ItemSKU');
xw.text(sku);
xw.endDocument();
/*************************Request Module******************/
request(
{
'method': "POST",
'url': 'https://lensclues.unicommerce.com/services/soap/uniware16.wsdl?',
'body': xw.toString(),
'Content-Type': 'text/xml'
},
function (error, response, body) {
if (!error && response.statusCode == 200) {
parseString(body, function (err, result) {
let tt = result['SOAP-ENV:Envelope']['SOAP-ENV:Body'][0]['GetInventorySnapshotResponse'][0]['Successful'][0]
console.log("Error---------->", tt);
console.log("Result---------->", result);
if (tt == "false") {
var check = result['SOAP-ENV:Envelope']['SOAP-ENV:Body'][0]['GetInventorySnapshotResponse'][0]['Errors'][0]['Error'][0];
console.log("check", check);
mainCallback(check, err);
}
else {
let inventoryValue = result['SOAP-ENV:Envelope']['SOAP-ENV:Body'][0]['GetInventorySnapshotResponse'][0]['InventorySnapshots'][0]["InventorySnapshot"][0]['Inventory'][0];
console.log("inventoryValue", inventoryValue);
if (inventoryValue >= prodQty) {
mainCallback(null, inventoryValue);
}
else {
mainCallback("Inventory not be available", err);
}
}
});
}
else {
console.log("------------->", error)
mainCallback("error in to fetch inventory data", error);
}
}
);

Related

Video Upload using formidable in Nodejs, Error found: post 404 / 502

I'm uploading video file from local to server and then I'll be uploading it to cdn,
the issue i'm facing is my code is running well on local but its not working when i patch it to server.
Here is my code
commonJs
$("#uploadVideo").click(function (e) {
var reader = new FileReader();
var fileInput = document.getElementById('Videofile');
var previewUrl = window.URL.createObjectURL(fileInput.files[0]);
$(".video").attr("src", previewUrl);
var videotype = "video/mp4";
var file_data = $("#Videofile").prop("files")[0];
if (!file_data.type.match(videotype)) {
return "alert('Please upload mp4 files')"
} else {
var metadata = {
'content-type': 'video/mp4',
'size': file_data.size,
'uploaded': new Date(),
}
reader.onload = function (e) {
$("file_data").text("File Content: " + reader.result); // Show the file content
}
reader.readAsBinaryString(file_data);
file_data.onloadedmetadata = function () {
alert("Meta data for audio loaded");
};
};
var form_data = new FormData();
form_data.append("file", file_data)
form_data.append("metdata", metadata)
for (var key of form_data.entries()) {
console.log(key[0] + ', ' + key[1]);
}
if (form_data != undefined) {
$.ajax({
type: "post",
contentType: false,
processData: false,
url: "/api/recordvideo",
data: form_data,
dataType: 'json',
success: function (result) {
if (result) {
$(".video").attr("src", result.videolink);
alert("Successfully Uploaded Video");
console.log("Successfully Uploaded Video");
} else {
alert("Error on Uploading Video");
console.log("Error on Uploading Video");
}
},
error: function (err) {
console.log("error");
}
});
}
e.preventDefault();
e.stopPropagation();
});
ServerSide
app.post('/api/recordvideo',Api.recordvideo);
var Upload = require('gcs-resumable-upload');
ApiService.recordvideo = function (req, res) {
var db = req.db;
console.log("came in cloudupload");
var form = new formidable.IncomingForm();
var filesdata;
form.keepExtensions = true;
form.multiples = false;
form.on('fileBegin', function (name, file){
file.path = 'public/demo/' + file.name;
console.log("fileBegin: " + JSON.stringify(file));
});
form.on('file', function (name, file){
console.log('Uploaded ' + JSON.stringify(file));
var path = file.path;
console.log("came in cloud3 :" + JSON.stringify(path));
});
form.parse(req, function (err, fields, files) {
console.log("came in cloud0" + JSON.stringify(files));
filesdata = files;
});
console.log("came in cloud2");
form.on('end', function (fields, files) {
var userid = appconfig.ObjectID(appconfig.decrypt(req.signedCookies['gid']));
var path = this.openedFiles[0].path;
console.log("came in cloud3 :" + JSON.stringify(path));
fs.createReadStream(path)
.pipe(Upload.upload({ bucket: '******', file: path, metadata: { contentType: this.openedFiles[0].type } }))
.on('finish', function (response) {
console.log("Successfully Uploaded Video :" + JSON.stringify(response));
res.send({ "status": false, "videolink": "https://****/****/" + filesdata.file.name });
});
});
//res.send({ "status": false, "err": null });
}
At start atleast it was uploading to server folder & then in chrome developers tool it used to give response: {readystate : 4, . . . }
And now, I made some changes then it doesnt even hit my api, After few seconds it gives error in chrome developer tools 404() / 502 ()
Well, I got the solution, Previously I was using gcs-resumable-upload module to upload, but now I tried with '#google-cloud/storage' module through which I was able to upload upto 9mb.
const Storage = require('#google-cloud/storage');
var db = req.db;
console.log("came in cloudupload");
var form = new formidable.IncomingForm();
var filesdata;
form.keepExtensions = true;
form.multiples = false;
form.parse(req, function (err, fields, files) {
filesdata = files;
});
form.on('end', function (fields, files) {
var userid = appconfig.ObjectID(appconfig.decrypt(req.signedCookies['gid']));
var path = this.openedFiles[0].path;
const storage = new Storage({
keyFilename: 'gcloudcred.json'
});
const myBucket = storage.bucket('onfvideo');
myBucket.upload(path).then((resp) => {
console.log('uploaded to' + resp);
res.send({ "status": true, "err": null });
}).catch(err => {
console.error('ERROR:', err);
res.send({ "status": false, "err": null });
});
});
};
The Limitation of 9mb I was facing due to .netframework data-transfer limit which i was able to resolve using
<system.web>
<customErrors mode="Off"/>
<httpRuntime targetFramework="4.5" maxRequestLength="7483648" />
</system.web>
Method 2: Using xhr calling RestApi
1. Generated Access token using google-auto-auth module
2. XMLHttpRequest
var fileInput = $("#Videofile").prop("files")[0];
var url = "https://www.googleapis.com/upload/storage/v1/b/bucketname/o?uploadType=media&name=" + fileInput.name;
var http = new XMLHttpRequest();
http.open('POST', url, true);
http.setRequestHeader('Content-type', 'video/mp4');
http.setRequestHeader("Authorization", "Bearer " + token);
http.send(fileInput);
http.onprogress = function (ev) {
if (ev.lengthComputable) {
var percentage = Math.round((ev.loaded / ev.total) * 100);
console.log("percent " + percentage + '%');
}else {
console.log("Unable to compute progress information since the total size is unknown");
}
}
http.onloadstart = function (ev) {console.log("start")}
http.onloadend = function (ev) {}
http.onreadystatechange = function () {
if (http.readyState == 4 && http.status == 200) {
var response = JSON.parse(http.responseText);
alert("Successfully Uploaded Video");
}
}

NodeJS API: having trouble passing 2 parameters to request response

I am having trouble passing 2 ids via request response function. I can pass 1 id/argument without any issue but having trouble passing 2 arguments below is a snippet of my code:
server:
var http = require("http");
var org = require("../controllers/org");
var school = require("../controllers/school");
var academicSession = require("../controllers/academicSession");
var term = require("../controllers/term");
var gradingperiod = require("../controllers/gradingperiod");
var course = require("../controllers/course");
var stu = require("../controllers/student");
var cls = require("../controllers/class");
var user = require("../controllers/user");
var demo = require("../controllers/demographic");
var enroll = require("../controllers/enrollment");
var teach = require("../controllers/teacher");
var crscls = require("../controllers/schoolcs");
var enr = require("../controllers/enrollqrys");
var settings = require("../settings");
var httpMsgs = require("../core/httpMsgs");
http.createServer(function (req, resp) {
switch (req.method) {
case "GET":
if (req.url === "/") {
httpMsgs.showHome(req, resp);
}
else if (req.url === "/orgs") {
org.getOrgs(req, resp);
}
else if (req.url.match("/orgs/[0-9]+$")) {
var idorg = "[0-9]+";
var patt = new RegExp("/orgs/" + idorg);
if (patt.test(req.url)) {
patt = new RegExp(idorg);
var id = patt.exec(req.url);
org.getOrg(req, resp, id);
}
else {
httpMsgs.show404(req, resp);
}
}
***else if (req.url.match("/schools/[0-9]+[A-Za-z0-9\-\_]+/classes/[0-9]+[A-Za-z0-9\-\_]+/enrollments$")) {
var idcl = "[0-9]+[A-Za-z0-9\-\_]+";
var idsc = "[0-9]+[A-Za-z0-9\-\_]+";
var str = idcl + "[0-9]+[A-Za-z0-9\-\_]+" + idsc;
var patt = new RegExp("/schools/" + idcl + "/classes/" + idsc + "/enrollments");
if (patt.test(req.url)) {
patt = new RegExp(str);
var id = patt.exec(req.url);
enr.getEnrollmentsForClassInSchools(req, resp, id, arg2);
}
else {
httpMsgs.show404(req, resp);
}
}***
enrollqrys.js:
var db = require("../core/db");
var httpMsgs = require("../core/httpMsgs");
exports.getEnrollmentsForClassInSchools = function (req, resp, id, arg2) {
db.executeSql("EXEC dbo.getEnrollmentsForClassInSchools #clid = '" + id + "'" + ", #scid = '" + arg2 + "'", function (data, err) {
if (err) {
httpMsgs.show500(req, resp, err);
}
else {
//resp.writeHead(200, { "Content-Type": "application/json" });
//resp.write(JSON.stringify(data));
//resp.end();
httpMsgs.sendJson(req, resp, data);
}
});
};
exports.getStudentsForClassInSchool = function (req, resp, id, id) {
db.executeSql("EXEC dbo.getEnrollmentsForClassInSchools #clid = '" + clid + "'" + ", #scid =" + scid, function (data, err) {
if (err) {
httpMsgs.show500(req, resp, err);
}
else {
//resp.writeHead(200, { "Content-Type": "application/json" });
//resp.write(JSON.stringify(data));
//resp.end();
httpMsgs.sendJson(req, resp, data);
}
});
};
I get a not referenced error when testing this GET. Any help would be greatly appreciated. I will add that I am new to both javascript and nodejs
Error below:
Started listening at: 9000
C:\Users\THOMMA02\source\repos\NodejsConsoleApp1\NodejsConsoleApp1\core\server.js:281
enr.getEnrollmentsForClassInSchools(req, resp, id, arg2);
^
ReferenceError: arg2 is not defined
at Server. (C:\Users\THOMMA02\source\repos\NodejsConsoleApp1\NodejsConsoleApp1\core\server.js:281:72)
at emitTwo (events.js:126:13)
at Server.emit (events.js:214:7)
at parserOnIncoming (_http_server.js:619:12)
at HTTPParser.parserOnHeadersComplete (_http_common.js:112:17)
Waiting for the debugger to disconnect...

Trouble with asynchronous functions in node.js

I'm very new to node, and I'm trying to pull a list of IDs from an API, iterate through that list for each ID saving the output, and ultimately rename each file generated. The code below is the closest I've come, and while it works sometimes, it frequently fails as I believe one function isn't waiting for the other to complete (e.g. tries to read before a write), but I'm sure I have other issues going on.
const apiKey = inputData.apiKey
var https = require('https');
var sync = require('sync');
var fs = require('fs');
var JSONfileloc = "./pdfs/file.json"
var queryurl = 'https://intakeq.com/api/v1/intakes/summary?startDate=2018-01-01'
var authHeaders = { 'X-Auth-Key': apiKey }
var queryOpts = { method: 'GET', headers: authHeaders}
function handleFile (error, file)
{
if (error) return console.error('Ran into a problem here', error)
}
fetch(queryurl, queryOpts)
.then
(function findAPI(res, err)
{
if( err )
{ console.log('I cant find the API '+err) }
return res.json()
{console.log('found the API!')}
}
)
.then (function itID(res, err)
{
if( err )
{ console.log('I cant iterate the API '+err) }
for(var i = 0; i < res.length; i++)
{
var intakeID=res[i].Id;
var APIoptions={ host:"intakeq.com", path:"/api/v1/intakes/"+ intakeID, headers: authHeaders };
var PDFoptions={ host:"intakeq.com", path:"/api/v1/intakes/"+ intakeID+'/pdf', headers: authHeaders };
console.log('Working on ID:'+intakeID)
var JSONrequest = https.get(APIoptions, writeJSON)
}})
//READ JSON FUNCTION
function readJSON (err, data)
{
if (err) throw err;
if(data.indexOf('New Patient Forms') >= 0)
var contents = fs.readFileSync(JSONfileloc, handleFile);
var jsonContent = JSON.parse(contents)
//pull PT Name
pName = (jsonContent.ClientName);
console.log('The Patient Name Is ' + jsonContent.ClientName)
//pull PT DOB
pDob = (jsonContent.Questions[3].Answer)
console.log('Patient DOB Is ' + jsonContent.Questions[3].Answer)
//pull Form Type
pForm = (jsonContent.QuestionnaireName)
console.log('The Form Submitted is ' + jsonContent.QuestionnaireName)
//rename and move JSON
fs.rename("./pdfs/file.json", './JSONLogs/'+pName+' '+pForm+' '+Date.now()+'.json', function(err) {
if ( err ) console.log('Problem renaming! ' + err)
else console.log('Copying & Renaming JSON File!');
})
};
//WRITE JSON FUNCTION
function writeJSON(response, err)
{
var JSONfile = fs.createWriteStream(JSONfileloc, handleFile);
if (err) throw err;
response.pipe(JSONfile);
console.log('JSON Created')
fs.readFile(JSONfileloc, readJSON)
}
The research I've done leads me to believe that async.forEach is probably the right approach here, but I've been having a hard time getting that to work properly. Thanks in advance and any suggestions are much appreciated.
const apiKey = inputData.apiKey
var https = require('https');
var sync = require('sync');
var fs = require('fs');
var JSONfileloc = "./pdfs/file.json"
var queryurl = 'https://intakeq.com/api/v1/intakes/summary?startDate=2018-01-01'
var authHeaders = {
'X-Auth-Key': apiKey
}
var queryOpts = {
method: 'GET',
headers: authHeaders
}
function handleFile(error, file) {
if (error) return console.error('Ran into a problem here', error)
}
fetch(queryurl, queryOpts)
.then(function findAPI(res) {
return res.json();
})
.then(function itID(res) {
const JSONRequests = [];
for (var i = 0; i < res.length; i++) {
var intakeID = res[i].Id;
var APIoptions = {
host: "intakeq.com",
path: "/api/v1/intakes/" + intakeID,
headers: authHeaders
};
var PDFoptions = {
host: "intakeq.com",
path: "/api/v1/intakes/" + intakeID + '/pdf',
headers: authHeaders
};
// https.get has response as a stream and not a promise
// This `httpsGet` function converts it to a promise
JSONRequests.push(httpsGet(APIoptions, i));
}
return Promise.all(JSONRequests);
})
function httpsGet(options, filename) {
return new Promise((resolve, reject) => {
https.get(options, (response) => {
// The WriteJSON function, just for brewity
// Otherwise pass resolve to the seperate writeJSON and call it in there
var JSONfile = fs.createWriteStream(filename + ".json");
response.pipe(JSONfile);
JSONfile.on('close', () => {
readJSON(filename + ".json").then(() => {
resolve();
})
})
})
})
}
//READ JSON FUNCTION
function readJSON(filename) {
// if (err) throw err;
var contents = fs.readFileSync(filename, 'utf-8'); // removed handleFile as readFileSync does not allow callbacks, added format
var jsonContent = JSON.parse(contents)
// Make your conditional checks here with the jsonContents
//pull PT Name
pName = (jsonContent.ClientName);
console.log('The Patient Name Is ' + jsonContent.ClientName)
//pull PT DOB
pDob = (jsonContent.Questions[3].Answer)
console.log('Patient DOB Is ' + jsonContent.Questions[3].Answer)
//pull Form Type
pForm = (jsonContent.QuestionnaireName)
console.log('The Form Submitted is ' + jsonContent.QuestionnaireName)
//rename and move JSON
return new Promise((resolve, reject) => {
fs.rename("./pdfs/file.json", './JSONLogs/' + pName + ' ' + pForm + ' ' + Date.now() + '.json', function (err) {
if (err) {
console.log('Problem renaming! ' + err);
reject(err);
} else {
console.log('Copying & Renaming JSON File!');
resolve();
}
})
})
};
Updated to convert https.get response stream to return a Promise which can be handled much better.

NodeJs async waterfall (callback method is not a function)

I am having a problem using async waterfall where I find that after calling the second callback (cbNumPages), the first parameter "pages" is the actual callback for the next function, instead of the last parameter "cbGetFiles" which it should be (as far as I know async waterfall says that last parameter should always be the callback, well in this case is apparently not).
The code is the following:
async.waterfall
([
function(cbNumPages)
{
request({
url: 'any-url',
qs: {},
method: 'GET',
headers: {
'Authorization' : 'any-auth'
}
}, (err, response, body) => {
if (!err && response.statusCode == 200)
{
var $ = cheerio.load(body);
var pagesList = $('ol.aui-nav').children();
if(pagesList.length<1)
{
var numPages = 1;
} else {
var numPages = pagesList.length-2;
}
console.log(numPages);
var pages = new Array(numPages),
total = numPages*20,
iterator = 0;
async.eachSeries(pages, function(page, cb)
{
if(page>1)
{
pages[iterator] = iterator;
}else {
pages[iterator] = iterator*20;
}
iterator++;
cb();
}, function(err){
if(err) cbNumPages(err);
cbNumPages(null, pages);
});
} else {
cbNumPages(err);
}
})
},
function(pages, cbGetFiles)
{
var files = [];
var limitDate = moment().tz('Europe/Madrid').subtract(330,'days').format();
async.eachSeries(pages, function(page, cb)
{
request({
url: 'any-url'+page,
qs: {},
method: 'GET',
headers: {
'Authorization' : 'any-auth'
}
}, (err, response, body) => {
if(!err && response.statusCode == 200)
{
var $ = cheerio.load(body);
var rows = $('tr[id^=\'attachment-\']');
async.eachLimit(rows, 1, function(row, cb)
{
var id = row.attribs['id'];
var file = row.attribs['data-attachment-filename'];
var author = $(row).children('.creator').text().trim();
var created = $(row).children('.created-date').text().trim();
created = moment.tz(created, 'MMM D, YYYY', 'Europe/Madrid').format();
var urlFile = 'simple-file' + $(row).children('.filename-column').children('.filename').attr('href');
var extension = row.attribs['data-attachment-filename'].split('.');
extension = extension[extension.length-1];
if(created<limitDate && validExtensions.indexOf(extension)>-1)
{
var f = '{ "id": "' + id + '",';
f += ' "file": "' + file + '",';
f += ' "url": "' + urlFile + '",';
f += ' "author": "' + author + '",';
f += ' "modified": "' + created + '" }';
files.push(JSON.parse(f));
}
cb();
}, (err) => {
if(err) cbGetFiles(err);
});
cb();
} else {
cb(err);
}
});
}, function(err){
if(err){
cbGetFiles(err);
} else {
cbGetFiles(null, files);
}
});
},
function(files, cbGetAutors)
{
var filesFinal = {};
for(var f in files)
{
if(!filesFinal[files[f].author])
{
var ff = {};
for(var i in files)
{
if(files[i].author === files[f].author)
{
ff[files[i].file] = files[i].url;
}
}
filesFinal[files[f].author] = ff;
}
}
cbGetAutors(null, JSON.parse(JSON.stringify(filesFinal)));
},
function(filesFinal, cbSendEmail)
{
var authors = Object.keys(filesFinal);
async.eachSeries(authors, function(author, cb)
{
var name = author.split(' ');
var email = 'simple-mail#gmail.com';
var msg = '<p>Hi ' + author + ',</p><p>how is it going:</p><p>';
for(var a in Object.keys(filesFinal[author]))
{
msg += '<p style="margin-left:20px"> '+ICON_DOC+' <a href="';
msg += filesFinal[author][Object.keys(filesFinal[author])[a]]+'">'+Object.keys(filesFinal[author])[a]+'</a></p>';
}
msg += '</p></p><p><b>NOTE: This is a no-reply address.</b></p><p>Have a nice day! '+ICON_MONKEY+'</p>';
var message = {
text: msg,
from: 'test#mail.com',
to: email,
bcc: '',
subject: 'Sample subject',
attachment: [{data: msg, alternative: true}]
};
serverEmail.send(message, function(err, message)
{
if(err)
{
cb(err);
} else {
console.log(message);
cb();
}
});
}, function(err){
if(err) cbSendEmail(err);
cbSendEmail();
});
}
], (err) => {
if(err) console.log(err);
});
I would like to know if there is a way to control this issue or at least if there are another options for what I want to do.
Thanks.
A better (neat) way to use async waterfall.
Make sure you use return before any callback function. I have added them in the code.
Also, if you are nesting eachSeries, it is better to give a different name to callback function than the parent callback function.
I have changed 'cb' of child async.series to 'inner_cb'
Updated Code:
async.waterfall
([
funcOne,
funcTwo,
funcThree,
funcFour
], (err) => {
if(err) console.log(err);
});
funciton funcOne(cbNumPages) {
request({
url: 'any-url',
qs: {},
method: 'GET',
headers: {
'Authorization' : 'any-auth'
}
}, (err, response, body) => {
if (!err && response.statusCode == 200)
{
var $ = cheerio.load(body);
var pagesList = $('ol.aui-nav').children();
if(pagesList.length<1)
{
var numPages = 1;
} else {
var numPages = pagesList.length-2;
}
console.log(numPages);
var pages = new Array(numPages),
total = numPages*20,
iterator = 0;
async.eachSeries(pages, function(page, cb)
{
if(page>1)
{
pages[iterator] = iterator;
}else {
pages[iterator] = iterator*20;
}
iterator++;
return cb();
}, function(err){
if(err) return cbNumPages(err);
return cbNumPages(null, pages);
});
} else {
return cbNumPages(err);
}
})
}
function funcTwo(pages, cbGetFiles) {
var files = [];
var limitDate = moment().tz('Europe/Madrid').subtract(330,'days').format();
async.eachSeries(pages, function(page, cb)
{
request({
url: 'any-url'+page,
qs: {},
method: 'GET',
headers: {
'Authorization' : 'any-auth'
}
}, (err, response, body) => {
if(!err && response.statusCode == 200)
{
var $ = cheerio.load(body);
var rows = $('tr[id^=\'attachment-\']');
async.eachLimit(rows, 1, function(row, inner_cb)
{
var id = row.attribs['id'];
var file = row.attribs['data-attachment-filename'];
var author = $(row).children('.creator').text().trim();
var created = $(row).children('.created-date').text().trim();
created = moment.tz(created, 'MMM D, YYYY', 'Europe/Madrid').format();
var urlFile = 'simple-file' + $(row).children('.filename-column').children('.filename').attr('href');
var extension = row.attribs['data-attachment-filename'].split('.');
extension = extension[extension.length-1];
if(created<limitDate && validExtensions.indexOf(extension)>-1)
{
var f = '{ "id": "' + id + '",';
f += ' "file": "' + file + '",';
f += ' "url": "' + urlFile + '",';
f += ' "author": "' + author + '",';
f += ' "modified": "' + created + '" }';
files.push(JSON.parse(f));
}
return inner_cb();
}, (err) => {
if(err) return cbGetFiles(err);
});
return cb();
} else {
return cb(err);
}
});
}, function(err){
if(err){
return cbGetFiles(err);
} else {
return cbGetFiles(null, files);
}
});
}
function funcThree(files, cbGetAutors) {
var filesFinal = {};
for(var f in files)
{
if(!filesFinal[files[f].author])
{
var ff = {};
for(var i in files)
{
if(files[i].author === files[f].author)
{
ff[files[i].file] = files[i].url;
}
}
filesFinal[files[f].author] = ff;
}
}
return cbGetAutors(null, JSON.parse(JSON.stringify(filesFinal)));
}
function funcFour(filesFinal, cbSendEmail) {
var authors = Object.keys(filesFinal);
async.eachSeries(authors, function(author, cb)
{
var name = author.split(' ');
var email = 'simple-mail#gmail.com';
var msg = '<p>Hi ' + author + ',</p><p>how is it going:</p><p>';
for(var a in Object.keys(filesFinal[author]))
{
msg += '<p style="margin-left:20px"> '+ICON_DOC+' <a href="';
msg += filesFinal[author][Object.keys(filesFinal[author])[a]]+'">'+Object.keys(filesFinal[author])[a]+'</a></p>';
}
msg += '</p></p><p><b>NOTE: This is a no-reply address.</b></p><p>Have a nice day! '+ICON_MONKEY+'</p>';
var message = {
text: msg,
from: 'test#mail.com',
to: email,
bcc: '',
subject: 'Sample subject',
attachment: [{data: msg, alternative: true}]
};
serverEmail.send(message, function(err, message)
{
if(err)
{
return cb(err);
} else {
console.log(message);
return cb();
}
});
}, function(err){
if(err) return cbSendEmail(err);
return cbSendEmail();
});
}
As #YSK said in a comment, I was obtaining a 401 from the response.statusCode and therefore it is being missleaded to the cbSendEmail(err) with err beying null. Making the next method in the waterfall's first parameter beying the callback instead of the second.

Paypal express checkout in Node.js (error 10410)

I have been unable to to successfully parse the returned token to authenticate final Paypal sandbox payment process. Any help or input would be greatly appreciated :)
REF:
https://github.com/petersirka/node-paypal-express-checkout
https://developer.paypal.com/docs/classic/express-checkout/in-context/integration/
Here is the error I am returned:
ACK: 'Failure',
VERSION: '52.0',
BUILD: '22708556'
L_ERRORCODE0: '10410',
L_SHORTMESSAGE0: 'Invalid token',
L_LONGMESSAGE0: 'Invalid token.',
L_SEVERITYCODE0: 'Error'
Client side:
<form id="myContainer" method="post" action="/api/payment/payone"></form>
<script>
window.paypalCheckoutReady = function () {
paypal.checkout.setup('XXX', {
environment: 'sandbox',
container: 'myContainer'
});
};
</script>
<script src="//www.paypalobjects.com/api/checkout.js" async></script>
API:
payone: function(req, res) {
var paypal = require('paypal-express-checkout').init('username', 'password', 'signature', 'return url', 'cancel url', 'debug');
paypal.pay('20130001', 10.00, 'XX', 'USD', false, function(err, url) {
if (err) {
console.log(err);
return;
}
res.redirect(url);
});
paypal.detail( "token", "PayerID", function(err, data, invoiceNumber, price) {
if (err) {
console.log(err);
return;
}
});
}
Finally, paypal-express doc:
var parser = require('url');
var https = require('https');
var qs = require('querystring');
function Paypal(username, password, signature, returnUrl, cancelUrl, debug) {
this.username = "XXX";
this.password = "XXX";
this.solutiontype = 'Mark';
this.signature = "XXX";
this.debug = debug || false;
this.returnUrl = 'XXX';
this.cancelUrl = 'XXX';
this.url = 'https://' + 'api-3t.sandbox.paypal.com' + '/nvp'; //'https://' + (debug ? 'api-3t.sandbox.paypal.com' : 'api-3t.paypal.com') + '/nvp';
this.redirect = 'https://' + 'www.sandbox.paypal.com/cgi-bin/webscr'; //https://' + (debug ? 'www.sandbox.paypal.com/cgi-bin/webscr' : 'www.paypal.com/cgi-bin/webscr');
};
Paypal.prototype.params = function() {
var self = this;
return {
USER: self.username,
PWD: self.password,
SIGNATURE: self.signature,
SOLUTIONTYPE: self.solutiontype,
VERSION: '52.0'
};
console.log(self);
};
Paypal.prototype.detail = function(token, payer, callback) {
if (token.get !== undefined && typeof(payer) === 'function') {
callback = payer;
payer = token.get.PayerID;
token = token.get.token;
}
console.log(token);
var self = this;
var params = self.params();
params.TOKEN = token;
params.METHOD = 'GetExpressCheckoutDetails';
self.request(self.url, 'POST', params, function(err, data) {
if (err) {
callback(err, data);
return;
}
if (typeof(data.CUSTOM) === 'undefined') {
callback(data, null);
return;
}
console.log('3.3');
var custom = data.CUSTOM.split('|');
var params = self.params();
params.PAYMENTACTION = 'Sale';
params.PAYERID = payer;
params.TOKEN = token;
params.AMT = custom[1];
params.CURRENCYCODE = custom[2];
params.METHOD = 'DoExpressCheckoutPayment';
self.request(self.url, 'POST', params, function(err, data) {
if (err) {
callback(err, data);
return;
}
console.log('3.4');
callback(null, data, custom[0], custom[1]);
});
});
return self;
};
Paypal.prototype.request = function(url, method, data, callback) {
var self = this;
var params = qs.stringify(data);
if (method === 'GET')
url += '?' + params;
var uri = parser.parse(url);
var headers = {};
headers['Content-Type'] = method === 'POST' ? 'application/x-www-form-urlencoded' : 'text/plain';
headers['Content-Length'] = params.length;
var location = '';
var options = { protocol: uri.protocol, auth: uri.auth, method: method || 'GET', hostname: uri.hostname, port: uri.port, path: uri.path, agent: false, headers: headers };
var response = function (res) {
var buffer = '';
res.on('data', function(chunk) {
buffer += chunk.toString('utf8');
})
req.setTimeout(exports.timeout, function() {
callback(new Error('timeout'), null);
});
res.on('end', function() {
var error = null;
var data = '';
if (res.statusCode > 200) {
error = new Error(res.statusCode);
data = buffer;
} else
data = qs.parse(buffer);
callback(error, data);
});
};
var req = https.request(options, response);
req.on('error', function(err) {
callback(err, null);
});
if (method === 'POST')
req.end(params);
else
req.end();
return self;
};
Paypal.prototype.pay = function(invoiceNumber, amount, description, currency, requireAddress, callback) {
// Backward compatibility
if (typeof(requireAddress) === 'function') {
callback = requireAddress;
requireAddress = false;
}
var self = this;
var params = self.params();
params.PAYMENTACTION = 'Sale';
params.AMT = prepareNumber(amount);
params.RETURNURL = self.returnUrl;
params.CANCELURL = self.cancelUrl;
params.DESC = description;
params.NOSHIPPING = requireAddress ? 0 : 1;
params.ALLOWNOTE = 1;
params.CURRENCYCODE = currency;
params.METHOD = 'SetExpressCheckout';
params.INVNUM = invoiceNumber;
params.CUSTOM = invoiceNumber + '|' + params.AMT + '|' + currency;
self.request(self.url, 'POST', params, function(err, data) {
if (err) {
callback(err, null);
return;
}
if (data.ACK === 'Success') {
callback(null, self.redirect + '?cmd=_express- checkout&useraction=commit&token=' + data.TOKEN);
return;
}
callback(new Error('ACK ' + data.ACK + ': ' + data.L_LONGMESSAGE0), null);
});
return self;
console.log(self);
};
function prepareNumber(num, doubleZero) {
var str = num.toString().replace(',', '.');
var index = str.indexOf('.');
if (index > -1) {
var len = str.substring(index + 1).length;
if (len === 1)
str += '0';
if (len > 2)
str = str.substring(0, index + 3);
} else {
if (doubleZero || true)
str += '.00';
}
return str;
};
exports.timeout = 10000;
exports.Paypal = Paypal;
exports.init = function(username, password, signature, returnUrl, cancelUrl, debug) {
return new Paypal(username, password, signature, returnUrl, cancelUrl, debug);
};
exports.create = function(username, password, signature, returnUrl, cancelUrl, debug) {
return exports.init(username, password, signature, returnUrl, cancelUrl, debug);
};
Usually we encounter Error 10410 if there is incorrect token or no token passed.
please make sure that you are passing the right PayPal Express token.
for information on error codes you may refer: https://developer.paypal.com/docs/classic/api/errorcodes/

Resources