Hire the author: Lakshay G
This project is available at https://github.com/learningdollars/lakshay-mws-api
What is Amazon MWS?
Amazon Marketplace Web Service (Amazon MWS) is an integrated web service API that helps Amazon sellers to programmatically exchange data on listings, orders, payments, reports, and more. Data integration with Amazon enables high levels of selling automation, which can help sellers grow their business. By using Amazon MWS, sellers can increase selling efficiency, reduce labor requirements, and improve response time to customers.
In this blog, we will stick only with the reports part.
Motivation
It is expected that after this present situation of COVID-19 is over, the e-commerce industry will get a huge boost for which the sellers should be prepared beforehand which is the main thing that motivates me to do this project.
Goal
Not every seller can read the data in a JSON format or XML format, so my goal is to make these reports available in a tabular form and downloadable as a .csv which is readable by everyone easily.
Glossary
Settlement report: This report is useful for reconciling transactions with bank statements and reports
Project Requirements
- Basic programming fundamentals are a must
- Must have a little bit idea about Node.js and JavaScript
Note:
You can obtain the AWS KEY hereYou can obtain the AWS SECRET KEY here
Getting Started
1. Understanding the documentation, how each report will be fetched ?
Refer to this to understand which report requires which API operation to be performed and what are there enumeration values, because these two things will help us generate the reports easily. For example: One of the inventory reports is ‘All Listings Report’, that has the enumeration value of ‘_GET_MERCHANT_LISTINGS_ALL_DATA_’ and the API operation associated with this is RequestReport. So we will be requesting this report, which will in turn give us the request report ID. After this we will perform the getReportRequestList operation to get the generated Reports ID and finally we will perform getReport operation to get our requested report. This flow can also be understood by the picture shown below:
2. After understanding the documentation, let’s move on to the implementation part
We will be using amazon-mws npm package for the implementation purpose
- Install the package using npm install amazon-mws command and require it inside the Reports.js file, so that you can use the functions provided by the npm package
- We will be fetching 5 most important reports Inventory, Order, Settlement, Performance and Amazon Pay report
- For this we will need to define all the API operations associated with these reports respectively
- Inventory Report: requestReport -> getReportRequestList -> getReport
- Order Report: requestReport -> getReportRequestList -> getReport
- Performance Report: requestReport -> getReportRequestList -> getReport
- Amazon Pay Report: requestReport -> getReportRequestList -> getReport
- Settlement Report: getReportList -> getReport
NOTICE: We can’t request settlement reports as these are scheduled by Amazon itself.Therefore, we will be fetching only the latest one in this mws project
Here is the Report.js file
//import MwsApi from 'amazon-mws'; | |
const log = require('simple-node-logger').createSimpleLogger('project.log'); | |
const MwsApi = require('amazon-mws') | |
const amazonMws = new MwsApi(); | |
const requestReport = async(accessKey, accessSecret, version, reportType, sellerId, mwsAuthToken, startDate, endDate) => { | |
amazonMws.setApiKey(accessKey, accessSecret); | |
amazonMws.setHost('mws.amazonservices.in'); | |
try { | |
const response = await amazonMws.reports.submit({ | |
'Version': version, | |
'Action': 'RequestReport', | |
'SellerId': sellerId, | |
'MWSAuthToken': mwsAuthToken, | |
'ReportType': reportType, | |
'StartDate': startDate, | |
'EndDate': endDate | |
}); | |
// console.log(response); | |
return response; | |
} catch (error) { | |
log.info("ERROR!!" + error) | |
return error; | |
} | |
}; | |
const getReportRequestList = async(accessKey, accessSecret, version, sellerId, mwsAuthToken, reportRequestId) => { | |
amazonMws.setApiKey(accessKey, accessSecret); | |
amazonMws.setHost('mws.amazonservices.in'); | |
try { | |
const response = await amazonMws.reports.search({ | |
'Version': version, | |
'Action': 'GetReportRequestList', | |
'SellerId': sellerId, | |
'MWSAuthToken': mwsAuthToken, | |
'ReportRequestIdList.Id.1': reportRequestId | |
}); | |
return response; | |
} catch (error) { | |
log.info("ERROR!!" + error) | |
return error; | |
} | |
}; | |
const getReportList = async(accessKey, accessSecret, version, sellerId, mwsAuthToken, report) => { | |
amazonMws.setApiKey(accessKey, accessSecret); | |
amazonMws.setHost('mws.amazonservices.in'); | |
try { | |
const response = await amazonMws.reports.search({ | |
'Version': version, | |
'Action': 'GetReportList', | |
'SellerId': sellerId, | |
'MWSAuthToken': mwsAuthToken, | |
'ReportTypeList.Type.1': report | |
}); | |
return response; | |
} catch (error) { | |
log.info("ERROR!!" + error) | |
return error; | |
} | |
}; | |
const getReport = async(accessKey, accessSecret, version, sellerId, mwsAuthToken, reportRequestId) => { | |
amazonMws.setApiKey(accessKey, accessSecret); | |
amazonMws.setHost('mws.amazonservices.in'); | |
try { | |
const response = await amazonMws.reports.search({ | |
'Version': '2009-01-01', | |
'Action': 'GetReport', | |
'SellerId': sellerId, | |
'MWSAuthToken': mwsAuthToken, | |
'ReportId': reportRequestId, | |
}); | |
return response; | |
} catch (error) { | |
log.info("ERROR!!" + error) | |
return error; | |
} | |
}; | |
exports.getReport = getReport; | |
exports.getReportRequestList = getReportRequestList; | |
exports.requestReport = requestReport; | |
exports.getReportList = getReportList; |
3. After this we will create another file named HelperReport.js, that will include the enumeration values of the reports to be generated and the array of requestID’s of different report
requestId’s are needed in order to keep a track of all the requested reports
const log = require('simple-node-logger').createSimpleLogger('project.log'); | |
//Reports | |
const inventoryReports = ['_GET_MERCHANT_LISTINGS_ALL_DATA_', ]; | |
const orderReports = ['_GET_FLAT_FILE_ORDERS_DATA_']; | |
const performanceReports = ['_GET_V1_SELLER_PERFORMANCE_REPORT_']; | |
const amazonPayReports = ['_GET_FLAT_FILE_OFFAMAZONPAYMENTS_SANDBOX_SETTLEMENT_DATA_']; | |
const settlementReports = ['_GET_V2_SETTLEMENT_REPORT_DATA_XML_']; | |
//helper reports array | |
var helper = []; | |
helper[0] = inventoryReports | |
helper[1] = orderReports; | |
helper[2] = performanceReports; | |
helper[3] = amazonPayReports; | |
helper[4] = settlementReports; | |
//RequestIds | |
var inventoryRequestIds = []; | |
var orderRequestIds = []; | |
var performanceRequestIds = []; | |
var amazonPayRequestIds = []; | |
var requestIds = [inventoryRequestIds, orderRequestIds, performanceRequestIds, amazonPayRequestIds]; | |
exports.helper = helper | |
exports.requestIds = requestIds |
4. Now let’s set up our app.js file which is going to perform the main magic
In this we will be exporting a class, whose constructor will set up some required values as entered by the user in the frontend(discussed later) and call the generateReports function which will step by step perform all the API operations required by respective reports
Also we will be exporting the reports generated/fetched in this file
var Report = require('./reports/Reports.js') | |
var HelperReport = require('./reports/HelperReport.js') | |
const log = require('simple-node-logger').createSimpleLogger('project.log'); | |
var UserId = 'xyz' | |
const VERSION = '2009-01-01'; | |
var AWS_ACCESS_KEY_ID = 'arg1' | |
var AWS_SECRET_ACCESS_KEY = 'arg2' | |
var SellerId = 'arg3' | |
var MWSAuthToken = 'arg4' | |
var startDate; | |
var endDate; | |
var settlement = "No data found for settlement Reports"; | |
var performance = "No data found for performance Reports"; | |
var inventory = "No data found for inventory Reports"; | |
var order = "No data found for order Reports"; | |
var amazonpay = "No data found for amazonpay Reports"; | |
exports.RequestReports = class RequestReports { | |
constructor(aws_key, aws_secret_key, seller_id, mws_Auth_token, user_id, StartDate, EndDate) { | |
AWS_ACCESS_KEY_ID = aws_key; | |
AWS_SECRET_ACCESS_KEY = aws_secret_key; | |
SellerId = seller_id; | |
MWSAuthToken = mws_Auth_token; | |
UserId = user_id; | |
startDate = StartDate; | |
endDate = EndDate; | |
generateReports(); | |
} | |
} | |
const getReportList = async() => { | |
try { | |
HelperReport.helper[4].forEach(async report => { | |
const getReportListResult = await Report.getReportList(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, VERSION, SellerId, MWSAuthToken, report); | |
if (!getReportListResult.ReportInfo) { | |
log.info("For userID: " + UserId + " NO SETTLEMENT REPORTS FOUND"); | |
} else { | |
if (getReportListResult.ReportInfo[0].ReportId) { | |
log.info("For userId: " + UserId + " SettlementReportId: " + getReportListResult.ReportInfo[0].ReportId); | |
getReports(getReportListResult.ReportInfo[0].ReportId, 4); | |
} else { | |
log.info("For userId: " + UserId + " No settlements report found"); | |
} | |
} | |
}) | |
} catch (err) { | |
log.Info("For userID: " + UserId + " Error occured!! " + err); | |
} | |
} | |
const requestReports = async(reportType) => { | |
HelperReport.helper[reportType].forEach(async report => { | |
const requestReportResult = await Report.requestReport(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, VERSION, report, SellerId, MWSAuthToken, startDate, endDate); | |
handleRequestReportResult(requestReportResult, reportType); | |
}); | |
}; | |
const requestReportStatus = async(reportRequestId, reportType) => { | |
const requestReportStatusResult = await Report.getReportRequestList(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, VERSION, SellerId, MWSAuthToken, reportRequestId); | |
log.info("For userId: " + UserId + " requestReport is successful " + requestReportStatusResult); | |
handleRequestReportStatus(requestReportStatusResult, reportType); | |
}; | |
const getReports = async(reportRequestId, reportType) => { | |
const reportRes = await Report.getReport(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, VERSION, SellerId, MWSAuthToken, reportRequestId); | |
switch (reportType) { | |
case 0: | |
exports.inventory = reportRes.data ? reportRes.data : reportRes | |
break; | |
case 1: | |
exports.order = reportRes.data ? reportRes.data : reportRes; | |
break; | |
case 2: | |
exports.performance = reportRes.data ? reportRes.data : reportRes; | |
break; | |
case 3: | |
exports.amazonpay = reportRes.data ? reportRes.data : reportRes; | |
break; | |
case 4: | |
exports.settlement = reportRes.data ? reportRes.data : reportRes; | |
break; | |
} | |
}; | |
const handleRequestReportResult = (requestReportResult, reportType) => { | |
try { | |
if (requestReportResult.ReportRequestInfo.ReportRequestId) { | |
HelperReport.requestIds[reportType].push(requestReportResult.ReportRequestInfo.ReportRequestId); | |
log.info("For userId: " + UserId + " ReportRequestId: " + requestReportResult.ReportRequestInfo.ReportRequestId); | |
setTimeout(function () { | |
requestReportStatus(requestReportResult.ReportRequestInfo.ReportRequestId, reportType); | |
}, 60000); | |
} | |
} catch (err) { | |
log.info("For userId: " + UserId + "ERROR OCCURED!!! " + err) | |
} | |
}; | |
const handleRequestReportStatus = (requestReportStatusResult, reportType) => { | |
if (!requestReportStatusResult.ReportRequestInfo.GeneratedReportId) { | |
log.info("For userId: " + UserId + " No data is available for " + requestReportStatusResult.ReportRequestInfo.ReportType) | |
} else { | |
log.info("For userId: " + UserId + " GeneratedReportId: " + requestReportStatusResult.ReportRequestInfo.GeneratedReportId) | |
getReports(requestReportStatusResult.ReportRequestInfo.GeneratedReportId, reportType); | |
} | |
}; | |
const generateReports = () => { | |
if (true) { | |
requestReports(0); | |
requestReports(1); | |
requestReports(2); | |
requestReports(3); | |
getReportList(); | |
}; | |
} |
5. Now comes the last step of our backend, to set up our server.js file
This file includes how the different requests are going to get served or how they are going to get redirected
var express = require('express') | |
var app = express(); | |
var App = require('./app.js') | |
var bodyParser = require('body-parser') | |
app.use(bodyParser.urlencoded({ | |
extended: true | |
})) | |
app.get('/', function (req, res) { | |
res.render('home.ejs'); | |
}) | |
app.get('/reports', function (req, res) { | |
res.render('reportsPage.ejs', { | |
settlementdata: App.settlement, | |
performancedata: App.performance, | |
inventorydata: App.inventory, | |
orderdata: App.order, | |
amazonpaydata: App.amazonpay | |
}) | |
}) | |
try { | |
app.post('/', function (req, res) { | |
new App.RequestReports(req.body.aws_key, req.body.aws_secret_key, req.body.seller_id, req.body.mws_auth_token, req.body.seller_name, new Date(req.body.start_date).toISOString(), new Date(req.body.end_date).toISOString()) | |
res.render('loading.ejs', { | |
settlementdata: App.settlement, | |
performancedata: App.performance, | |
inventorydata: App.inventory, | |
orderdata: App.order, | |
amazonpaydata: App.amazonpay | |
}) | |
}) | |
} catch (err) { | |
console.log("Error") | |
} | |
app.listen(3000/*process.env.PORT*/, process.env.IP, function () { | |
console.log("Server started") | |
}) |
Frontend
Lets design our home.ejs, loading.ejs and reportsPage.ejs files respectively
home.ejs
This will be our landing page, and includes one jumbotron (a big box for calling extra attention to some special content or information) and one form
<html> | |
<head> | |
<title>Reports</title> | |
<!-- jQuery --> | |
https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js | |
<!-- Bootstrap --> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> | |
https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js | |
<!-- Datepicker --> | |
<link href='bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css' rel='stylesheet' type='text/css'> | |
http://bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js | |
<script> | |
$(document).ready(function () { | |
var date_input = $('input[name="start_date"]'); | |
var container = $('.bootstrap-iso form').length > 0 ? $('.bootstrap-iso form').parent() : "body"; | |
var options = { | |
format: 'mm/dd/yyyy', | |
container: container, | |
todayHighlight: true, | |
autoclose: true, | |
}; | |
date_input.datepicker(options); | |
}) | |
</script> | |
<script> | |
$(document).ready(function () { | |
var date_input = $('input[name="end_date"]'); | |
var container = $('.bootstrap-iso form').length > 0 ? $('.bootstrap-iso form').parent() : "body"; | |
var options = { | |
format: 'mm/dd/yyyy', | |
container: container, | |
todayHighlight: true, | |
autoclose: true, | |
}; | |
date_input.datepicker(options); | |
}) | |
</script> | |
https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.4.1/js/bootstrap-datepicker.min.js | |
<link rel="stylesheet" | |
href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.4.1/css/bootstrap-datepicker3.css" /> | |
<script> | |
$(document).ready(function () { | |
var date_input = $('input[name="start_date"]'); | |
var container = $('.bootstrap-iso form').length > 0 ? $('.bootstrap-iso form').parent() : "body"; | |
date_input.datepicker({ | |
format: 'mm/dd/yyyy', | |
container: container, | |
todayHighlight: true, | |
autoclose: true, | |
}) | |
}) | |
</script> | |
<script> | |
$(document).ready(function () { | |
var date_input = $('input[name="end_date"]'); | |
var container = $('.bootstrap-iso form').length > 0 ? $('.bootstrap-iso form').parent() : "body"; | |
date_input.datepicker({ | |
format: 'mm/dd/yyyy', | |
container: container, | |
todayHighlight: true, | |
autoclose: true, | |
}) | |
}) | |
</script> | |
<style> | |
.containerform{ | |
margin-left:35% !important | |
} | |
form{ | |
width:40% !important | |
} | |
button{ | |
width:100% !important | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<div class="jumbotron jumbotron-fluid"> | |
<div class="container"> | |
<h1 class="display-4">Easy way to generate your amazon reports</h1> | |
<p class="lead">Fill in your credentials and hit the generate reports button</p> | |
</div> | |
</div> | |
<div class='containerform'> | |
<form method='POST' action="/"> | |
<div class="form-group"> | |
<input type="text" class="form-control" name="aws_key" placeholder='AWS KEY' required autocomplete="off"> | |
</div> | |
<div class="form-group"> | |
<input type="text" class="form-control" name="aws_secret_key" placeholder='AWS SECRET KEY' required autocomplete="off"> | |
</div> | |
<div class="form-group"> | |
<input type="text" class="form-control" name="seller_id" placeholder='SELLER ID' required autocomplete="off"> | |
</div> | |
<div class="form-group"> | |
<input type="text" class="form-control" name="mws_auth_token" placeholder='MWS AUTHORISATION TOKEN' required autocomplete="off"> | |
</div> | |
<div class="form-group"> | |
<input type="text" class="form-control" name="seller_name" placeholder='SELLER NAME' required autocomplete="off"> | |
</div> | |
<div class="form-group"> | |
<input class="form-control" name="start_date" placeholder="START DATE MM/DD/YYYY" type="text" autocomplete="off" required/> | |
</div> | |
<div class="form-group"> | |
<input class="form-control" name="end_date" placeholder="END DATE MM/DD/YYY" type="text" autocomplete="off" required/> | |
</div> | |
<button type="submit" class="btn btn-primary">Generate Reports</button> | |
</form> | |
</div> | |
</div> | |
</body> | |
</html> |

loading.ejs
This page includes a progress bar that will complete once the reports are fetched properly
I took help of this video in order to implement a progress bar

reportsPage.ejs
This page includes how to display our reports that we fetched in our backend using mws API
This page also includes a feature of downloading a report as CSV which I implemented with the help of this solution.
Learning Tools
First of all, documentation is a must. Go through the amazon-mws documentation thoroughly, After that you can go through the following links (these helped me complete my project)
Link to design a progress bar
Link to create Download as CSV function for mws reports
Don’t write a messy code, everything should be well understood. As a result it will help you in debugging. Also, do a console.log() at each major step you feel is important or you feel maybe will throw some kind of error.
Learning Strategy
The major obstacle that I came across in order to implement this successfully was when I tried fetching settlement reports. I followed the traditional approach of performing requestReport API operation. So, in order to avoid these silly mistakes focus on the official documentation is a must. Don’t write a messy code, everything should be well understood. As a result it will help you in debugging. Also, do a console.log() at each major step you feel is important or you feel maybe will throw some kind of error.
Search terms
Amazon mws, Amazon mws node.js, Javascript mws reports generation
This project isn’t for the faint of heart
Try to be in a quiet place while doing this project, it requires focus.
Reflective Analysis
It was a fun learning experience to understand how to use Amazon MWS API. I didn’t know about this API earlier.A very common mistake that you are expected to make is with the API operation you will use for fetching settlement reports (Remember its getReportList and not requestReport)
Conclusion
This project includes the reports that are fetched using the MWS API, how about a complete project that includes some calculations from the reports data that is fetched and present it in graphical form to the seller and also guide him/her to follow certain steps to reduce losses
This project is available at https://github.com/learningdollars/lakshay-mws-api
Dude! this is amazing
The article is very educative and indeed requires focus, I love the way you have arranged it providing links in case one is stuck and also for better understanding. It’s a nice idea for it provides a tabular form that is easy to understand and interpret. Reports are an essential thing to every business company and also to every individual in the business field since sellers can increase selling efficiency, reduce labor requirements, and improve response time to customers.
Since the technology is already applicable, future directions can be generating reports and allowing some calculations that will present a clear view of the customer’s direction in the business. From the reports data that is fetched and present in graphical form to the seller and also guide him/her to follow certain steps to reduce losses. We can also do a study and see if same reports can be generated on other top selling businesses like the Jumia and Alibaba
For adding your javascript in webpage try this opensource chrome extension
https://chrome.google.com/webstore/detail/website-scripting/aggnfbkmhedkekjoplldenefbchaoiln
Thanks for the post, it helped me to understand MWS services. I have one question though, I see that you set a timeout of 60 seconds. Is there a way to implement the ‘ReportProcessingFinishedNotification’ service? I’m reading that you need a SQS in order to receive the notification for making the getReport request, but I’m a bit lost in this topic.
Thanks Gerardo for the positive feedback! You can try with async / await function also