POST requests with the Coinbase Pro API using Google Apps Script - google-apps-script

I'm working with the Coinbase Pro API and while it's working well for GET requests I'm getting an "Invalid signature" when I try a POST request to place an order. I suspect that this may be something related to the "body" of the message since this is the only difference with the GET where "body" is empty.
I'm wondering if someone can help me on this.
function SellMarket (product,amount) {
var requestPath = '/orders';
var method = 'POST';
var body = JSON.stringify({
"type": "market",
"side": "buy",
"product_id": product,
//"size": amount,
'size': '1.0'
});
Logger.log('Sell - Body = '+body);
var responseJson = SignAndCallAPI(method, requestPath, body);
Logger.log('Sell Executed = '+responseJson);
}
function SignAndCallAPI(method, requestPath, body) {
var timestamp = Math.floor(Date.now() / 1000).toString();
var what = Utilities.base64Decode(Utilities.base64Encode(timestamp + method + requestPath + body));
var decodedsecret = Utilities.base64Decode(globalvars_CB.secret);
var hmac = Utilities.base64Encode(Utilities.computeHmacSha256Signature(what, decodedsecret));
var options = {
'method' : method,
'muteHttpExceptions' : true,
'headers' : {
'Content-Type': 'application/json',
'CB-ACCESS-KEY' : globalvars_CB.apikey,
'CB-ACCESS-SIGN' : hmac,
'CB-ACCESS-TIMESTAMP' : timestamp,
'CB-ACCESS-PASSPHRASE' : globalvars_CB.passphrase,
}
}
var responseJson = UrlFetchApp.fetch(globalvars_CB.uri+requestPath, options);
return(responseJson);
}

Untested suggestion, I recommend changing this bit in your code (in SignAndCallAPI function):
var options = {
'method' : method,
'muteHttpExceptions' : true,
'headers' : {
'Content-Type': 'application/json',
'CB-ACCESS-KEY' : globalvars_CB.apikey,
'CB-ACCESS-SIGN' : hmac,
'CB-ACCESS-TIMESTAMP' : timestamp,
'CB-ACCESS-PASSPHRASE' : globalvars_CB.passphrase,
}
}
to:
const options = {
method: method,
payload: body,
contentType: 'application/json',
muteHttpExceptions: true,
headers: {
'CB-ACCESS-KEY': globalvars_CB.apikey,
'CB-ACCESS-SIGN': hmac,
'CB-ACCESS-TIMESTAMP': timestamp,
'CB-ACCESS-PASSPHRASE': globalvars_CB.passphrase,
},
};
due to the reasons below:
Although you pass body to SignAndCallAPI function and use body in HMAC computation, you don't appear to actually include body in the POST request sent to the server. I was expecting to see it as the value to a payload property of your options object.
UrlFetchApp documentation (https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app#fetchurl,-params) seems to indicate that Content-Type should be specified in the contentType property of your options object (as opposed to explicitly including a Content-Type header).
I've not looked at the Coinbase API documentation in detail (https://docs.pro.coinbase.com/#place-a-new-order), but body seems okay. You'll also want to make sure the HMAC computation for the CB-ACCESS-SIGN request header is as described in the documentation (https://docs.pro.coinbase.com/#signing-a-message).

Related

can't using post on google script

hi iam trying to make post using google script , but it does not work,
original site here: https://billing.te.eg/ar-eg , it like you inquiry with code (ex:055) and number (ex:3020100) and it return bill
I try this before using python and it work but it don't work on google script
here is the code working using python:
import json
import requests
url = "https://billing.te.eg/api/Account/Inquiry"
data = {
"AreaCode": "055", # <-- change this
"PhoneNumber": "3020100", # <-- change this
"PinCode": "",
"InquiryBy": "telephone",
"AccountNo": "",
}
with requests.session() as s:
# load cookies
s.get("https://billing.te.eg/ar-eg", verify=False)
resp = s.post(url, data=data, verify=False).json()
print(json.dumps(resp, indent=4))
and this is the code i trying in google script but didn't work :
function landBill() {
var url = "https://billing.te.eg/api/Account/Inquiry"
var data = {"AreaCode": "055", "PhoneNumber": "3020105", "PinCode": "", "InquiryBy": "telephone",
"AccountNo": "", };
var options = {
'method' : 'post',
'contentType': 'application/x-www-form-urlencoded; charset=UTF-8',
// Convert the JavaScript object to a JSON string.
'payload' : JSON.stringify(data),
'muteHttpExceptions': true,
};
var res = UrlFetchApp.fetch(url,options)
var cont = res.getContentText()
Logger.log(cont)
}
On the python code you are doing 2 requests, a GET and a POST
Since they are within a Session object cookies returned by the website are being automatically managed.
That concept however does not exist in built-in libraries of Apps Script, given that it has to be done manually.
Sample Code:
function myFunction() {
// =================== GET ===================
//initiating a request object for the first GET request
var request1 = {
'url': 'https://billing.te.eg/ar-eg',
'method' : 'get',
'muteHttpExceptions': true,
'validateHttpsCertificates': false,
};
// Adding ('validateHttpsCertificates': false,) since it is the equivalent of (verify=false) in the python code
var resGet = UrlFetchApp.fetchAll([request1]); //storing the response to digest cookies
// Digesting the cookies returned by the server (excluding cookies attributes) and adding them to an array to be reused in the POST
var cookies = [];
for (response of resGet) {
var headers = response.getAllHeaders();
for (cookie of headers['Set-Cookie']){
cookies.push(cookie.split(";")[0]);
}
}
// =================== POST ===================
// declaring the payload for the POST
var postData = {
"AreaCode": "055",
"PhoneNumber": "3020100",
"PinCode": "",
"InquiryBy": "telephone",
"AccountNo": "",
};
//Initiating a request object for the POST request and injecting the stored cookies into the HTTP headers
var request2 = {
'url': "https://billing.te.eg/api/Account/Inquiry",
'method' : 'post',
'contentType': 'application/x-www-form-urlencoded',
'muteHttpExceptions': true,
'validateHttpsCertificates': false,
'headers' : {
'Cookie': (cookies.join("; ") + ";"),
},
'payload' : postData,
};
//Storing the POST response
var resPost = UrlFetchApp.fetchAll([request2]);
//Printing POST response content to console
console.log(resPost[0].getContentText());
}

Put request via urlfetchapp produces "Exception: Attribute provided with invalid value: Header:Content-Length"

Goal is to update "OrderStatusID" of order to "1" using urlfetchapp with a put request via Apps Script.
function updateOrderStatus(){
var private_key = "{private_key}";
var merchant_token = "{merchant_token}";
var secure_url = "{secure_url}";
var body = JSON.stringify({"OrderStatusID": "1"});
var url ="https://apirest.3dcart.com/3dCartWebAPI/v2/Orders/{orderID}";
var options = {
"method" : "put",
"headers" : {
"Content-Type" : "application/json",
"Content-Length" : body.length,
"Accept" : 'application/json',
"SecureURL" : secure_url,
"PrivateKey" : private_key,
"Token" : merchant_token
},
"body" : body,
"muteHttpExceptions" : false,
}
try{
var response = UrlFetchApp.fetch(url, options);
}
catch(err){
Logger.log(err);
}
finally{
Logger.log(response);
}
}
Code throws error Exception: Attribute provided with invalid value: Header:Content-Length
Code altered to remove sensitive information.
Changing the name of the option "body" to "payload" resolved the issue.
The answer was found in this google issue tracker thread.
ek...#googlers.comek...#googlers.com #6Apr 11, 2016 07:03AM Status:
Won't Fix (Not Reproducible) UrlFetchApp doesn't have an advanced
parameter called 'contentLength'. The Content-Length header is
automatically calculated based on the length of the payload passed in.
The 'contentLength' advanced parameter you set in your sample is
simply ignored by the backend and the length automatically calculated.
The Content-Length is automatically calculated from the payload. I thought it was supposed to be named body because the 3dCart API documentation was using body as the name of its json in the example.
Corrected Code:
function updateOrderStatus(){
var private_key = "{private_key}";
var merchant_token = "{merchant_token}";
var secure_url = "{secure_url}";
var body = JSON.stringify({"OrderStatusID": "1"});
var url ="https://apirest.3dcart.com/3dCartWebAPI/v2/Orders/{orderID}";
var options = {
"method" : "put",
"headers" : {
"Content-Type" : "application/json",
//"Content-Length" : body.length,
"Accept" : 'application/json',
"SecureURL" : secure_url,
"PrivateKey" : private_key,
"Token" : merchant_token
},
"payload" : body,
"muteHttpExceptions" : false,
}
try{
var response = UrlFetchApp.fetch(url, options);
}
catch(err){
Logger.log(err);
}
finally{
Logger.log(response);
}
}

How to fix Invalid JSON payload error from the Google Ads API

I'm trying to pull a report from the Google Ads API into Google sheets and I can't get the API to recognize my query as a query
Here's the code and error I'm getting:
function basicReport() {
var query = {
"query" : "SELECT campaign.name, campaign.status FROM campaign ORDER BY campaign.id"
};
var body = JSON.stringify(query);
var head = {
'Developer-token' : "<Dev token>",
'login-customer-id' : <Manager ID>,
'Authorization' : "Bearer <Auth token>",
};
var options = {
'method' : 'POST',
'content-type': 'application/json',
'headers' : head,
'payload' : body,
'muteHttpExceptions' : true
};
var response = UrlFetchApp.fetch('https://googleads.googleapis.com/v4/customers/<Customer ID>/googleAds:searchStream', options);
var json = response.getContentText();
var data = JSON.parse(json);
But I constantly get the error:
"error": {
"code": 400,
"message": "Invalid JSON payload received. Unknown name \"{\"query\":\"SELECT campaign.name, campaign.status FROM campaign ORDER BY campaign.id\"}\": Cannot bind query parameter. Field '{\"query\":\"SELECT campaign' could not be found in request message.",
"status": "INVALID_ARGUMENT",
"details": [
{
"#type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"description": "Invalid JSON payload received. Unknown name \"{\"query\":\"SELECT campaign.name, campaign.status FROM campaign ORDER BY campaign.id\"}\": Cannot bind query parameter. Field '{\"query\":\"SELECT campaign' could not be found in request message."
I've run the query in OAuth playground (https://developers.google.com/oauthplayground) and it worked there, so I know the query is ok.
I've tried passing the body as an object not a string, but then I get a 500 error.
In case anyone else is looking for this - I did solve it.
When set in the header, Content-Type has a dash, when set in the options, contentType does not.
function basicReport() {
var query = {
"query" : "SELECT campaign.name, campaign.status FROM campaign ORDER BY campaign.id"
};
var body = JSON.stringify(query);
var head = {
'developer-token' : "<Dev token>",
'login-customer-id' : "<Manager ID>",
'authorization' : "Bearer <Auth token>",
'accept' : 'application/json'
'Content-Type': 'application/json',
};
var options = {
'method' : 'POST',
'headers' : head,
'payload' : body,
'muteHttpExceptions' : true
};
var response = UrlFetchApp.fetch('https://googleads.googleapis.com/v4/customers/<Customer ID>/googleAds:searchStream', options);
var json = response.getContentText();
var data = JSON.parse(json);
Logger.log(data);

Slack API call to postMessage not working

I'm just trying to make a simple postMessage call from a google apps script with an image attached, but I get the following response:
"{"ok":false,"error":"invalid_arg_name"}"
Here is the function that creates the payload:
function getPostMessagePayload(fileUrl) {
var content = {
"channel":"#data-vis",
"token": ACCESS_TOKEN,
"text":"Chart update:",
"attachments": [
{
"title": "Chart",
"fallback": "Fallback",
"text": "Testing chart",
"image_url": fileUrl
}
]
};
return content;
}
And here is where I make the request:
var POST_MESSAGE_ENDPOINT = 'https://slack.com/api/chat.postMessage';
function performPostMessage(payload) {
var res = UrlFetchApp.fetch(
POST_MESSAGE_ENDPOINT,
{
method: "post",
payload: JSON.stringify(payload),
muteHttpExceptions: true,
}).getContentText();
return res;
}
It's impossible to tell what the actual problem is. I've tried making my token obviously incorrect, the URL obviously incorrect, and deleting/adding random args and it gives the same response every time.
When I use the webhook to do this rather than the API, it works fine.
My app has the following permissions in Slack:
chat:write:bot
incoming-webhook
Problem
You are sending a JSON object as payload with your POST request, whilst the contentType parameter of the fetch() method is defaulted to application/x-www-form-urlencoded.
Solution 1
In addition to JSON.stringify(), to ensure the payload is sent correctly, wrap it in an encodeURIComponent() built-in function. If the issue persists, continue to solution 2.
Update to solution 1
Nearly forgot how fetch() method treats objects passed to payload with default x-www-form-urlencoded content type. Remove the JSON.stringify() entirely (and add encodeURI() / encodeURIComponent() if needed).
Solution 2
Slack API supports application/json content type of POST requests. In your case it might be easier to send the request with contentType parameter set to application.json (note that you will have to move authorization from payload to headers):
//fetch part;
var res = UrlFetchApp.fetch(
POST_MESSAGE_ENDPOINT,
{
method : 'post',
contentType : 'application/json',
headers : {
Authorization : 'Bearer ' + ACCESS_TOKEN
},
payload : JSON.stringify(payload),
muteHttpExceptions : true,
})
//payload part;
var payload = {
"channel" : "#data-vis",
"text" : "Chart update:",
"attachments" : [
{
"title" : "Chart",
"fallback" : "Fallback",
"text" : "Testing chart",
"image_url" : fileUrl
}
]
};
Useful links
fetch() method reference;
postMessage method reference (Slack API);

How to send a JSON payload with UrlFetchApp service?

I'm trying to POST to a web service that is expecting to get JSON as payload using Google Apps Script. I'm using the following code:
var options =
{
"method" : "post",
"contentType" : "application/json",
"headers" : {
"Authorization" : "Basic <Base64 of user:password>"
},
"payload" : { "endDate": "2012-06-03" }
};
var response = UrlFetchApp.fetch("http://www.example.com/service/expecting/json", options);
On the server side I'm getting the following error:
WARN [facade.SettingsServlet] 04 Jun 2012 15:30:26 - Unable to parse request body: endDate=2012-06-03
net.liftweb.json.JsonParser$ParseException: unknown token e
I'm assuming that the server is expecting to get
{ "endDate": "2012-06-03" }
instead of
endDate=2012-06-03
but I don't know how to make the UrlFetchApp do it.
I do not understand the server side error but the 'payload' parameter must be a string as specified here: https://developers.google.com/apps-script/class_urlfetchapp?hl=fr-FR#fetch.
try:
var options =
{
"method" : "post",
"contentType" : "application/json",
"headers" : {
"Authorization" : "Basic <Base64 of user:password>"
},
"payload" : '{ "endDate": "2012-06-03" }'
};
If you set payload as a String, it will be passed directly (as a
UTF-8 string).
If you set payload as an Object, it will be sent like
an HTML form (which means either 'application/x-www-form-urlencoded' if the
fields are simple, or 'multipart/form-data' if the Object includes a
blob/file).
For your use case (the server is expecting to receive JSON), it sounds like Utilities.jsonStringify() is the way to go.
Something like this worked for me in a similar situation:
Instead of creating payload and adding to options, I built the parameters into a string to append to the URL:
var params = "id=2179853&price=62755";
then, I appended params to the url string:
var result = UrlFetchApp.getRequest(url + '?' + params, options);
So, my options was passed containing only the header.
For my project, this worked like a charm.
Here goes the code that should work with some important comments:
function testMe() {
var products_authkey = "------------";
try {
var url = "https://app.ecwid.com/api/v1/---------/product?id=----------&secure_auth_key=" + products_authkey;
//url= "http://requestb.in/----------"; // you can actually debug what you send out with PUTs or POSTs using Requestb.in service
var payload = {
id: "21798583", // id is necessary and should be a string, or it might be sent in scientific representation (with E)
price: 62755
};
payload = JSON.stringify(payload); // the payload needs to be sent as a string, so we need this
var options = {
method: "put",
contentType: "application/json", // contentType property was mistyped as ContentType - case matters
payload: payload
};
var result = UrlFetchApp.getRequest(url, options);
Logger.log(result) // a better way to debug
var result = UrlFetchApp.fetch(url, options); // works perfectly in my case
Logger.log(result)
} catch (e) {
Logger.log(e)
}
}