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

  1. Basic programming fundamentals are a must
  2. Must have a little bit idea about Node.js and JavaScript

Note:

You can obtain the AWS KEY here
You 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: How to implement Amazon's MWS API in Node.js and Javascript to generate frontend reports

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;
view raw Reports.js hosted with ❤ by GitHub

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
view raw HelperReport.js hosted with ❤ by GitHub

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();
};
}
view raw app.js hosted with ❤ by GitHub

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")
})
view raw server.js hosted with ❤ by GitHub

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>
view raw home.ejs hosted with ❤ by GitHub
How to implement Amazon's MWS API in Node.js and Javascript to generate frontend reports

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

Waiting Page mws

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.Reports Page mws

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