1

在过去的 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 编码。

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

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

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

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

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

使用与号 ( & ) 分隔名称-值对(ASCII 代码 38)。

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

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

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

如需更多信息,请访问http://www.ietf.org/rfc/rfc2104.txt

将结果值转换为 base64。

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

重要 您在请求中发送的最终签名必须按照 RFC 3986 中的规定进行 URL 编码(有关更多信息,请访问 http://www.ietf.org/rfc/rfc3986.txt)。如果您的工具包 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 &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 有关生成签名的更多示例,请参阅附录:示例代码。

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

4

1 回答 1

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;
}
于 2013-07-06T16:38:23.760 回答