Sign SP-API request in Google Apps Script - google-apps-script

I'm trying to call the Amazon SP-API from Google Apps Script.
I was able to retrieve the access-token. However when trying call the
I attempted to follow the advice on this post: Google Apps Script: Getting Orders from Amazon Selling Partner API (Signing Requests)
There seems to be something wrong with the way the signature is being calculated.
(I am able to make this call in Postman.)
However in Goolge Apps Script I'm getting an InvalidSignature response.
This is my code:
function getAsins() {
const hex = bytes => bytes.map(byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');
const digestToHex = data => hex(Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, data));
const toBytes = data => Utilities.newBlob(data).getBytes();
//Credential variables
var access_token = AccessToken();
const ACCESS_ID = 'AKIEXAMPLEEXAMPLEW5';
const ACCESS_KEY = 'bh8EXAMPLEEXAMPLEWw5SA/EXAMPLE+5EXAMPLEP';
const marketplaceId = 'ATVPDKIKX0DER';
//Time variables
var currentDate = new Date();
var isoDate = currentDate.toISOString();
var isoString = isoDate.replace(/-/g, "").replace(/:/g, "").replace(/(\.\d{3})/, "");
var yearMonthDay = Utilities.formatDate(currentDate, 'GTM-4', 'yyyyMMdd');
//API variables
var end_point = 'https://sellingpartnerapi-na.amazon.com';
var aws_region = "us-east-1";
var service = "execute-api";
var termination_string = "aws4_request";
//CanonicalRequest components:
var asin = 'B07X6C9RMF';
var httpRequestMethod = 'GET';
var canonicalURI = '/catalog/2022-04-01/items/' + asin;
var canonicalQueryString = '?marketplaceIds=' + marketplaceId;
var canonicalheaders = 'host:' + "sellingpartnerapi-na.amazon.com" + '\n' + 'x-amz-access-token:' + access_token + '\n' + 'x-amz-date:' + isoDate;
var signedheaders = 'host;x-amz-access-token;x-amz-date'; //;user-agent
var requestPayloadHashed = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, "");//NEW
requestPayloadHashed = requestPayloadHashed.map(function (e) { return ("0" + (e < 0 ? e + 256 : e).toString(16)).slice(-2) }).join("");//NEW
//Building the canonical request
var canonical_string = httpRequestMethod + '\n' + canonicalURI + '\n' + "MarketplaceIds=" + marketplaceId + '\n' + canonicalheaders + '\n\n' + signedheaders + '\n' + requestPayloadHashed;//UPDATED
var canonical_signature = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, canonical_string);
canonical_request = canonical_signature.map(function (e) { return ("0" + (e < 0 ? e + 256 : e).toString(16)).slice(-2) }).join("");
var credential_scope = yearMonthDay + '/' + aws_region + '/' + service + '/' + termination_string;
var string_to_sign = "AWS4-HMAC-SHA256" + '\n' + isoString + '\n' + credential_scope + '\n' + canonical_request;
var kSecret = ACCESS_KEY;
var kDate = Utilities.computeHmacSha256Signature(yearMonthDay, "AWS4" + kSecret);
var kRegion = Utilities.computeHmacSha256Signature(toBytes(aws_region), kDate);
var kService = Utilities.computeHmacSha256Signature(toBytes(service), kRegion);
var kSigning = Utilities.computeHmacSha256Signature(toBytes(termination_string), kService);
var signature = hex(Utilities.computeHmacSha256Signature(toBytes(string_to_sign), kSigning));
Logger.log('signature: ' + signature)
var options = {
'method': 'GET',
'headers': {
'x-amz-access-token': access_token,
'x-amz-date': isoDate,
'Authorization': 'AWS4-HMAC-SHA256 Credential=' + ACCESS_ID + '/' + credential_scope + ', SignedHeaders=' + signedheaders + ', Signature=' + signature,
},
'muteHttpExceptions': true
}
var asinData = UrlFetchApp.fetch(end_point + canonicalURI + canonicalQueryString, options);
Logger.log(asinData);
}
This is the response I'm getting:
{
"errors": [
{
"message": "The request signature we calculated does not match the signature you provided.
....
....
The String-to-Sign should have been
'AWS4-HMAC-SHA256
20220701T185142Z
20220701/us-east-1/execute-api/aws4_request
9a5fa583759a5ff04d7ce67d01fcf7157e9f8a58c4fbbdd69f91cb7a816f5650'
",
"code": "InvalidSignature"
}
]
}
Any help sorting this out would be greatly appreciated!

I'm only starting on this but I think the Canonical Query String does not include the question mark.

Related

Google Apps Script: Connecting to Amazon Selling Partner API (signature does not match)

I'm trying to get data from Amazon Selling Partner API, using a signing request.
The documentation of the API for Orders can be found here.
And I'm trying to invoke the GET /catalog/2020-04-01/items/
Here is the code so far:
function getData(){
const hex = bytes => bytes.map(byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');
const digestToHex = data => hex(Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, data));
const toBytes = data => Utilities.newBlob(data).getBytes();
var access_token = access_token();
//Time variables
var ACCESS_KEY = "the_key";
var ACCESS_ID = "the_id";
//Time variables
var currentDate = new Date();
var isoDate = currentDate.toISOString();
var yearMonthDay= Utilities.formatDate(currentDate, 'GTM-5', 'yyyyMMdd');
//API variables
var end_point = 'https://sellingpartnerapi-eu.amazon.com';
//Credential variables
var region = "eu-west-1";
var service = "execute-api";
var termination = "aws4_request";
var httpRequestMethod = 'GET';
var canonicalURI = '/catalog/2022-04-01/items/B0BS147PTP';
var canonicalQueryString = '?marketplaceId=A1PA6795UKMFR9';
var canonicalheaders = 'host:' + canonicalURI + '\n' + 'x-amz-access-token:' + access_token + '\n' + 'x-amz-date:' + isoDate;
var signedheaders = 'host;user-agent;x-amz-access-token;x-amz-date';
const canonicalRequest = [httpRequestMethod,canonicalURI,canonicalQueryString,canonicalheaders + "\n",""].join("\n");
const canonical_request = digestToHex(canonicalRequest);
var credential_scope = yearMonthDay + '/' + aws_region + '/' + service + '/' + termination_string;
var string_to_sign = "AWS4-HMAC-SHA256" + '\n' + isoDate + '\n' + credential_scope + '\n' + canonical_request;
Logger.log(string_to_sign)
var kSecret = ACCESS_KEY;
var kDate = Utilities.computeHmacSha256Signature(yearMonthDay, "AWS4" + kSecret);
var kRegion = Utilities.computeHmacSha256Signature(toBytes(aws_region), kDate);
var kService = Utilities.computeHmacSha256Signature(toBytes(service), kRegion);
var kSigning = Utilities.computeHmacSha256Signature(toBytes(termination_string), kService);
const signature = hex(Utilities.computeHmacSha256Signature(toBytes(string_to_sign), kSigning));
Logger.log(signature)
// 4. Request.
var options = {
'method': 'GET',
'headers': {
'x-amz-access-token': access_token,
'x-amz-date': isoDate,
'Authorization': 'AWS4-HMAC-SHA256 Credential=' + ACCESS_ID + '/' + credential_scope + ', SignedHeaders=' + signedheaders + ', Signature=' + signature,
},
}
var getOrders = UrlFetchApp.fetch(end_point + canonicalURI + canonicalQueryString, options);
Logger.log(getOrders);
}
and here is the response I have:
"errors": [
{
"message": "The request signature we calculated does not match the signature you provided. Check your AWS Secret Access... (use muteHttpExceptions option to examine full response)

Script do nothing

I'm trying to write code to save data and read data from telegram to google sheets using this code
function sendText(id,text) {
var url = telegramUrl + "/sendMessage?chat_id=" + id + "&text=" + text;
var response = UrlFetchApp.fetch(url);
Logger.log(response.getContentText());
}
function doPost(e) {
var data = JSON.parse(e.postData.contents);
var id = data.message.chat.id;
var username = data.message.chat.username;
var date = Utilities.formatDate(new Date(), "GMT+9", "dd/MM/yyyy");
var hour = Utilities.formatDate(new Date(), "GMT+9", "HH:mm:ss");
var text = data.message.text;
if (text.startsWith("/start")) {
sendText(id,"Hello " + username + ", welcome to my bot");
} else if (text.startsWith("/input")) {
const text = String(data.message.text).replace("/input ", "");
var item = text.split(",");
var sheet = SpreadsheetApp.openById(ssId).getSheetByName(sheet1);
sheet.appendRow([date,hour,id,username,item[1],item[2],item[3],item[4],item[5],item[6],item[7],item[8],item[9]]);
sendText(id,"data has been saved");
} else if (text.startsWith("/search")) {
const text = String(data.message.text).replace("/search ", "");
var keyword = text.split(" ");
var sheet = SpreadsheetApp.openById(ssId).getSheetByName(sheet2);
var data = sheet.getRange("A2:E").getValues();
var found = false;
for (var i = 0; i < data.length; i++) {
if (data[i][0] == keyword[1]) {
sendText(id, "Data found!\n" + "data1 = " + data[i][0] + "\n" + "data2 = " + data[i][1] + "\n" + "data3 = " + data[i][2] + "\n" + "data4 = " + data[i][3] + "\n" + "data5 = " + data[i][4]);
found = true;
}
}
if (!found) {
sendText(id,"data not found");
}
} else if (text.startsWith("/help")) {
sendText(id,"/input (date, hour, id, username, item[0], item[1], item[2], item[3], item[4], item[5], item[6], item[7])\n/search (keyword)");
}
}
I have tried to use openAi chat to help fix my code, but still not work.

TypeError: Cannot read property 'getResponseCode' of undefined doGet_Appraisal throws error but before it was executed properly how to resolve

It was actually a Google Sheets project, so I'm learning on the fly as best as I can, but I can't even format this code below correctly - ugh!)
The error I am receiving is this:
TypeError: Cannot read property 'getResponseCode' of undefined
doGet_Appraisal # Appraisal_Email.gs:64
And the code is below:
//1. Performing to generate the Payslip getting sheet contents
function doGet_Appraisal(range,shTabName) {
var blob,exportUrl,name,options,response,sheetTabId,ss,ssID,url_base,email,subject;
var folderId ="1o9WImzeKdzsek7DfZWD_qzFsI9yLoplz";
var milliseconds = 2000;
range = range ? range : "B1:R44";
ss = SpreadsheetApp.openById("sheet_ID");
shTabName = "Appraisal Payslip Template";
ssID = ss.getId();
sh = ss.getSheetByName(shTabName);
const body = "Dear Employee" + "\n\n" + "<p>" + "<b> Payslip for the month of January is Available </b>" + "</p>";
var htmlText = body.replace(/\n/g,'\n<br>');
var bioCode = sh.getRange('F12');
const values = [...new Set(bioCode.getDataValidation().getCriteriaValues()[0].getValues().flat())];
var first = values[0];
var number = values.length - 1;
bioCode.setValue(first);
//2. Looping the Emp_ID till null
for(i = 0;i < number;i++)
{
if (sh.getRange('F12').getValue().length > 0) {
var person = sh.getRange('F12').getValue();
sheetTabId = sh.getSheetId();
//3. Getting the Corresponfing EMail based on Emp_ID
email = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Appraisal Payslip Template').getRange('N12').getValue().toString();
Utilities.sleep(milliseconds);
url_base = ss.getUrl().replace(/edit$/,'');
name = sh.getRange("S1").getValue();
name = name + ""
subject = `${name}`;
Logger.log('email: ' + email)
Logger.log('body: ' + body)
//4. Exporting the sheet as PDF (contents)
exportUrl = url_base + 'export?exportFormat=pdf&format=pdf' +
'&gid=' + sheetTabId + '&id=' + ssID +
'&range=' + range +
'&size=A4' +
'&portrait=true' +
'&fitw=true' +
'&sheetnames=false&printtitle=false&pagenumbers=true' +
'&gridlines=false' +
'&fzr=false';
//5. Authorizing email setup OAuthToken
options = {
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken(),
}
}
//6. Getting the sheet url and export as PDF
options.muteHttpExceptions = true;
response = UrlFetchApp.fetch(exportUrl, options);
//var response2 = UrlFetchApp.fetch(exportUrl, options).getBlob();
Logger.log(response.getResponseCode())
const nextValue = values[values.indexOf(bioCode.getValue()) + 1] || values[0];
bioCode.setValue(nextValue);
}
else {const nextValue = values[values.indexOf(bioCode.getValue()) + 1] ||
values[0];
bioCode.setValue(nextValue);
}
//7. If response code is not 200 return error exporting PDF
if (response.getResponseCode() !== 200) {
console.log("Error exporting Sheet to PDF! Response Code: " + response.getResponseCode());
return;
}
blob = response.getBlob();
blob.setName(name + '.pdf')
GmailApp.sendEmail(email, subject, htmlText,
{
htmlBody: htmlText,
attachments: [{
fileName: `${name}` + ".pdf",
//content: response2.getBytes(),
mimeType: "application/pdf"
}]
});
//pdfFile = DriveApp.getFolderById(folderId).createFile(blob);
}
}
This was worked recently but now throws error
TypeError: Cannot read property 'getResponseCode' of undefined
doGet_Appraisal # Appraisal_Email.gs:64

Google Workspace email sending with Google Appscript is giving error. Message blocked Your message to has been blocked

My script was working fine with normal Gmail account but is giving error with Google Workspace account.
I have read somewhere that I need to use GmailApp in place of MailApp, I have already done that. It seems that mail is sent but after that it is blocked.
I am getting the following error:
Message blocked
Your message to xxxxxxx has been blocked.
See technical details below for more information.
LEARN MORE
The script is as below:
function exportPartAsPDF(selectedRanges,shId,regNo) {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getSheets()[shId];
var selectedRange = sheet.getRange(selectedRanges);
var pdfNameValue = sheet.getRange(regNo).getValue();
var pdfName = (pdfNameValue || "export") + ".pdf";
try {
var blob = _getAsBlob(spreadsheet.getUrl(), sheet, selectedRange);
blob.setName(pdfName);
_sendMail(blob);
} catch (e) {
console.log(e);
}
return;
}
function _getAsBlob(url, sheet, range) {
var rangeParam = "";
var sheetParam = "";
if (range) {
rangeParam =
"&r1=" +
(range.getRow() - 1) +
"&r2=" +
range.getLastRow() +
"&c1=" +
(range.getColumn() - 1) +
"&c2=" +
range.getLastColumn();
}
if (sheet) {
sheetParam = "&gid=" + sheet.getSheetId();
}
var exportUrl =
url.replace(/\/edit.*$/, "") +
"/export?exportFormat=pdf&format=pdf" +
"&size=A4" + //A3/A4/A5/B4/B5/letter/tabloid/legal/statement/executive/folio
"&portrait=true" +
'&scale=4' +
"&fitw=true" +
"&top_margin=0.5" +
"&bottom_margin=0.5" +
"&left_margin=0.5" +
"&right_margin=0.5" +
"&gridlines=false" + //true/false
"&horizontal_alignment=CENTER" + //LEFT/CENTER/RIGHT
"&vertical_alignment=TOP" + //TOP/MIDDLE/BOTTOM
"&sheetnames=false&printtitle=false" +
"&pagenum=false" +
"&gridlines=false" +
"&fzr=FALSE" +
sheetParam +
rangeParam;
Logger.log("exportUrl=" + exportUrl);
var response = UrlFetchApp.fetch(exportUrl, {
headers: {
Authorization: "Bearer " + ScriptApp.getOAuthToken(),
},
});
return response.getBlob();
}
function _sendMail(blob) {
var toEmail = "xxxxxx#gmail.com,xxxx#print.epsonconnect.com";
var htmlBody = "Payment Recieved";
var subject = "Reciept";
var options = {
attachments: blob,
htmlBody: htmlBody,
};
GmailApp.sendEmail(toEmail, subject, "html content only", options);
}

Google app script triggering twice after Google form submit

I'm having issue with my script running twice in a Google Form and the linked Sheets. I have one form submit trigger. If I delete this trigger, the script doesn't run at all on a form submit. If I add the trigger back in, it will trigger twice -- but there is only one form submittal in the sheet responses. I thought that maybe I was clicking the form submittal twice, but that doesn't seem to be it. Thanks.
Here is the code:
function myFunction(e) {
// Set values for event "e" from Response form, each number being a column in the spreadsheet. Column zero is not used - it is the timestamp
var JobNumber = e.values[1];
// Create an array from the PNEZD field in the form then find the array location for the inputted shot number
var PNEZD_field = e.values[2];
var PNEZD_array = Utilities.parseCsv(PNEZD_field);
var StructureNumber = e.values[3];
for (i = 0; i < PNEZD_array.length; i++) {
if (PNEZD_array[i][0] == StructureNumber) {
var Elevation = PNEZD_array[i][3];
var Easting = PNEZD_array[i][2];
var Northing = PNEZD_array[i][1];
}
}
// STRUCTURE TYPE AND SIZE
var StructureType = e.values[4];
var StructureSize = e.values[5];
// PIPE NUMBER 1
var PipeSize1 = e.values[6];
var PipeMaterial1 = e.values[7];
var PipeDirection1 = e.values[8];
var PipeMeasureDown1 = e.values[9];
// PIPE NUMBER 2
var PipeSize2 = e.values[10];
var PipeMaterial2 = e.values[11];
var PipeDirection2 = e.values[12];
var PipeMeasureDown2 = e.values[13];
// PIPE NUMBER 3
var PipeSize3 = e.values[14];
var PipeMaterial3 = e.values[15];
var PipeDirection3 = e.values[16];
var PipeMeasureDown3 = e.values[17];
// PIPE NUMBER 4
var PipeSize4 = e.values[18];
var PipeMaterial4 = e.values[19];
var PipeDirection4 = e.values[20];
var PipeMeasureDown4 = e.values[21];
// PIPE NUMBER 5
var PipeSize5 = e.values[22];
var PipeMaterial5 = e.values[23];
var PipeDirection5 = e.values[24];
var PipeMeasureDown5 = e.values[25];
// Calculate Invert Elevations:
var Dip1 = Elevation - PipeMeasureDown1;
var Dip2 = Elevation - PipeMeasureDown2;
var Dip3 = Elevation - PipeMeasureDown3;
var Dip4 = Elevation - PipeMeasureDown4;
var Dip5 = Elevation - PipeMeasureDown5;
// Build structure information
var firstLine = StructureType + " " + StructureSize + "\n";
var secondLine = "Rim=" + Elevation + "\n";
var thirdLine = PipeSize1 + " " + PipeMaterial1 + " I.E. " + PipeDirection1 + "=" + Dip1 + "'\n";
var fourthLine = PipeSize2 + " " + PipeMaterial2 + " I.E. " + PipeDirection2 + "=" + Dip2 + "'\n";
var fifthLine = PipeSize3 + " " + PipeMaterial3 + " I.E. " + PipeDirection3 + "=" + Dip3 + "'\n";
var sixthLine = PipeSize4 + " " + PipeMaterial4 + " I.E. " + PipeDirection4 + "=" + Dip4 + "'\n";
var seventhLine = PipeSize5 + " " + PipeMaterial5 + " I.E. " + PipeDirection5 + "=" + Dip5 + "'";
var Mtext = firstLine + secondLine + thirdLine + fourthLine + fifthLine + sixthLine + seventhLine;
var calcsheet = SpreadsheetApp.openById('-removed-').getSheetByName('Calculations');
var ss = SpreadsheetApp.getActiveSpreadsheet(); // ss is now the spreadsheet the script is associated with
var sheet = ss.getSheets()[1]; // sheets are counted starting from 0
// sheet is the second worksheet in the spreadsheet
var cell = sheet.getRange("A1");
cell.setValue(Mtext);
// Dump variable to logger log
// var dump = "Variable dumps: first the URL: " + PNEZD_fileURL + " and next the file ID alone: " + PNEZD_fileID + " and now the job number: " + JobNumber;
// Logger.log( dump );
// Logger.log(JobNumber);
// Logger.log("Data dump: " + PNEZD_field);
// Logger.log("Data dump: " + PNEZD_array[3][3]);
// Logger.log(StructureNumber + "," + Northing + "," + Easting + "," + Elevation);
}