如何在node.js / Javascript中签署Amazon Simple Paybutton

我花了6个小时试图按照Amazon的说明在node.js中签署一个Simple Paybutton。 我不能接受签署,我已经尝试了混乱的说明的每一个排列。 任何人都可以帮助我摆脱苦难吗?

我得到的错误是

input参数签名无效

这是我的程序

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 

我连接并将输出粘贴到此表单进行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> 

这里是亚马逊的文档

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

如何生成签名

创build签名

创build此过程中稍后需要的规范化查询string:

按照自然字节顺序,通过参数名称对UTF-8查询string组件进行sorting。

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

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

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

这些非保留字符是AZ,az,0-9,连字符( – ),下划线(_),句点(。)和波浪号(〜)。

使用%XY对所有其他字符进行百分比编码,其中X和Y是hex字符0-9和大写AF。

百分号以%XY%ZA的forms编码扩展的UTF-8字符….

百分比将空格字符编码为%20(而不是+,如常见的编码scheme那样)。

注意目前所有AWS服务参数名称都使用非保留字符,因此您不需要对其进行编码。 但是,您可能希望包含代码来处理使用保留字符的参数名称,以备将来使用。 即使参数值为空,也可以使用等号(=)(ASCII字符61)将编码后的参数名与其编码值分开。

用&符号(ASCII代码38)分隔名称 – 值对。

根据以下伪文法创build​​要签名的string(“\ n”代表ASCII换行符)。

StringToSign = HTTPVerb +“\ n”+ ValueOfHostHeaderInLowercase +“\ n”+ HTTPRequestURI +“\ n”+ CanonicalizedQueryString HTTPRequestURI组件是URI的HTTP绝对path组件,直到但不包括查询string。 如果HTTPRequestURI为空,请使用正斜杠(/)。

使用刚刚创build的string,秘密访问密钥作为密钥以及SHA256或SHA1作为哈希algorithm来计算符合RFC 2104的HMAC。

欲了解更多信息,请访问http://www.ietf.org/rfc/rfc2104.txt 。

将结果值转换为base64。

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

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

在以下示例中,插入了新行以使示例更易于阅读。 在需要新行的地方使用明确的'\ n'。

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

以下是上例中用于StringToSign的string示例。

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有关生成签名的更多示例,请参阅附录:示例代码。

有关正确签署button表单的信息,请参阅如何正确签署button表单。

为了后代,这里是解决scheme:

1)不要搞乱简单的工资button – 使用FPS代替

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

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

此代码将正确签署Amazon FPS请求(假设configuration为hmac和nconf的encryption)

 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'); // eg, authorize.payments.amazon.com; var path=nconf.get('awsPayments:path'); //eg /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; } 
Interesting Posts