我花了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了解更多 生成签名的示例,请参阅附录:示例代码。
有关正确签署按钮表单的信息,请参阅如何签名 你的按钮形式正确。
答案 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;
}