how to convert nested object to querystring in node using http module - javascript

I need help to send a nested object on the POST request to stripe API using standard http module on node.js
When I use querystring module to convert the json to a querystring it does not give appropriate output.
It does not behave well with a nested object.
This is my payload object:
const payload = {
"card": {
"number": number,
"exp_month": exp_month,
"exp_year": exp_year,
'cvc': cvc
},
};
My helper method to send HTTP POST request:
helpers.createPaymentToken = (payload, callback) => {
//validate the parameters
if (payload) { //configure the request details
const stringPayload =queryString.stringify(payload)
//configure request details
const requestDetails = {
protocol: "https:",
hostname: "api.stripe.com",
method: "post",
path:
"/v1/tokens",
auth:config.stripe.authToken,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": Buffer.byteLength(stringPayload),
},
};
//instantiate the request
const req = https.request(requestDetails, function (res) {
res.setEncoding('utf8');
var body = '';
console.log(res)
res.on('data', function (chunk) {
body = body + chunk;
});
res.on('end',function(){
console.log("Body :" + body);
if (res.statusCode != 200) {
callback(res.statusCode,body);
} else {
callback(null);
}
});
});
//bind to an error event so it does not get thrown
req.on("error", (e) => {
callback(e);
});
//Add the payload
req.write(stringPayload);
//end the request
req.end();
} else {
callback("Given parameters are missing on invalid");
}
};
expected querystring:
card[number]=****************2&card[exp_month]=11&card[exp_year]=2021&card[cvc]=***
expected output:(request body)
{
"card": {
"number": "************4242",
"exp_month": "11",
"exp_year": "2021",
"cvc": "***"
}
}
actual output: (request body)
{
"card": ""
}

Your code is currently handling card details directly which you shouldn't attempt for security reasons.
Since you are collecting card details, you should use Elements which is Stripe's UI library to collect card details client-side securely while meeting the lowest level of PCI compliance. You can read more about PCI compliance here.

Related

MailChimp Error Status: 401 Title: "API Key Invalid"

I am following a MailChimp API tutorial
When I test the API, I get a 401 response saying my API key is invalid.
Error -
Status: 401
"Your API key may be invalid, or you've attempted to access the wrong datacenter."
I have yet to register a domain yet, this is being testing using a local server. Could this be error be caused by MailChimp's refusing the request for another reason, perhaps CORS?
app.post('/signup', (req, res) => {
// Get form data
const { email } = req.body;
// Make sure field is filled
if(!email) {
res.redirect('/html/fail.html');
return;
}
// Construct req data
const data = {
members: [
{
email_address: email,
status: 'subscribed'
}
]
}
// Convert to JSON
const postData = JSON.stringify(data);
const options = {
url: 'https://us19.api.mailchimp.com/3.0/lists/listID',
method: 'POST',
headers: {
Authorization: 'auth xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-us19'
},
body: postData
};
request(options, (err, response, body) => {
if(err) {
console.log(err);
res.redirect('/html/fail.html');
} else {
if(response.statusCode === 200) {
res.redirect('/html/success.html');
} else {
console.log(response.body);
res.redirect('/html/fail.html');
}
}
});
})
I tried running the same code in request in PostMan and I got back a 200 response.
I was initially importing the API key from a config file, that I had not destructured...

Getting response, body and error from an asynchronous method making a POST API call

I am using this example to make a POST API call to an API: https://nodejs.dev/making-http-requests-with-nodejs#perform-a-post-request. No issues there, it works well.
Next, I wanted to create a function that makes this API call by taking in dynamic connection parameters, headers and payload. Did that and I am able to return the response object from the function so I can detect the response.statusCode, response.statusMessage, etc. Here's my Node.js code:
Module Code
const https = require("https");
function postLendingApplication(connection, data, callback) {
const options = {
hostname: connection.hostname,
port: connection.port,
path: connection.path,
method: connection.method,
headers: connection.headers
};
//console.log(options)
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`);
res.on("data", d => {
process.stdout.write(d);
});
callback(res);
});
req.on("error", error => {
console.error(error);
});
req.write(data);
req.end();
}
exports.postLendingApplication = postLendingApplication;
Invoking the code from another file
const bpc = require("./public-api");
const data = JSON.stringify({
title: 'foo',
body: 'bar',
userId: 1
});
const connection = {
hostname: 'jsonplaceholder.typicode.com',
port: 443,
path: '/posts',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length,
}
}
var response = bpc.postLendingApplication(connection, data, function(response) {
console.log("Inside the calling function");
//console.log(response);
console.log("Status Code: " + response.statusCode);
console.log("Status Message: " + response.statusMessage);
});
Successful console response
statusCode: 201
Inside the calling function
Status Code: 201
Status Message: Created
{
"title": "foo",
"body": "bar",
"userId": 1,
"id": 101
}
Question: In my callback method, I would like to receive the response body (The JSON) as well as the error so I can run some assertions based on the response/body/error that I received. I am not able to figure out how to setup callback in the module method so it can return all 3 values. If you can please help out with that, it would be greatly appreciated.
Here's the Repl URL in case you'd like to take a stab at it online: https://repl.it/#varun_verma/POST-API
I am not 100% on the question you are asking, I personally if you are wanting to use callbacks in this way use two functions one to handle the error and one for the succsessful response
however, you can use object destruction to give you undefined or default it to null if you like for the item not passed back as shown below:
Module Code
const https = require("https");
function postLendingApplication(connection, data, callback) {
const options = {
hostname: connection.hostname,
port: connection.port,
path: connection.path,
method: connection.method,
headers: connection.headers
};
//console.log(options)
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`);
let data = ''
res.on("data", d => {
data += d;
});
res.on('end', () => {
callback({response: res, data});
});
});
req.on("error", error => {
console.error(error);
callback({response: res, error});
});
req.write(data);
req.end();
}
exports.postLendingApplication = postLendingApplication;
Invoking the code from another file
const bpc = require("./public-api");
const data = JSON.stringify({
title: 'foo',
body: 'bar',
userId: 1
});
const connection = {
hostname: 'jsonplaceholder.typicode.com',
port: 443,
path: '/posts',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length,
}
}
var response = bpc.postLendingApplication(connection, data, function({ response, data, error}) {
// if error is not passed back in the object it defaults to undefined
if (error) console.error(error)
console.log("Inside the calling function");
//console.log(response);
console.log("Status Code: " + response.statusCode);
console.log("Status Message: " + response.statusMessage);
});
If I understand your question correctly, you want to have the response, data, and the error passed to the callback. (where you currently only pass the response)
You can pass the data to the callback like so:
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`);
let data = '';
res.on("data", d => {
data += d;
});
res.on('end', () => {
callback(res, data);
});
});
This buffers the data from the response as it comes in into a string and then only when the response ends passes both the response object and the data as a string to the callback. (you can then use JSON.parse in the callback to convert the data string to an object)
Passing the error is more difficult as the error callback is given separately from the response. I would recommend having a separate callback for the error:
function postLendingApplication(connection, data, callback, error_callback) {
...
req.on("error", error => {
console.error(error);
error_callback(error);
});
...
}
However someone else on here may be able to give a better solution for the error.

Retrieve data from external static JSON file and use in AWS Lambda

I am looking to pull some data from an external static JSON file based on an event sent to AWS Lambda.
So when someone sends their "customer_id" we pull the matching "email" and "option" from the external JSON file
https://3objects.netlify.com/3objects.json
Here is the code I have so far?
const AWS = require('aws-sdk');
const ses = new AWS.SES();
const request = require('request');
exports.handler = (event) => {
console.log(event.customer_id);
request({
url: 'https://3objects.netlify.com/3objects.json',
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
})
}, function (error, response) {
if (!error && response.statusCode == 200) {
var jsonResponse = JSON.parse(body); // turn response into JSON
// do stuff with the response and pass it back to Lambda...
});
// After JSON data retrieval of 'email' and 'option' from https://3objects.netlify.com/3objects.json we send them an email with this info
clientEmail = email;
contact_option = option;
var eParams = {Destination: {ToAddresses: [clientEmail]}, Message: {Body: { Text: { Data: 'Your contact option is ${contact_option}' },},Subject: { Data: "Your Contact Preference" }}, Source: "sales#example.com"};
var email = ses.sendEmail(eParams, function (err, data) { if (err) console.log(err); else { console.log("===EMAIL SENT==="); } });
};
How can I query and use that external JSON url data?
I prefer to using node-fetch. It is a package that let you use the fetch function from ES6.
I created an example of using node-fetch. The function getCustomers gets the customers from the url.
Then I created a function that returns a Promise. Inside this Promise, the retrieved data is send by mail using AWS.SES().
const AWS = require('aws-sdk'),
ses = new AWS.SES(),
fetch = require('node-fetch');
exports.handler = async (event) => {
console.log(event.customer_id);
const customers = await getCustomers();
customers.map(async customer => {
await sendEmailToCustomer(customer);
});
}
async function getCustomers() {
try {
const resp = await fetch('https://3objects.netlify.com/3objects.json');
const json = await resp.json();
console.log(json);
return json;
}
catch(e) {
throw e;
}
}
const sendEmailToCustomer = (customer) => new Promise((resolve, reject) => {
ses.sendEmail({
Destination:
{ ToAddresses: [customer.email] },
Message:
{
Body: { Text: { Data: `Your contact option is ${customer.customer_id}` }},
Subject: { Data: "Your Contact Preference" }
},
Source: "sales#example.com"}, (error, result => {
if (error) return reject(error);
resolve(result);
console.log(result);
})
}
In general, you should post the exact error you're facing instead of general question. it would help contributors to figure out the problem you're facing.
In your code snippet above, the part
// After JSON data retrieval of 'email' and 'option' from https://3objects.netlify.com/3objects.json we send them an email with this info
clientEmail = email;
contact_option = option;
var eParams = {Destination: {ToAddresses: [clientEmail]}, Message: {Body: { Text: { Data: 'Your contact option is ${contact_option}' },},Subject: { Data: "Your Contact Preference" }}, Source: "sales#example.com"};
var email = ses.sendEmail(eParams, function (err, data) { if (err) console.log(err); else { console.log("===EMAIL SENT==="); } });
is executed before the request returns. You need to move that code inside your callback to execute it when the request finishes.
I also would suggest you to convert your code to async/await for better readability and avoid this type of error.
See this article to learn how to do that : https://www.stormacq.com/2019/06/22/async-js.html

Failing to log into loopback API using request

I'm trying to log into a loopback API I'm running on a web server using the standard POST login request. However every time I run it I get:
{"error":{"statusCode":400,"name":"Error","message":"username or email is required","code":"USERNAME_EMAIL_REQUIRED"}}
I've tried logging in two ways. firstly:
var userDetails = {
"email": "foo%40bar.com",
"password": "test"
}
const requestOptions = {
url: "APIURL/api/Users/login?email="+userDetails.email+"&password="+userDetails.password
};
request.post(requestOptions, function (error, response, body) {
console.log(body);
});
And:
var userDetails = {
"email": "foo%40bar.com",
"password": "test"
}
const requestOptions = {
url: "https://temp-243314.appspot.com/api/Users/login",
header: {
"email": userDetails.email,
"password": userDetails.password
}
};
request.post(requestOptions, function (error, response, body) {
console.log(body);
});
Where both return the same error.
I like it when I see ; at the end of declarations :/. your var declarations need a lil ; :D
I'm 99% sure they are going to want that in the body. Those headers that you show in your 2nd attempt are non-standard so they would be stripped from most inbound servers (As is standard for most ingest servers like NGINX) If they wanted a custom header, they probably would have noted it like, "X-email" or something weird.
IF you're going to send those elements in the "body", they probably want it in JSON format, in which case you need to designate json=true in the request function.
If sent in the body, don't url encode with the %40 in replacement of the #
5.
const request = require('request');
let options = {
uri: 'APIURL/api/Users/login',
method: 'POST',
json: {
"email":"foo#bar.com",
"password":"¡¡¡¡¡¡¡¡UNHACKABLE!!!!!!!!!"
}
};
request(options, function (err, resp, body) {
let isHTTPSuccess;
try {
isHTTPSuccess = resp.statusCode + '';
isHTTPSuccess = isHTTPSuccess[0];
if (isHTTPSuccess === '2'){
isHTTPSuccess = true;
} else {
isHTTPSuccess = false;
}
} catch(parseError){
console.error(parseError);
console.error(err);
return;
}
if (err || !isHTTPSuccess){
console.error(body);
return;
}
console.log('WOWZER, THE CODE ABOVE IS A BIT MUCH NO? ANYWAY... HERE IS THE BODY: ', body);
return;
});
Good Luck!
Your request should be like:
var userDetails = {
"email": "foo#bar.com",
"password": "test"
}

how to pass value in api body in protractor while requesting the api

I want to pass the value in API request body.
I tried below code for that
var options = { method: 'POST',
url: 'https://ssgpreprod.serviceurl.in/gonogo-api/atm/tw/cro-approval',
headers:
{ 'Postman-Token': '9d6a0ad1-c3a1-402f-b845-b6416f49df6b',
'cache-control': 'no-cache',
'Content-Type': 'application/json' },
body:
{ oHeader:
{ sReqType: 'application/json',
sAppSource: 'WEB 2.02.01',
sSourceID: 'GONOGO_HDBFS',
sAppID: 610961419000670,
dtSubmit: '',
sCroId: 'HDB_TW_CRO#cell.com',
sDsaId: 'default',
sInstID: 4019,
sUserName: 'CHDBTWCRO',
sProduct: 'TW',
sDealerId: '61096' },
sRefID:testData.twPreIpa.twReferenceId,
sAppStat: testData.twCroDetails.twCroDecision,
aCroJustification: [ { sRemark: testData.twCroDetails.twRemark, sSubTo: testData.twCroDetails.twSubjectTo} ],
bApprAmtExist: true,
dApprAmt: testData.twApplyDetails.twLoanAmount,
dItrRt: testData.twCroDetails.twRoi,
dLtv: testData.twCroDetails.twLtv,
aDedupeRefID: [] },
json: true };
request(options, function (error, response, body) {
if (error) throw new Error(error);
browser.logger.info(JSON.stringify(body));
browser.logger.info(JSON.stringify(response));
browser.logger.info('status code is : ' + response.statusCode);
expect(response.statusCode).toBe(200).then(function () {
browser.logger.info('case is approved');
this.logOut(testData);
})
});
I am passing value from xlsx file i.e testData.twPreIpa.twReferenceId but I am getting 422 status code and below output
[2019-05-28 15:42:10.403] [INFO] : - {"title":"Conversion Failed","status":422,"detail":"The content you've sent is probably malformed."}
Also, when I add - browser.logger.info('approved'); above var options it prints on console.. but when I add - browser.logger.info(testData.twPreIpa.twReferenceId);
It gives me error .. ouput displayed -
Failed: Cannot read property 'twReferenceId' of undefined
TypeError: Cannot read property 'twReferenceId' of undefined
While this may not directly answer your question it should be helpful to you.
I worked on a similar framework to what (I assume) yours looks like,some api and some UI validations.
I had a separate class for my API calls which returned some value, sometimes the entire response body. I allowed myself the ability to pass in whatever body I needed as a parameter.
This is an example of the approach I took.
module.exports = class Endpoints {
addSomethingToUser(bearerToken, userId, JSONbody) {
//Function will preform a POST request and return the status code if successful
//Else if will return the body of the response
let endpoint = 'http://myApp.com:9090/api/users/' + userId;
return new Promise((resolve, reject) => {
let options = {
method: "POST",
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
},
body: JSON.stringify(JSONbody),
};
request(
endpoint,
options ,
function (error, response, body) {
if (!error && (response.statusCode >= 200 && response.statusCode < 300)) {
resolve(response.statusCode);
} else {
console.log('error:', error, response && response.statusCode);
reject(JSON.stringify(response, undefined, 2));
};
}
);
});
};
}
It is called like
let apiCallsFile = require('../apiCalls/restCalls');
let apiCalls = new apiCallsFile();
it('simple test', async function(){
let requiredBody = {
"NAME": "Test Name",
"GENDER": "MALE",
};
let apiResult = await apiCalls.addSomethingToUser(bearerToken, userId, requiredBody );
expect(apiResult).toBe('201');
}
Your issue seems to be related to the actual values you are using from excel. You should attempt to print out your values before attempting to send the request to ensure they are all present and in the format you expect.

Categories