Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

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

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

Problem

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 
   */
    //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";
    }
    //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"; //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

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.

Problem courtesy of: metalaureate

Solution

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 
    */

    //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
Solution courtesy of: metalaureate

Discussion

View additional discussion.



This post first appeared on Node.js Recipes, please read the originial post: here

Share the post

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

×

Subscribe to Node.js Recipes

Get updates delivered right to your inbox!

Thank you for your subscription

×