How to sign Amazon Simple Pay button in node.js / Javascript - node.js

I've spent the past 6 hours trying to follow Amazon's instructions for signing a Simple Pay button form in node.js. I can't get it to the accept the sig and I've tried every permutation of the confusing instructions. Can anyone help me out of my misery?
The error I get is
Input parameter signature is invalid
Here is my procedure
var params={
"returnUrl": "[confidential]",
"ipnUrl": "[confidential]",
"processImmediate": "1",
"accessKey" :"[AWS key]",
"collectShippingAddress" :"0",
"isDonationWidget" :"0",
"amazonPaymentsAccountId" :"[the button generator creates this but there is no mention in the docs]",
"referenceId" :ref,
"cobrandingStyle" :"logo",
"immediateReturn" :"1",
"amount" :"USD "+amount,
"description" : desc,
"abandonUrl" :"[confidential]",
"signatureMethod": "HmacSHA256", //docs not clear if signatureMethod and signatureVersion should be included, but I've tried all permutations and can't get it to work
"signatureVersion" :"2"
}
//Docs say it should confirm to
/*
StringToSign = HTTPVerb + "\n" +
ValueOfHostHeaderInLowercase + "\n" +
HTTPRequestURI + "\n" +
CanonicalizedQueryString <from the preceding step>
*/
//sort parameters (natural byte order)
var p=_.pairs(params);
var psorted=_.sortBy(p, function(p) { return p[0];});
//start to construct the form
var input='';
for(var i=0; i<psorted.length;i++) {
input+="<input type='hidden' name='"+psorted[i][0]+"' value='"+psorted[i][1]+"'>";
}
//prepare the string to be signed
var qstring='POST'+'\n';
qstring+='https://authorize.payments.amazon.com'+'\n';
qstring+='/pba/paypipeline'+'\n';
for(var i=0; i<psorted.length;i++) {
psorted[i][0]=encodeURI(psorted[i][0]);
psorted[i][1]=encodeURI(psorted[i][1]);
qstring+=psorted[i][0]+'='+psorted[i][1];
if (i<psorted.length-1) {qstring+='&';}
};
console.log(qstring+'\n\n');
var sig=crypto.createHmac("SHA256", "[AWS Secret Key") 6
.update(qstring)
.digest('base64');
input+="<input type='hidden' name='signature' value='"+sig+"'>"; //doesn't matter whether or not i url encode this
console.log(input);
That converts the parameters into
POST
authorize.payments.amazon.com
/pba/paypipeline
abandonUrl=XXXX&accessKey=XXXXX&amazonPaymentsAccountId=XXXXXX&amount=USD%203&cobrandingStyle=logo&collectShippingAddress=0&description=sadasdd&immediateReturn=1&ipnUrl=XXXXXx&isDonationWidget=0&processImmediate=1&referenceId=324324&returnUrl=XXXXXXXX&signatureMethod=HmacSHA256&signatureVersion=2
I concatenate and paste the the output to this form for testing
<form action="https://authorize.payments.amazon.com/pba/paypipeline" method="POST">
<input type='hidden' name='abandonUrl' value='[confidential]'>
<input type='hidden' name='accessKey' value='[confidential]'>
<input type='hidden' name='amazonPaymentsAccountId' value='[confidential]'>
<input type='hidden' name='amount' value='USD 3'>
<input type='hidden' name='cobrandingStyle' value='logo'>
<input type='hidden' name='collectShippingAddress' value='0'>
<input type='hidden' name='description' value='sadasdd'>
<input type='hidden' name='immediateReturn' value='1'>
<input type='hidden' name='ipnUrl' value='[confidential]'>
<input type='hidden' name='isDonationWidget' value='0'>
<input type='hidden' name='processImmediate' value='1'>
<input type='hidden' name='referenceId' value='324324'>
<input type='hidden' name='returnUrl' value='[confidential]'>
<input type='hidden' name='signatureMethod' value='HmacSHA256'>
<input type='hidden' name='signatureVersion' value='2'>
<input type='hidden' name='signature' value='fHSA+p37r5ooOJOUnjYBdhNFe/pAEg/KunAEOudUvGs='>
<input type="submit">
</form>
Here are the Amazon docs
http://docs.aws.amazon.com/AmazonSimplePay/latest/ASPAdvancedUserGuide/Sig2CreateSignature.html
How to Generate a Signature
To create the signature
Create the canonicalized query string that you need later in this
procedure:
Sort the UTF-8 query string components by parameter name with natural
byte ordering.
The parameters can come from the GET URI or from the POST body (when
Content-Type is application/x-www-form-urlencoded).
URL encode the parameter name and values according to the following
rules:
Do not URL encode any of the unreserved characters that RFC 3986
defines.
These unreserved characters are A-Z, a-z, 0-9, hyphen ( - ),
underscore ( _ ), period ( . ), and tilde ( ~ ).
Percent encode all other characters with %XY, where X and Y are hex
characters 0-9 and uppercase A-F.
Percent encode extended UTF-8 characters in the form %XY%ZA....
Percent encode the space character as %20 (and not +, as common
encoding schemes do).
Note Currently all AWS service parameter names use unreserved
characters, so you don't need to encode them. However, you might want
to include code to handle parameter names that use reserved
characters, for possible future use. Separate the encoded parameter
names from their encoded values with the equals sign ( = ) (ASCII
character 61), even if the parameter value is empty.
Separate the name-value pairs with an ampersand ( & ) (ASCII code 38).
Create the string to sign according to the following pseudo-grammar
(the "\n" represents an ASCII newline).
StringToSign = HTTPVerb + "\n" + ValueOfHostHeaderInLowercase + "\n" +
HTTPRequestURI + "\n" + CanonicalizedQueryString The HTTPRequestURI component is the HTTP absolute path
component of the URI up to, but not including, the query string. If
the HTTPRequestURI is empty, use a forward slash ( / ).
Calculate an RFC 2104-compliant HMAC with the string you just created,
your Secret Access Key as the key, and SHA256 or SHA1 as the hash
algorithm.
For more information, go to http://www.ietf.org/rfc/rfc2104.txt.
Convert the resulting value to base64.
Use the resulting value as the value of the Signature request
parameter.
Important The final signature you send in the request must be URL
encoded as specified in RFC 3986 (for more information, go to
http://www.ietf.org/rfc/rfc3986.txt). If your toolkit URL encodes your
final request, then it handles the required URL encoding of the
signature. If your toolkit doesn't URL encode the final request, then
make sure to URL encode the signature before you include it in the
request. Most importantly, make sure the signature is URL encoded only
once. A common mistake is to URL encode it manually during signature
formation, and then again when the toolkit URL encodes the entire
request. For information on the high-level process for creating a
button, see Creating Button Forms Dynamically .
In the following examples, new lines have been inserted to make the
examples easier to read. Explicit '\n' is used wherever new line is
required.
The following is an example Amazon Simple Pay request using POST.
The following is an example of a string to use for
StringToSign in the preceding example.
POST\n authorize.payments-sandbox.amazon.com\n /pba/paypipeline\n
SignatureMethod=HmacSHA256 &SignatureVersion=2
&accessKey=YourCallerKey &amount=USD%201.1 &cobrandingStyle=logo
&description=Test%20Widget &immediateReturn=0
&ipnUrl=http%3A%2F%2Fyourwebsite.com%2Fipn &processImmediate=1
&referenceId=YourReferenceId
&returnUrl=http%3A%2F%2Fyourwebsite.com%2Freturn.html For more
examples of generating a signature, see Appendix: Sample Code.
For information on signing your button form correctly, see How to Sign
your Button Form Correctly.

For posterity, here is the solution:
1) Don't mess with Simple Pay Buttons - use FPS instead
2) Of the myriad overlapping documents, I found this to be the simplest and clearest: http://docs.aws.amazon.com/AmazonFPS/latest/FPSBasicGuide/SendingaCBUIRequest.html
3) Use encodeURIComponent not encodeURI - this was my biggest most frustrating mistake
This code will correctly sign an Amazon FPS request (assumes crypto for hmac and nconf for configuration)
var crypto = require('crypto');
var _ = require('underscore');
var nconf = require('nconf').argv().env().file({
file: "./config.json"
});
exports.azPayRequest=function (amount, desc,ref) {
var params={
"returnUrl": nconf.get("awsPayments:returnURL"), //callback
"callerKey" : nconf.get("awsPayments:callerKey"), //aws id
"callerReference": ref,
"pipelineName":"SingleUse",
"cobrandingStyle" :"logo",
"currencyCode" :"USD",
"transactionAmount" : amount,
"paymentReason" : desc,
"signatureMethod": "HmacSHA256",
"signatureVersion" :"2"
}
/*
StringToSign = HTTPVerb + "\n" +
ValueOfHostHeaderInLowercase + "\n" +
HTTPRequestURI + "\n" +
CanonicalizedQueryString <from the preceding step>
*/
//sort parameters
var p=_.pairs(params);
var psorted=_.sortBy(p, function(p) { return p[0];});
//method, host, path
var method='GET';
var host=nconf.get('awsPayments:host'); // e.g., authorize.payments.amazon.com;
var path=nconf.get('awsPayments:path'); //e.g. /cobranded-ui/actions/start;
//url encode parameters
var qstring='';
for(var i=0; i<psorted.length;i++) {
psorted[i][0]=encodeURIComponent(psorted[i][0]);
psorted[i][1]=encodeURIComponent(psorted[i][1]);
qstring+=psorted[i][0]+'='+psorted[i][1];
if (i<psorted.length-1) {qstring+='&';}
};
//calculate hmac
var nl=String.fromCharCode(10);
var encode_request=method+nl+host+nl+path+nl+qstring;
console.log("STRING TO ENCODE\n"+encode_request+'\n\n');
var sig=crypto.createHmac("SHA256", nconf.get("awsPayments:awsSecretAccessKey"))
.update(encode_request)
.digest('base64');
var url="https://"+host+path+"?"+qstring+'&signature='+encodeURIComponent(sig);
return url;
}

Related

Encode string in base64 into Shopify

How can I encode a string to base 64 in Shopify?
I need to encrypt into base64 the the buyer email ({{customer.email}}) and the order ({{order_name}})
The scope if for adding the trustedbadge*.de button into the email, and they are requesting the fallow url format:
www . trustedshops/buyerrating/rate_XFD9974BBC558C007CD46
431D056DF230.html&buyerEmail="[base64 buyerEmail]: "&
shopOrderID=" [base64 Order] "&channel=cmF0ZW5vd2J1dHRvbg"
Is there any way to convert those two values in base64?
Thank you for your time!
Base64 is not encryption -- it's known as an encoding.
There is no shopify liquid filter for base64 that I'm aware of, so you'll have to do this in javascript.
Liquid:
HTML:
<a href="#" onclick="window.open('https://www.trustedshops/buyerrating/rate_XFD9974BBC558C007CD46431D056DF230.html&buyerEmail=' + encodeURIComponent(btoa({{ order.email | json }})) + '&shopOrderID=' + encodeURIComponent(btoa('{{ order.order_number }}')) + '&channel=cmF0ZW5vd2J1dHRvbg')">
Open order on trustedshops.com
</a>
And this code just explains the javascript in that onclick.
Javascript:
// The btoa function is the javascript function to encode a string as base64
// Since base64 encoded strings can't go directly in a url, we then
// need to use encodeURIComponent to make it ok to use in a url.
var encodedEmail = encodeURIComponent(btoa('hello#example.com')); // aGVsbG9AZXhhbXBsZS5jb20%3D
var encodedOrderId = encodeURIComponent(btoa('1234567890')); // MTIzNDU2Nzg5MA%3D%3D
// we can now construct the full url
var url = 'https://www.trustedshops/buyerrating/rate_XFD9974BBC558C007CD46431D056DF230.html&buyerEmail=' + encodedEmail + '&shopOrderID=' + encodedOrderId + '&channel=cmF0ZW5vd2J1dHRvbg';
// use window.open to open the url in a new tab
window.open(url);
// or use window.location.href = url if you'd like it
// to be the same tab

Need to pass search query into URL string using custom params

I have a simple search form that looks like this:
<form action="http://www.theurltosearch.com" method="post">
<input class="search-box" name="query" type="text" value="search all reports" />
<input type="submit" name="search" />
</form>
What I'm trying to accomplish
The search is pointing to whats really a filtering system using tags.
In order for the user to properly see the results of what they queried the query url has to look something like this http://www.theurltosearch.com/#/Kboxes the # and the K are important as its how the tagging system returns results where K stands for keyword.
For multi term queries the url has to look like this separated by a comma http://www.theurltosearch.com/#/Kboxes,Kmoving
A user should also get results when they enter a string query something like http://www.theurltosearch.com/#/K%22more%20stuff%22
Right now if someone used the search it would just take them to the url and not actually display any results matching their query.
How can I manipulate the url string to return the results how I've shown above?
My actual attempt
<script type="text/javascript">
window.onload = function(){
var form = document.getElementById("reports-search");
form.onsubmit = function(){
var searchText = document.getElementById("search-reports");
window.location = "http://www.urltosearch.com/#/K" + searchText.value;
return false;
};
};
</script>
<form id="reports-search" method="get">
<input class="search-box" id="search-reports" type="text" value="search all reports" /><!--search term was analysis-->
<input type="submit" name="search" />
</form>
returns
http://www.urltosearch.com/#/Kanalysis
and displays all results with the analysis tag
This attempt works succesfully if someone is searching a single keyword but not if the user is searching multiple or a string
How do I change the JS to achieve the other options?
Okay, here's a dog'n'bird implementation (ruff,ruff, cheap,cheap).
I've allowed the user to enter multiple terms, each separated with the pipe character | If you wish to allow the user to enter a url in essentially the same format as they'd receive by 'normal' keywords, you may wish to check the entered text first and if found, simply pass it straight through without changing it.
You'll notice, I've wrapped all search terms with " ", regardless of whether the term is multi-word or not. You could easily differentiate between a single-word term and a multi, by searching the string for a space character after the string.trim has removed leading/trailing spaces. I.e
if (trimmedTerm.indexOf(' ') == -1)
{
// single word search term
}
else
{
// multi-word search term here
}
Anyway, here's a working demo, hope it gives insight.
function byId(id){return document.getElementById(id)}
// useful for HtmlCollection, NodeList, String types
function forEach(array, callback, scope){for (var i=0,n=array.length; i<n; i++)callback.call(scope, array[i], i, array);} // passes back stuff we need
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
byId('goBtn').addEventListener('click', onGoBtnClicked);
}
function onGoBtnClicked(evt)
{
// get the user input
var inputString = byId('userInput').value;
// split it into an array of terms, based on the | char
var searchTerms = inputString.split('|');
// init the result
var result ='';
// for each element in the array of search terms, call the function to trim wrap with "" and encode
forEach(searchTerms, addCurTermToResult);
// update the output display
byId('output').textContent = 'http://www.theurltosearch.com/#/' + result;
function addCurTermToResult(curTerm, index)
{
if (index != 0) // put a comma before all terms except the first one
result += ',';
var trimmedTerm = curTerm.trim(); // remove leading/trailing spaces
result += 'K' + encodeURI('"' + trimmedTerm + '"' ); // wrap with "" then URI encode it, suitable for use as a URL
}
}
.panel
{
border: solid 1px black;
border-radius: 8px;
padding: 8px;
background-color: #eef;
display:inline-block;
}
.panel textarea
{
width: 500px;
height: 200px;
}
<div class='panel'>
<textarea type='text' id='userInput' placeholder='Enter tags or a url. tags should be seperated with the | character'></textarea>
<div style='text-align: center'><button id='goBtn'>Submit</button></div>
<hr>
<label>URL: <span id='output'></span></label>
</div>

Node.js (sails.js) wysiwyg editor - images

Is there a way to use any WYSIWYG/html editor in the sails app? I
can't find any manuals to do that. Seems like Mercury is
well-supported in Node but I can't find a way to adapt sails for it
either. :( Please guide me
OK now, it turned up to be easy to connect TinyMCE (just as easy as described on http://www.tinymce.com/wiki.php/Installation ). So now another major question comes out: is there any Node.js connector to any editor for uploading images and stuff?
Or just how can I allow user to upload an image and insert it to a post body?
Thanks
Yay! Finally did it!
At last I used the CKEditor and it worked! Check it out:
download the editor at http://ckeditor.com/download
put it into the assets folder of your project
add config.filebrowserUploadUrl = '/uploader'; to your ckeditor/config.js file
add an action for uploads in your controller :
upload_file : function(req, res) {
var fs = require('fs');
console.log(req.files);
fs.readFile(req.files.upload.path, function (err, data) {
var newPath = 'assets/files/' + req.files.upload.name;
fs.writeFile(newPath, data, function (err) {
if (err) res.view({err: err});
html = "";
html += "<script type='text/javascript'>";
html += " var funcNum = " + req.query.CKEditorFuncNum + ";";
html += " var url = \"/files/" + req.files.upload.name + "\";";
html += " var message = \"Uploaded file successfully\";";
html += "";
html += " window.parent.CKEDITOR.tools.callFunction(funcNum, url, message);";
html += "</script>";
res.send(html);
});
});
}
add a route for this action:
'/uploader' : {
controller : 'post',
action : 'upload_file'
}
make a folder (assets/files for me) for uploads
finally, change the form putting ckeditor in:
block body
script(type="text/javascript", src="/ckeditor/ckeditor.js")
form(action='/posts/create', method="post", enctype="multipart/form-data")
p Title
input(type='text', name='title')
p Body
textarea(name='body', id='ck')
script.
CKEDITOR.replace( 'ck' );
hr
input(type='submit', value='Сохранить')
(in jade here)
That's all! Enjoy WYSIWYG :)
The above answer will work (I gave it the up vote), but if you have the csrf token enabled on the site you need to do a few extra things (I would leave a comment but my rep is not high enough yet):
Add the standard csrf hidden input on the form that ckeditor is being used in:
<input type="hidden" name="_csrf" value="<%= _csrf %>" id="csrf" />
Next, add the following lines to ckeditor/ckeditor.js around line 498.
var csrf = document.getElementsByName("_csrf");
var token = csrfitems[0].defaultValue;
You then need to add the hidden input on the form that the uploader uses on line 499 of ckeditor.js
<input type="hidden" name="_csrf" value="' + token + '" id="csrf" />
Here is the full line 499 just to see it in context:
var csrf = document.getElementsByName("_csrf");var token = csrfitems[0].defaultValue;
d.$.write(['<html dir="'+g+'" lang="'+i+'"><head><title></title></head><body style="margin: 0; overflow: hidden; background: transparent;">','<form enctype="multipart/form-data" method="POST" dir="'+g+'" lang="'+i+'" action="',CKEDITOR.tools.htmlEncode(f.action),
'"><label id="',a.labelId,'" for="',h,'" style="display:none">',
CKEDITOR.tools.htmlEncode(f.label),
'</label>
<input type="hidden" name="_csrf" value="' + token + '" id="csrf" /><input style="width:100%" id="',h,'" aria-labelledby="',a.labelId,'" type="file" name="',CKEDITOR.tools.htmlEncode(f.id||"cke_upload"),
Hopefully this saves some people the headaches I had to go through. This might not be the most elegant solution but it will allow the uploader to work across your sails site now.

How does one parse nested elements using express.bodyParser/node-formidable?

I'm using express.js with the bodyParser middleware (which, is technically node-formidable, behind the scenes).
I'd like to take and create a form that represents inputs for each of the data elements listed here:
{
"name" : "asdf",
"children" : [
{
"child_name" : "xyz1234",
"size" : 12
},
{
"child_name" : "1234aszds"
"size": 14
}
]
}
The number of children here will be dynamic, but I'd like to be able to add additional fields client-side, and have them map into req.body on the server.
Note, I'm looking for how to name the inputs in the client in order for the raw POST body to have the correct encoding to allow node-formidable to parse them out into an array.
The general way to achieve this is as follows (excluding non-essential attributes for clarity):
<input name="name"/>
<input name="children[0[child_name]"/>
<input name="children[0[size]"/>
<input name="children[1[child_name]"/>
<input name="children[1[size]"/>
Note that the square brackets are not balanced, this is required due to a "bug" in the "querystring" package (or, at least it would seem so, as of August 20th, 2013).
But, if the square brackets are left unbalanced, as above, the result is that this will be parsed into the object structure requested in my original question (i.e. this will be made available as "req.body").
The benefit of this is that it does not require any Javascript to do any "pre-flighting" on form submission.
Using an HTML form with input fields will be problematic since there is no easy way to define the structure without parsing each field name to determine its place in the structure.
For instance, if you posted the following:
<form action="/post" method="post">
<input type="text" name="data[name]">
<input type="text" name="data[children][][child_name]">
<input type="text" name="data[children][][size]">
<input type="text" name="data[children][][child_name]">
<input type="text" name="data[children][][size]">
<input type="submit" value="Submit">
</form>
Formidable would interpret into this JSON structure:
[ [ 'data[name]', 'name' ],
[ 'data[children][][child_name]', [ 'cname1', 'cname2' ] ],
[ 'data[children][][size]', [ 'csize1', 'csize2' ] ] ]
To post deep structures this way, I would recommend using AJAX and posting a complete JSON object instead, which I have outlined below.
The following should allow you to copy all of the fields directly into req.body. I elected to use the .mixin() helper from the utile library.
var utile = require('utile')
var form = new formidable.IncomingForm()
, fields = {}
form
.on('field', function (field, value) {
fields[field] = value
})
.on('end', function () {
req.body = utile.mixin(req.body, fields)
})
Based on your JSON in the question itself, you could then access your values like this:
console.log(req.body.children[0].child_name)
If you are looking for a native way of merging the objects, see Combine or merge JSON on node.js without jQuery for a way to do this.
Hopefully this answers the question and helps you out!

express.js - req.body?

I'm new to js,
I see this a lot in the code I'm reading
_.pick(req.body, ' ' , ' ')
What does req.body do?
And when can I say req.body.something?
req.body holds parameters that are sent up from the client as part of a POST request. See the API.
// POST user[name]=tobi&user[email]=tobi#learnboost.com
req.body.user.name
// => "tobi"
req.body.user.email
// => "tobi#learnboost.com"
// POST { "name": "tobi" }
req.body.name
// => "tobi"
(req.body, ' ' , ' ') --> here req is the parameter of your function and using this parameter your can access the properties over then url.
so look this example
suppose this is form
<form>
enter the name : <input type="text" name="name">
<button type ="submit"> submit </button>
</form>
so if you want to access the name -- which is filled by the user end.
so for this you can
do like this-> console.log(req.body.name); -- this will print the name (property) in console.

Resources