如何在node.js / Javascript中签署Amazon Simple Pay按钮

时间:2013-07-05 23:14:38

标签: node.js amazon-web-services amazon-pay

我花了6个多小时试图按照亚马逊的说明在node.js中签署一个简单付款按钮表单。我无法接受信号,我已经尝试了令人困惑的指令的每一种排列。任何人都可以帮助我摆脱痛苦吗?

我得到的错误是

  

输入参数签名无效

这是我的程序

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);

将参数转换为

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

我将输出连接并粘贴到此表单以进行测试

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

以下是亚马逊文档

http://docs.aws.amazon.com/AmazonSimplePay/latest/ASPAdvancedUserGuide/Sig2CreateSignature.html

  

如何生成签名

     

创建签名

     

创建稍后需要的规范化查询字符串   过程:

     

使用自然参数名称对UTF-8查询字符串组件进行排序   字节排序。

     

参数可以来自GET URI或POST主体(何时   Content-Type是application / x-www-form-urlencoded)。

     

URL根据以下内容对参数名称和值进行编码   规则:

     

不要对RFC 3986中任何未保留的字符进行URL编码   定义

     

这些未保留的字符是A-Z,a-z,0-9,连字符( - ),   下划线(_),句号(。)和波浪号(〜)。

     

百分比用%XY编码所有其他字符,其中X和Y是十六进制   字符0-9和大写字母A-F。

     

百分比编码扩展的UTF-8字符,格式为%XY%ZA ....

     

百分比将空格字符编码为%20(而不是+,常见   编码方案。)。

     

注意目前,所有AWS服务参数名称都使用未保留   字符,所以你不需要编码它们。但是,你可能想要   包含处理使用保留的参数名称的代码   字符,以备将来使用。分离编码参数   来自其编码值的名称,带等号(=)(ASCII   字符61),即使参数值为空。

     

用&符号(&amp;)(ASCII码38)分隔名称 - 值对。

     

根据以下伪语法创建要签名的字符串   (“\ n”表示ASCII换行符。)

     

StringToSign = HTTPVerb +“\ n”+ ValueOfHostHeaderInLowercase +“\ n”+   HTTPRequestURI +“\ n”+ CanonicalizedQueryString HTTPRequestURI组件是HTTP绝对路径   URI的组件,但不包括查询字符串。如果   HTTPRequestURI为空,使用正斜杠(/)。

     

使用您刚刚创建的字符串计算符合RFC 2104的HMAC,   您的秘密访问密钥作为密钥,SHA256或SHA1作为哈希   算法

     

有关详细信息,请转到http://www.ietf.org/rfc/rfc2104.txt

     

将结果值转换为base64。

     

使用结果值作为签名请求的值   参数。

     

重要事项您在请求中发送的最终签名必须是URL   按照RFC 3986中的规定进行编码(有关更多信息,请转到   http://www.ietf.org/rfc/rfc3986.txt)。如果您的工具包URL编码您的   最终请求,然后它处理所需的URL编码   签名。如果您的工具包没有对最终请求进行URL编码,那么   确保在将签名包含在URL中之前对其进行URL编码   请求。最重要的是,确保签名仅为URL编码   一旦。一个常见的错误是在签名期间手动对其进行URL编码   形成,然后再次当工具包URL编码整个   请求。有关创建的高级过程的信息   按钮,请参阅动态创建按钮表单。

     

在以下示例中,插入了新行以生成   示例更容易阅读。显式'\ n'用于新行所在的位置   必需的。

     

以下是使用POST的Amazon Simple Pay请求示例。

     

                                                                                            以下是要用于的字符串示例   StringToSign在前面的例子中。

     

POST \ n authorize.payments-sandbox.amazon.com \ n / pba / paypipeline \ n   SignatureMethod = HmacSHA256&amp; SignatureVersion = 2   &amp; accessKey = YourCallerKey&amp; amount = USD%201.1&amp; cobrandingStyle = logo   &amp; description = Test%20Widget&amp; immediateReturn = 0   &amp; ipnUrl = http%3A%2F%2Fyourwebsite.com%2Fipn&amp; processImmediate = 1   &安培; referenceId = YourReferenceId   &amp; returnUrl = http%3A%2F%2Fyourwebsite.com%2Freturn.html了解更多   生成签名的示例,请参阅附录:示例代码。

     

有关正确签署按钮表单的信息,请参阅如何签名   你的按钮形式正确。

1 个答案:

答案 0 :(得分:2)

对于后人,这是解决方案:

1)不要乱用简单的按钮 - 改为使用FPS

2)在无数重叠的文件中,我发现这是最简单和最清晰的:http://docs.aws.amazon.com/AmazonFPS/latest/FPSBasicGuide/SendingaCBUIRequest.html

3)使用encodeURIComponent而不是encodeURI - 这是我最大的最令人沮丧的错误

此代码将正确签署Amazon FPS请求(假设为hmac和nconf加密以进行配置)

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;
}