Xero API error [ TokenInvalidAudience ] when fetching tenants list - google-apps-script

I have recently started working on Xero API using google apps script (javascript like).
From the ouath2 flow mentioned at https://developer.xero.com/documentation/oauth2/auth-flow, I have been able to get through the authentication mechanism to get access token as well as refreshing the access token.
As for the further use of API, it requires fetching tenant-id.
Now, when I try to get the data via the endpoint:
https://api.xero.com/connections
it returns an error code 401 with following details:
{"Type":null,"Title":"Unauthorized","Status":401,"Detail":"TokenInvalidAudience: 6CBC9B1478974A5CBF7229AB1D32....","Instance":"a2e2c373-0c73-4ecb-85cf-1b21f828...","Extensions":{}}
Can anyone please guide as to what could be the issue? and how to resolve?
Thanks.
For easy reference, i am using the following code:
function myFunction()
{
var url = "https://api.xero.com/connections";
var accToken = fetchKey(ACCESS_TOKEN_KEY);
var hdr = {
"Authorization" : "Bearer " + accToken
};
var params = {
headers : hdr,
method : "get",
muteHttpExceptions : true
};
var resp = UrlFetchApp.fetch(url, params);
return resp.getContentText();
}

After configuring another app and restarting from scratch, it seems my own mistake that I assumed {id_token} same as being access token while it is different but I missed it initially in the long token sequence.
Thank you for your time!

Related

create an invoice stripe using google app script

Hi I followed this documentation regarding on creating an invoice on stripe
it says here that The ID of the customer who will be billed.
so I created a test customer manually on stripe and now I tried this code
function testDataInvoices()
{
var url = "https://api.stripe.com/v1/invoices";
var params = {
method: "post",
headers: {Authorization: "Basic " + Utilities.base64Encode("sk_testXXXXX:")},
payload:
{
customer: "cus_JLKM93Pc6j2mxB",
}
};
var res = UrlFetchApp.fetch(url, params);
Logger.log(res.getContentText())
}
but I am getting this error
Exception: Request failed for https://api.stripe.com returned code 400. Truncated server response: {
"error": {
"code": "invoice_no_customer_line_items",
Can someone please enlighten me about this stripe I really just new to this API and their API is just only for node.js
#TRIED
I tried following this link
and change my code to this
function testDataInvoices()
{
var url = "https://api.stripe.com/v1/invoices";
var params = {
method: "post",
headers: {Authorization: "Basic " + Utilities.base64Encode("sk_test_XXXXXX:")},
payload:
{
"email": "paul.kevin#senren.page",
"payment_settings": 'pm_1FWS6ZClCIKljWvsVCvkdyWg',
"invoice_settings[default_payment_method]":'pm_1FWS6ZClCIKljWvsVCvkdyWg'
}
};
var res = UrlFetchApp.fetch(url, params);
Logger.log(res.getContentText())
}
and got this error
A few things:
I'm not familiar with Google App Scripts — are you sure it's safe to store a secret key in such a script and you don't need an actual backend server instead? That key has to be kept private since it can be used to do anything on your Stripe account.
In the first case, you get an error because to issue an Invoice, you have to first call /v1/invoiceitems to add some items to the customer. Then when you call /v1/invoices, which will pull those in to charge for them: https://stripe.com/docs/invoicing/integration#create-invoice-code
in the second case you get an error because those are not valid parameters for that endpoint(if you check the link you posted, those parameters are for /v1/customers, not /v1/invoices).
I'd suggest following https://stripe.com/docs/invoicing/integration .

Google Apps Script UrlFetchApp GET to a node.js endpoint results in an empty body

I am using Google Apps Script to make a UrlFetchApp call to a node.js API endpoint. I own both the Google Apps Script code and node.js code & endpoint, so I can watch what is happening.
When I use Postman, it works. But when I use GAS UrlFetchApp, it doesn't work, as req.body in node is empty { }. I even looked at the code that Postman creates for JavaScript Fetch and try to nearly duplicate it, except for things I know GAS' UrlFetchApp needs. I've done quite a few UrlFetchApp calls with GAS to various external endpoints. So, I'm not new, but can't figure this one out.
Here is my Google Apps Script code:
var url = 'https://xxxxxxxxxxx.azurewebsites.net/api/account';
var data = {
"email": address,
"apiKey": 'xxxxxxxxxxxxxxxxxxx'
}
var payLoadInfo = JSON.stringify(data);
var options = {
"method": "GET",
"headers": {'Content-Type': 'application/json'},
// 'Content-Type': 'application/json',
// "contentType": 'application/json',
redirect: 'follow',
"muteHttpExceptions": true,
"body": payLoadInfo,
// "payload": JSON.stringify(data)
// payload: payLoadInfo
};
var response = UrlFetchApp.fetch(url, options);
The commented out parts of "options" are several different ways I've tried to get it to work. I know in the past that I've usually used "payload" instead of "body" like I am this time (Postman suggested it). When I use "payload", it fails completely, not even getting to my node code. I also tried putting the apiKey in the header.
Here is the node.js API endpoint code:
router.get("/account", async (req, res) => {
var apiKey = process.env.API_KEY;
console.log('apiKey = ' + apiKey);
console.log('req.body = ' + JSON.stringify(req.body, null, 2));
console.log('req.body.apiKey = ' + req.body.apiKey);
if (req.body.apiKey != apiKey) {
console.log('apiKey is not equal');
res.status(401).send({ error: "You are not authorized." });
} else {
process the request...
}
When I use "payload" in "options" I get a 500, and the server code never executes.
When I use "body" in "options", I see the server code execute, but the console.log('req.body = ' + JSON.stringify(req.body, null, 2)), just comes back with an empty object {}, and then since req.body.apiKey != apiKey, it consoles "apiKey is not equal" and sends a 401. When using Postman, the req.body object consoles fine, showing the email & apiKey.
No matter what combinations of things I put into options, it fails either with 500 or 401. However, Postman works great with pretty much the same parameters, headers, etc.
Here is what Postman shows for the code:
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Cookie", "ARRAffinity=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; ARRAffinitySameSite=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
var raw = JSON.stringify({"email":"xxxxxxxxxxxxxxxxx#gmail.com","apiKey":"xxxxxxxxxxxxxxxx"});
var requestOptions = {
method: 'GET',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
I even tried including the cookie and "redirect: follow", but none works.
What am I missing?
I got it to work, thanks to help from #Tanaike (see comments above).
It seems that unlike normal "fetch" in node.js, URLFetchApp in Google Apps Script will not send a body along with a GET.
I still used GET, but changed to sending the param in the URL and the apiKey in the header.

Lightspeed Retail API call Google App Script, invalid argument error

I have a Google App Script that checks purchases in a Lightspeed account, see code below. The code is working correctly, except for the last parameter in the URL (&or=voided%3Dtrue|completed%3Dtrue). When I use these exact parameters in Postman everything is working correctly, I only get sales that are voided (cancelled) or completed. In GAS, I get the following error back from the Lightspeed api:
{"httpCode":"400","httpMessage":"Bad Request","message":"findAllByFieldMatch got search for "voided=true". Did you mean: voided?","errorClass":"Exception"}
When I try different options to URL encode these parameters I get an invalid argument error from GAS. I'm thoroughly confused here, does anyone know what I'm doing wrong in GAS?
//Call the Lightspeed API
const accessToken = auth();
const urlRaw = `https://api.lightspeedapp.com/API/Account/${accountid}/Sale.json?load_relations=["Customer.Contact", "SalePayments.PaymentType"]&updateTime=>,${timestamp}&or=voided%3Dtrue|completed%3Dtrue`
let url = encodeURI(urlRaw);
const payload = {
"method": "GET",
"headers": {
"Authorization": "Bearer " + accessToken
},
};
const dataRaw = UrlFetchApp.fetch(url, payload);
const data = JSON.parse(dataRaw);

Apps Script - UrlFetchApp.fetch {url, method: "GET"} to a gzip gets failed with code 406

I'm on a multi months quest here. Any help would be much appreciated!
I'm trying to connect via API on App Store Connect using UrlFetchApp.fetch(). After having struggled in the desert to generate the correct JWT for authentification on GAS, I now face harsh reality: the server answer (content-type and content-encoding) is not JSON but GZIP.
Code 406 message:
"Truncated server response: The provided Accept header is not supported for this request. Requested: application/json Allowed: application/a-gzip"
Is there a way to still get access to the file?
Here is the part of the code doing the call only (JWT signature code for authentification is above -> "sJWT")
var url = "https://api.appstoreconnect.apple.com/v1/financeReports?filter[regionCode]=ZZ&filter[reportDate]=2019-11&filter[reportType]=FINANCIAL&filter[vendorNumber]=xxx"
var response = UrlFetchApp.fetch(url, { method : "GET", headers : { "Authorization" : "Bearer "+sJWT }});
Many thanks!
How about this answer? Please think of this as just one of several possible answers.
Modification points:
When I saw the official document, it is required to use application/a-gzip for Accept in the request headers.
And also, in this case, the response returns the content of gzip. So it is required to decompress the content.
When above points are reflected to your script, how about the following modification?
Modified script:
var url = "https://api.appstoreconnect.apple.com/v1/financeReports?filter[regionCode]=ZZ&filter[reportDate]=2019-11&filter[reportType]=FINANCIAL&filter[vendorNumber]=xxx"
var response = UrlFetchApp.fetch(url, {
method: "GET",
headers: {
"Authorization": "Bearer " + sJWT,
"Accept": "application/a-gzip" // Added
}
});
var res = Utilities.ungzip(response.getBlob()); // Added
Note:
Above modified script supposes that the values of your URL and sJWT are correct for using the API.
References:
Download Finance Reports
fetch(url, params)
ungzip(blob)
406 Not Acceptable
Unfortunately, I cannot test above script. I apologize for this. So if above modified script didn't resolve your issue, I apologize.
Try changing the blob content type to "application/x-gzip":
var url = "https://api.appstoreconnect.apple.com/v1/financeReports?filter[regionCode]=ZZ&filter[reportDate]=2019-11&filter[reportType]=FINANCIAL&filter[vendorNumber]=xxx"
var response = UrlFetchApp.fetch(url, {
method: "GET",
headers: {
"Authorization": "Bearer " + sJWT,
"Accept": "application/a-gzip"
}
});
var res = Utilities.ungzip(response.getBlob().setContentType("application/x-gzip")); // Changed

Correct syntax for Google API PATCH request using UrlFetchApp for HTTPS Request

I'm trying to use UrlFetchApp.fetch(url) method in Apps Script to PATCH a groups resource using the Google Groups Settings API.
The code below allows me to GET the groups properties, but I'm unable to figure out the syntax for a PATCH request.
function doSomething (accessToken) {
var options = {
method: "GET",
headers: {
authorization: "Bearer " + accessToken
},
};
var result = UrlFetchApp.fetch("https://www.googleapis.com/groups/v1/groups/test_group_5#student.vis.ac.at", options);
return HtmlService.createHtmlOutput (result.getContentText());
}
A PATCH request needs a Header Override. You actually need to use a PUT request, and then override it to a PATCH request.
var payload = "{\"" + PropertyOne + "\":\"" + "Proptery Value" + "\"}";
Logger.log('payload: ' + payload);
var options = {"method" : "put", "headers": {"X-HTTP-Method-Override": "PATCH"}, "payload" : payload};
if (payload.length > 2) {
UrlFetchApp.fetch("https://www.googleapis.com/groups/v1/groups/test_group_5#student.vis.ac.at", options );
};
The code above won't be exactly what you want, and might not be error free, but the structure of it should be what you need. I'm sure the payload isn't configured correctly, because I don't know what the format is. It looks like the documentation calls it Patch body with an object.
Google Documentation - Group Settings API Patch
Key words: "Apps Script", patch