url encoding php data into json object - javascript

Please read - this isn't just simply doing json_encode from php to javascript.
Our billing system uses Authorize.net. Using the Authorize.net API, in PHP we create a token request. The data in the request passes customer info, balance, billing address, etc - that data is sent directly with PHP. In response we get a token to be processed in PHP - and then that token is embedded into the HTML form input.
After the credit card form is submitted, we get a javascript json response back to the form to be processed by other JS functions. All works fine until we have a customer with the & in their company name (IE: Bar & Grill)
The & triggers an error only in the json response back to the form - specifically the error we get is: unexpected end of json input which causes the rest of the scripts to error out.
So, the issue is, does the customer data in the PHP token request need to be urlencoded - or is there a special way to handle the json response? From what I can tell, Authorize simply returns the exact customer data in the json response - so if we url encode it on the front end (before the token request is sent), then does that mean we also need to url decode the json response.
Its kind of a chicken and an egg which came first problem.
Authorize.net Token Request (in PHP):
$customerData = new AnetAPI\CustomerDataType();
$customerData->setType("business");
$customerData->setId($ss['authCustID']);
$customerData->setEmail($ii['cEmail']);
// Set the Bill To info for new payment type
$billTo = new AnetAPI\CustomerAddressType();
$billTo->setFirstName($ii['cFirstName']);
$billTo->setLastName($ii['cLastName']);
$billTo->setCompany($ii['cName']); // #1 <----- IE: "Bar & Grill"
$billTo->setAddress($ii['cAddress']. " " .$ii['cAddress1']);
$billTo->setCity($ii['cCity']);
$billTo->setState($ii['cState']);
$billTo->setZip($ii['cZip']);
$billTo->setCountry("USA");
// set shipping profile
$shipTo = clone $billTo ;
// extend billTop profile
$billTo->setFaxNumber('8005551212') ;
$billTo->setPhoneNumber($ii['cPhone']);
//create a transaction
$transactionRequestType = new AnetAPI\TransactionRequestType();
$transactionRequestType->setTransactionType("authCaptureTransaction");
$transactionRequestType->setAmount($ss['balance']);
$transactionRequestType->setCustomer($customerData) ;
$transactionRequestType->setOrder($order) ;
$transactionRequestType->setBillTo($billTo) ;
$transactionRequestType->setShipTo($shipTo) ;
// Build transaction request
$request = new AnetAPI\GetHostedPaymentPageRequest();
$request->setMerchantAuthentication($merchantAuthentication);
$request->setRefId($refId);
$request->setTransactionRequest($transactionRequestType);
//execute request
$controller = new AnetController\GetHostedPaymentPageController($request);
$response = $controller->executeWithApiResponse(\net\authorize\api\constants\ANetEnvironment::PRODUCTION); // SANDBOX or PRODUCTION
$gToken=[] ;
if (($response != null) && ($response->getMessages()->getResultCode() == "Ok")) {
//echo $response->getToken()."\n";
$gToken["Error"] = 0 ;
$gToken["Token"] = $response->getToken() ;
} else {
//echo "ERROR : Failed to get hosted payment page token\n";
$errorMessages = $response->getMessages()->getMessage();
//echo "RESPONSE : " . $errorMessages[0]->getCode() . " " .$errorMessages[0]->getText() . "\n";
$gToken["Error"] = 1 ;
$gToken["errMsg"] = $errorMessages[0]->getCode() . ": " .$errorMessages[0]->getText() ;
}
Form/JSON response handler:
AuthorizeNetPopup.onReceiveCommunication = function (querystr) {
var params = parseQueryString(querystr);
//console.log(params) ;
switch (params["action"]) {
case "successfulSave":
AuthorizeNetPopup.closePopup();
break;
case "cancel":
AuthorizeNetPopup.closePopup();
break;
case "transactResponse":
// 'response' is a string value
// encode it as an object, to be passed to global function
// that will decode it again for PHP
console.log(params["response"]) ;
var response = JSON.parse(params["response"]); // #2 <--- ERROR: unexpected end of json input
//var response = params["response"];
httpReceipt(response) ;
AuthorizeNetPopup.closePopup();
break;
case "resizeWindow":
var w = parseInt(params["width"]);
var h = parseInt(params["height"]);
var ifrm = document.getElementById("iframeAuthorizeNet");
ifrm.style.width = w.toString() + "px";
ifrm.style.height = h.toString() + "px";
centerPopup();
break;
}
};
Do I url encode data at #1 (in php section) or do I url encode the json response before processing it (at #2) - or both? Very confused on how to handle this. Do we need to encode the data of eachparameter being added to the token request - or can we just encode the entire request before its submitted? Regardless of which end of the communication gets encoding/decoding applied, what are the proper encoding/decoding calls?
UPDATE
To illustrate flow:
paymentPage.html ->PHP generates token, embeds token into page form, also has iframe for paymentPageFrame.html
paymentPageFrame.html -> iFrame communicator page, relays msgs between paymentPage.html and authorize.net
paymentPage.html -> javascript onReceiveCommunication to process messages coming from paymentPageFrame.html
Turns out that authorize.net is returning a URL string to paymentPageFrame.html - the string is not urlencoded. The string is then getting passed back to the parent onReceiveCommunication at which point its being parsed with a custom parser:
function parseQueryString(str) {
var vars = [];
var arr = str.split('&');
var pair;
for (var i = 0; i < arr.length; i++) {
pair = arr[i].split('=');
vars.push(pair[0]);
vars[pair[0]] = unescape(pair[1]);
}
return vars;
}
This custom parser is then causing a data value that has the & in it (IE: "company" : "Bar & Grill") to split the name in half ultimately leading to the invalid/unexpected end of json input.
The string being passsed back to the iFrame, which in turn gets passed to the parent is:
action=transactResponse&response={"accountType":"Visa","accountNumber":"XXXX0623","transId":"62830720474","responseCode":"1","authorization":"185778","shipTo":{"firstName":"Bob","lastName":"Jones","company":"Backyard Bar & Grill","address":"123 Main St ","city":"Tampa","state":"FL","zip":"33606","country":"USA"},"orderDescription":"2020-12-01 bill for All Regions/All Sites","customerId":"1002-0","totalAmount":"1.50","orderInvoiceNumber":"11386-0","dateTime":"2/2/2021 4:57:53 PM","refId":"ref1612285039"}
Now that the string is in the parent page, I am trying to figure the best way to encode it then parse it so it won't break on parsing when a data value has & in it (IE: Backyard Bar & Grill)
So far I am trying, without sucess:
var urlstr = encodeURIComponent(response) ; // encode it first
var newUrl = new URLSearchParams(urlstr) ; // then proper parse it
But when I try to access the parameters, it returns null:
consoole.log(newUrl.get('action')) ;
> null

Your problem is that you split on the & characters which is also inside the JSON response. For a quick solution I suggest something like:
var str = 'action=transactResponse&response={"accountType":"Visa","accountNumber":"XXXX0623","transId":"62830720474","responseCode":"1","authorization":"185778","shipTo":{"firstName":"Bob","lastName":"Jones","company":"Backyard Bar & Grill","address":"123 Main St ","city":"Tampa","state":"FL","zip":"33606","country":"USA"},"orderDescription":"2020-12-01 bill for All Regions/All Sites","customerId":"1002-0","totalAmount":"1.50","orderInvoiceNumber":"11386-0","dateTime":"2/2/2021 4:57:53 PM","refId":"ref1612285039"}';
var response = JSON.parse(str.replace('action=transactResponse&response=', ''));
console.log(response);
console.log(response.shipTo.company);
JS fiddle: https://jsfiddle.net/vx9zr1b7/
This just replaces away the first parameter and the name of the second, so only the value of the second (and thereby the value of "response") will remain. Note that this only works under the assumption that the response is always in this very same format, and does not have any additional parameters in the response.
I suggest you put it inside your case "transactResponse": block.
This will parse you the JSON:
{
accountNumber: "XXXX0623",
accountType: "Visa",
authorization: "185778",
customerId: "1002-0",
dateTime: "2/2/2021 4:57:53 PM",
orderDescription: "2020-12-01 bill for All Regions/All Sites",
orderInvoiceNumber: "11386-0",
refId: "ref1612285039",
responseCode: "1",
shipTo: {
address: "123 Main St ",
city: "Tampa",
company: "Backyard Bar & Grill",
country: "USA",
firstName: "Bob",
lastName: "Jones",
state: "FL",
zip: "33606"
},
totalAmount: "1.50",
transId: "62830720474"
}
Note that the & became an & in the process of parsing the JSON. Therefore, if you are doing anything else with it than just displaying it in the browser, I suggest you html_entity_decode() the value on the PHP side.

After much tinkering around, i finally wrote my own parser that:
A: if exists, capture the response JSON first
B: then parse the remainder of the URL params:
C: return all params and response object as a single object
var str = 'action=transactResponse&response={"accountType":"Visa","accountNumber":"XXXX0623","transId":"62830720474","responseCode":"1","authorization":"185778","shipTo":{"firstName":"Preston","lastName":"Rolinger","company":"Backyard & Grill","address":"2808 W Foster Ave S ","city":"Tampa","state":"FL","zip":"33611","country":"USA"},"orderDescription":"2020-12-01 bill for All Regions/All Sites","customerId":"1002-0","totalAmount":"1.50","orderInvoiceNumber":"11386-0","dateTime":"2/2/2021 4:57:53 PM","refId":"ref1612285039"}&moreKey="other value"' ;
function parseResponse(str) {
var resInfo = [], res = {} ;
if (str.match("&response=")) {
var params = str.split("&response=") ; // leading params & response obj
if (params[1].match("}&")) {
resInfo = params[1].split("}&") ; // splits off anything at end of obj
res.response = JSON.parse(resInfo[0] + "}") ; // add } to complete obj string again
} else {
res.response = JSON.parse(params[1]) ; // else it already is the json string
}
if (resInfo[1]) {
params = params[0]+ "&" +resInfo[1] ; // join url params back together
} else {
params = params[0] ;
}
} else {
params = str ; // else the str has no "response" in it, process just params
}
params = new URLSearchParams(encodeURI(params)) ; //encode then parse
params.forEach(function(value, key) {
res[key] = value ;
}) ;
return res ;
}
var response = parseResponse(str) ;
console.log(response) ;

Related

MWS Post Request with Google Scripts

I am trying to make a post request through google scripts to amazon to collect information.
We are trying to get our orders to MWS and transfer them to sheets automatically.
I got to the last step which is signing the request.
A few things I wasnt sure about:
They say we use the secret key for hashing,I only see a client secret
and an access key id, which do I use?
Do I add the URL as part of whats getting signed? On the MWS Scratch pad they add this as shown:
POST
mws.amazonservices.com
/Orders/2013-09-01
Does it have to be on separate lines does it need post and the rest of the stuff. Its a little unclear.?
I read online that the sha256 byte code gets base64 encoded, not the string literal, is that true?
I tried to hash the string that amazon gave to me with an online tool and compare it to the hash they provided and the base64 encode, thing matched. I tried decoding as well, nothing matched
Can someone please send me an example that works, so I can understand what happens and how it works?
Thank you!
Below is what I have so far:
function POSTRequest() {
var url = 'https:mws.amazonservices.com/Orders/2013-09-01?';
var today = new Date();
var todayTime = ISODateString(today);
var yesterday = new Date();
yesterday.setDate(today.getDate() - 1);
yesterday.setHours(0,0,0,0);
var yesterdayTime = ISODateString(yesterday);
var dayBeforeYesterday = new Date();
dayBeforeYesterday.setDate(today.getDate() - 2);
dayBeforeYesterday.setHours(0,0,0,0);
var dayBeforeYesterdayTime = ISODateString(dayBeforeYesterday);
var unsignedURL =
'POST\r\nhttps:mws.amazonservices.com\r\n/Orders/2013-09-01\r\n'+
'AWSAccessKeyId=xxxxxxxxxxx' +
'&Action=ListOrders'+
'&CreatedAfter=' + dayBeforeYesterdayTime +
'&CreatedBefore' + yesterdayTime +
'&FulfillmentChannel.Channel.1=AFN' +
'&MWSAuthToken=xxxxxxxxxxxx'+
'&MarketplaceId.Id.1=ATVPDKIKX0DER' +
'&SellerId=xxxxxxxxxxx'+
'&SignatureMethod=HmacSHA256'+
'&SignatureVersion=2'+
'&Timestamp='+ ISODateString(new Date) +
'&Version=2013-09-0';
var formData = {
'AWSAccessKeyId' : 'xxxxxxxxx',
'Action' : "ListOrders",
'CreatedAfter' : dayBeforeYesterdayTime,
'CreatedBefore' : yesterdayTime,
'FulfillmentChannel.Channel.1' : 'AFN',
'MWSAuthToken' : 'xxxxxxxxxxxx',
'MarketplaceId.Id.1' : 'ATVPDKIKX0DER',
'SellerId' : 'xxxxxxxxxx',
'SignatureMethod' : 'HmacSHA256',
'SignatureVersion' : '2',
'Timestamp' : ISODateString(new Date),
'Version' : '2013-09-01',
'Signature' : calculatedSignature(unsignedURL)
};
var options = {
"method" : "post",
"muteHttpExceptions" : true,
"payload" : formData
};
var result = UrlFetchApp.fetch(url, options);
writeDataToXML(result);
Logger.log(result);
if (result.getResponseCode() == 200) {
writeDataToXML(result);
}
}
function calculatedSignature(url) {
var urlToSign = url;
var secret = "xxxxxxxxxxxxxxxxxxx";
var accesskeyid = 'xxxxxxxxxxxxxxx';
var byteSignature = Utilities.computeHmacSha256Signature(urlToSign, secret);
// convert byte array to hex string
var signature = byteSignature.reduce(function(str,chr){
chr = (chr < 0 ? chr + 256 : chr).toString(16);
return str + (chr.length==1?'0':'') + chr;
},'');
Logger.log("URL to sign: " + urlToSign);
Logger.log("");
Logger.log("byte " + byteSignature);
Logger.log("");
Logger.log("reg " + signature);
var byte64 = Utilities.base64Encode(byteSignature)
Logger.log("base64 byte " + Utilities.base64Encode(byteSignature));
Logger.log("");
Logger.log("base64 reg " + Utilities.base64Encode(signature));
return byte64;
}
Step 1, creating the string to be signed
The string_to_sign is the combination of the following:
The string POST followed by a NEWLINE character
The name of the host, mws.amazonservices.com, followed by a NEWLINE
The API URL, often just /, or somthing like /Orders/2013-09-01, followed by a NEWLINE
An alphabetical list of all parameters except Signature in URL encoding, like a=1&b=2, not followed by anything
The minimum parameters seem to be the following:
AWSAccessKeyId is a 20-character code provided by Amazon
Action is the name of your API call, like GetReport
SellerId is your 14-character seller ID
SignatureMethod is HmacSHA256
SignatureVersion is 2
Timestamp is a date like 20181231T23:59:59Z for one second before new year UTC
Version is the API version like 2013-09-01
additional parameters may be needed for your call, depending on the value of Action
Please note:
The NEWLINE character is just "\n", not "\r\n"
The name of the host should not include "https://" or "http://"
The Signature parameter is necessary for the actual call later (see step 3), but is not part of the string_to_sign.
Your string_to_sign should now look somewhat like this:
POST
mws.amazonservices.com
/Orders/2013-09-01
AWSAccessKeyId=12345678901234567890&Action=ListOrders&CreatedAfter .... &Version=2013-09-01
Step 2, signing that string
Calculate a SHA256 hash of above string using the 40-character Secret Key
Encode this hash using Base64
In Pseudocode: signature = Base64encode( SHA256( string_to_sign, secret_key ))
Step 3, send the call
Send a HTTPS POST request, using the full alphabetical list of parameters, now including above signature as Signature somewhere in the middle, because you need to keep ascending alphabetical order.
https://mws.amazonservices.com/Orders/2013-09-01?AWSAccessKeyId....Version=2013-09-01
Step 4, processing the result
You should be getting two things back: a response header and a XML document. Be sure to evaluate the HTTP status in the header as well as all contents of the XML document. Some error messages are hidden deeply in XML while HTTP returns "200 OK".
Step 5, Advanced Stuff
If you use calls that require you to send a document, like SendFeed, you need to do the following additional steps:
Calculate the MD5 hash of your document
Encode this hash using Base64
In Pseudocode: contentmd5= Base64encode( MD5( document ))
Add Content-Type: text/xml (or whatever fits your document) as HTTP header
Add Content-MD5: plus the base64 encoded hash as HTTP header
This code was a great help for building a similar application. I corrected some small issues to bring it up to work:
before signing the ISODate need to be worked out to replace the ":" chars
var todayTime = ISODateString(today);
var todayISO = todayTime;
var todayTime_ = todayTime.replace(":", "%3A");
todayTime = todayTime_.replace(":","%3A");
The same for the calculated signature (quick & dirty solution, replace only 3 appearences and need to updated for more chars)
Logger.log(unsignedURL);
var tmpsignature = calculatedSignature(unsignedURL);
var orsignature = tmpsignature;
// encode special chars
tmpsignature = encodeURIComponent(orsignature);
}
I added a header and dropped the form, put all parameters in the url
var header = {
"x-amazon-user-agent": "GoogleSheets/1.0 (Language=Javascript)",
"Content-Type": "application/x-www-form-urlencoded"
// "Content-Type": "text/xml"
};
var options = {
"method" : "post",
"muteHttpExceptions" : true,
// "payload" : formData,
"header":header
};
And changed the call to url encoded (Form was not working, do not know why)
var url = 'https:mws-eu.amazonservices.com/Orders/2013-09-01?'+
'AWSAccessKeyId=<your stuff>'+
'&Action=GetOrder'+
'&SellerId=<your stuff>+
'&MWSAuthToken=<your token>'+
'&SignatureVersion=2'+
'&Timestamp='+todayTime'+ // remember to replace the ":" thru hex
'&Version=2013-09-01'+
'&Signature='+ tmpsignature+
'&SignatureMethod=HmacSHA256'+
'&AmazonOrderId.Id.1='+<your order;
parsed the response:
var result = UrlFetchApp.fetch(url, options);
//writeDataToXML(result);
Logger.log(result);
var xml = result.getContentText();
var document = XmlService.parse(xml);
This worked! :-)
I checked the signature also with
https://mws-eu.amazonservices.com/scratchpad/index.html

Binance API Signature with Google Scripts

I am stuck on how to correctlly include the signitue into my get command based off of the Binance API within Google Scripts. What it states is
SIGNED endpoints require an additional parameter, signature, to be sent in the query string or request body.
Endpoints use HMAC SHA256 signatures. The HMAC SHA256 signature is a keyed HMAC SHA256 operation. Use your secretKey as the key and totalParams as the value for the HMAC operation.
The signature is not case sensitive.
totalParams is defined as the query string concatenated with the request body.
What I have is:
function BinanceTrades() {
var curTime = Number(new Date().getTime()).toFixed(0)
var sKey = Utilities.computeHmacSha256Signature('symbol=LTCBTC&timestamp=' + curTime, '**mySeceretKey**');
Logger.log(sKey)
var headers = {'X-MBX-APIKEY': '**myKey**'}
var data = UrlFetchApp.fetch("https://api.binance.com/api/v3/allOrders?signature=" + sKey + "&symbol=LTCBTC&timestamp=" + curTime, {'headers' : headers})
Logger.log(data)
}
and the error I get is:
{"code":-1100,"msg":"Illegal characters found in parameter 'signature'; legal range is '^[A-Fa-f0-9]{64}$'."}
I am unsure of how to compute the HMAC SHA256 correctly and incorporate the totalParams.
My previous post was this.
How about these modifications?
Modification points :
From the manual you provided
In your case, the string which is used for the signature is "symbol=LTCBTC&timestamp=" + curTime.
The signature is the string of the unsigned hexadecimal.
But at Google Apps Script, the data which was encrypted by Utilities.computeHmacSha256Signature() is the bytes array of the signed hexadecimal.
The modified script which reflected above points is as follows.
Modified script :
function BinanceTrades() {
var key = '**myKey**';
var secret = '**mySeceretKey**';
var curTime = Number(new Date().getTime()).toFixed(0);
var string = "symbol=LTCBTC&timestamp=" + curTime;
var sKey = Utilities.computeHmacSha256Signature(string, secret);
sKey = sKey.map(function(e) {
var v = (e < 0 ? e + 256 : e).toString(16);
return v.length == 1 ? "0" + v : v;
}).join("");
var params = {
'method': 'get',
'headers': {'X-MBX-APIKEY': key},
'muteHttpExceptions': true
};
var url = "https://api.binance.com/api/v3/allOrders?" + string + "&signature=" + sKey;
var data = UrlFetchApp.fetch(url, params);
Logger.log(data.getContentText())
}
Note :
About the encryption of signature, it has already confirmed that this script works fine from the manual you provided.
I have no account for binance.com. So I couldn't run this script. I'm sorry.
When you run this script, if the error occurs, can you show me the error messages?

Bitcoin - JavaScript function to return the current BTC exchange rate

I'm looking to write a JavaScript function that will return the current BTC/USD exchange rate. I've done some research, but I just want something simple. It won't be used server-side for calculating values (obvious security implications), but just as a convenience for my users. I have 2 text fields, and when the user changes one of the values, it will update the other field.
Here is my code so far:
var getBTCRate = function(){ /* code here */ };
var btcprice = getBTCRate();
// update the BTC value as the USD value is updated
$("#usdvalue").keyup(function(ev){
var usdvalue = $("#usdvalue").val();
$("#btcvalue").val(usdvalue / btcprice);
});
// update the USD value as the BTC value is updated
$("#btcvalue").keyup(function(ev){
var btcvalue = $("#btcvalue").val();
$("#usdvalue").val(btcvalue * btcprice);
});
Plain and simple. In my research I haven't been able to find something that will do this, only a bunch of confusing APIs. Any help is much appreciated.
EDITED to fix a mistake in the code.
EDITED AGAIN to fix the position of the function declaration. Thanks to #RobG for pointing this out.
My first idea was to use JQuery load like this
$.get('https://www.google.com/search?q=btc+value', function(p) {
console.log(p);
});
but cross-origin rules stopped me.
Now, you can pay for a service that has an API, but I wanted to do it without having to pay. What I ended up doing is a server based solution. I use PowerBasic for my back end, with the SocketTools Library.
#COMPILE EXE
#DIM ALL
#Include "pbcgi.inc"
#Include "C:\bas_src\socketTools\v9.5\inc\cstools9.inc"
Function PBMain () As Long
Local btc As String
Local html As String
html= httpGet("https://www.google.com/search?q=btc+value")
' filter out just the current BTC value
' by looking for what is between the words "Bitcoin =" and "United States Dollar"
btc=Remain$(html,"Bitcoin =")
btc=Extract$(btc,"United States Dollar")
btc=Trim$(btc)
writeCGI "{"+jsonPad("btc")+":"+jsonPad(btc)+","+jsonPad("error")+":"+jsonPad("0")+"}"
END FUNCTION
'================================================================
' retrieve the page and return it as a string
Function httpGet(ByVal URL As String) As String
If IsFalse( HttpInitialize($CSTOOLS9_LICENSE_KEY) ) Then
Function="unable to init socket library"
Exit Function
End If
Local hClient As Dword
Local lpszURL As STRINGZ * 4096
Local lpszBuffer As STRINGZ * 100000
Local httpContents As String
Local httpPort As Long
Local httpOptions As Dword
Local nResult As Long
If LCase$(Trim$(Left$(URL, 8))) = "https://" Then
httpPort = 443
httpOptions = %HTTP_OPTION_SECURE Or %HTTP_OPTION_REDIRECT
Else
httpPort = 80
httpOptions = %HTTP_OPTION_REDIRECT
End If
lpszURL = URL & Chr$(0)
hClient = HttpConnect(lpszURL, _
httpPort, _
%HTTP_TIMEOUT, _
httpOptions, _
%HTTP_VERSION_10)
If hClient = %INVALID_CLIENT Then
Function = "Could not connect to: " & URL
Exit Function
End If
HttpSetEncodingType(hClient, %HTTP_ENCODING_NONE)
nResult = HttpGetText(hClient, lpszURL, lpszBuffer, 100000)
If nResult = %HTTP_ERROR Then
Function = "Error retrieving file."+Str$(nResult)
Else
' success
httpContents = lpszBuffer
Function =httpContents
End If
Call HttpDisconnect(hClient)
HttpUninitialize()
End Function
'================================================================
' pad string for json return
Function jsonPad(jstr As String) As String
Local i As Long
Replace Chr$(10) With " " In jstr
Replace Chr$(13) With " " In jstr
Replace "\" With "\\" In jstr
Replace $Dq With "\"+$Dq In jstr
For i = 0 To 9
Replace Chr$(i) With " " In jstr
Next
Function=$Dq+Trim$(jstr)+$Dq
End Function
On my web page, I call it using AJAX
function showBTC(){
$.ajax({
type: "POST",
url: "../cgi/btcGet.exe",
dataType: "json",
success: function(json){
if(json.error !== "0" ){
console.log( json.error );
return;
}
$("#btcValue").html( "current BTC value $"+json.btc+"<br><br>" );
}
});
}
I know that seems like a "too specific" answer, but it's how I do it.
EDIT 5/11/2020:
there is a Bitcoin API found at coindesk.com which greatly simplifies this process.
const api = 'https://apiv2.bitcoinaverage.com/indices/local/ticker/short?crypto=BTC&fiat=USD'
$.get(api, p => {
document.querySelector('pre').textContent = JSON.stringify(p, null, 2)
});
Result
{
"BTCUSD": {
"ask": 3594.5649555077953,
"timestamp": 1550284932,
"bid": 3591.715961836563,
"last": 3592.745617344171,
"averages": {
"day": 3583.13243402
}
}
}
So take your pick p.BTCUSD.ask // or bid or last
demo

Display thumbnailPhoto from Active Directory using Javascript only - Base64 encoding issue

Here's what I'm trying to do:
From an html page using only Javascript I'm trying to query the Active Directory and retrieve some user's attributes.
Which I succeded to do (thanks to some helpful code found around that I just cleaned up a bit).
I can for example display on my html page the "displayName" of the user I provided the "samAccountName" in my code, which is great.
But I also wanted to display the "thumbnailPhoto" and here I'm getting some issues...
I know that the AD provide the "thumbnailPhoto" as a byte array and that I should be able to display it in a tag as follow:
<img src="data:image/jpeg;base64," />
including base64 encoded byte array at the end of the src attribute.
But I cannot manage to encode it at all.
I tried to use the following library for base64 encoding:
https://github.com/beatgammit/base64-js
But was unsuccesful, it's acting like nothing is returned for that AD attribute, but the photo is really there I can see it over Outlook or Lync.
Also when I directly put that returned value in the console I can see some weird charaters so I guess there's something but not sure how it should be handled.
Tried a typeof to find out what the variable type is but it's returning "undefined".
I'm adding here the code I use:
var ADConnection = new ActiveXObject( "ADODB.connection" );
var ADCommand = new ActiveXObject( "ADODB.Command" );
ADConnection.Open( "Data Source=Active Directory Provider;Provider=ADsDSOObject" );
ADCommand.ActiveConnection = ADConnection;
var ou = "DC=XX,DC=XXXX,DC=XXX";
var where = "objectCategory = 'user' AND objectClass='user' AND samaccountname='XXXXXXXX'";
var orderby = "samaccountname ASC";
var fields = "displayName,thumbnailPhoto";
var queryType = fields.match( /,(memberof|member),/ig ) ? "LDAP" : "GC";
var path = queryType + "://" + ou;
ADCommand.CommandText = "select '" + fields + "' from '" + path + "' WHERE " + where + " ORDER BY " + orderby;
var recordSet = ADCommand.Execute;
fields = fields.split( "," );
var data = [];
while(!recordSet.EOF)
{
var rowResult = { "length" : fields.length };
var i = fields.length;
while(i--)
{
var fieldName = fields[i];
if(fieldName == "directReports" && recordSet.Fields(fieldName).value != null)
{
rowResult[fieldName] = true;
}
else
{
rowResult[fieldName] = recordSet.Fields(fieldName).value;
}
}
data.push(rowResult);
recordSet.MoveNext;
}
recordSet.Close();
console.log(rowResult["displayName"]);
console.log(rowResult["thumbnailPhoto"]);
(I replaced db information by Xs)
(There's only one entry returned that's why I'm using the rowResult in the console instead of data)
And here's what the console returns:
LOG: Lastname, Firstname
LOG: 񏳿က䙊䙉Āā怀怀
(same here Lastname & Firstname returned are the correct value expected)
This is all running on IE9 and unfortunetly have to make this compatible with IE9 :/
Summary:
I need to find a solution in Javascript only
I know it should be returning a byte array and I need to base64 encode it, but all my attempts failed and I'm a bit clueless on the reason why
I'm not sure if the picture is getting returned at all here, the thing in the console seems pretty small... or if I'm nothing doing the encoding correctly
If someone could help me out with this it would be awesome, I'm struggling with this for so long now :/
Thanks!

ajax returns empty string instead of json [python cgi]

Basically, I have a cgi script that prints out valid json, I have checked and I have a similar functions that work the same way but this one doesn't some reason and I can't find it.
Javascript:
function updateChat(){
$.ajax({
type: "get",
url: "cgi-bin/main.py",
data: {'ajax':'1', 'chat':'1'},
datatype:"html",
async: false,
success: function(response) {
alert(response); //Returns an empty string
},
error:function(xhr,err)
{
alert("Error connecting to server, please contact system administator.");
}
});
Here is the JSON that python prints out:
[
"jon: Hi.",
"bob: Hello."
]
I used json.dumps to create the JSON it worked in previous functions that have pretty much the same JSON layout only different content.
There is a whole bunch more of server code, I tried to copy out the relevant parts. Basically I'm just trying to filter an ugly chat log for learning purposes. I filter it with regex and then create a json out of it.
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
print "Content-type: text/html\n\n"
print
import cgi, sys, cgitb, datetime, re, time, random, json
cgitb.enable()
formdata = cgi.FieldStorage()
def tail( f, window=20 ):
BUFSIZ = 1024
f.seek(0, 2)
bytes = f.tell()
size = window
block = -1
data = []
while size > 0 and bytes > 0:
if (bytes - BUFSIZ > 0):
# Seek back one whole BUFSIZ
f.seek(block*BUFSIZ, 2)
# read BUFFER
data.append(f.read(BUFSIZ))
else:
# file too small, start from begining
f.seek(0,0)
# only read what was not read
data.append(f.read(bytes))
linesFound = data[-1].count('\n')
size -= linesFound
bytes -= BUFSIZ
block -= 1
return '\n'.join(''.join(data).splitlines()[-window:])
def updateChatBox():
try:
f = open('test.txt', 'r')
lines = tail(f, window = 20)
chat_array = lines.split("\n")
f.close()
except:
print "Failed to access data"
sys.exit(4)
i = 0
while i < len(chat_array):
#remove timer
time = re.search("(\[).*(\])", chat_array[i])
result_time = time.group()
chat_array[i] = chat_array[i].replace(result_time, "")
#Removes braces around user
user = re.search("(\\().*?(_)", chat_array[i])
result_user = user.group()
chat_array[i] = chat_array[i].replace("(", "")
chat_array[i] = chat_array[i].replace(")", "")
#Removes underscore and message end marker
message = re.search("(_).*?(\|)", chat_array[i])
result_message = message.group()
chat_array[i] = chat_array[i].replace("_", ":")
chat_array[i] = chat_array[i].replace("|", "")
data += chat_array[i] + "\n"
i = i + 1
data_array = data.split("\n")
json_string = json.dumps(data_array)
print json_string
if formdata.has_key("ajax"):
ajax = formdata["ajax"].value
if ajax == "1": #ajax happens
if formdata.has_key("chat"):
chat = formdata["chat"].value
if chat == 1:
updateChatBox()
else:
print "ERROR"
elif formdata.has_key("get_all_stats"):
get_all_stats = formdata["get_all_stats"].value
if get_all_stats == "1":
getTopScores()
else:
print "ERROR"
Here is also a function that works perfectly and is in the same python file
def getTopScores():
try:
f = open('test_stats.txt', 'r')
stats = f.read()
stats_list = stats.split("\n")
f.close()
except:
print "Failed reading file"
sys.exit(4)
json_string = json.dumps(stats_list)
print json_string
The only difference is using the tail function and regex, the end result JSON actually looks identical.
Are you certain that updateChatBox is even getting called? Note that you compare ajax to the string "1" but you compare chat to the integer 1. I bet one of those doesn't match (in particular the chat one). If that doesn't match, your script will fall through without ever returning a value.
Also, though it isn't the root cause, you should clean up your content types for correctness. Your Javascript AJAX call is declared as expecting html in response, and your cgi script is also set to return content-type:text/html. These should be changed to json and content-type:application/json, respectively.

Categories

Resources