亚马逊MWS SubmitFeed Content-MD5 HTTP标头与亚马逊

时间:2016-10-21 15:08:26

标签: node.js meteor amazon-mws

我知道这个问题不是新问题,但我得到的所有解决方案都是PHP或我的问题与它们不同。

我正在使用MWS Feed API提交价格和数量更新的平面文件,并始终收到以下错误:

  

您为Feed传递的Content-MD5 HTTP标头与   我们为您的Feed计算的Content-MD5

我想在这里提出三个问题:

  1. ContentMD5Value参数是doc中给出的可选参数,但是如果我没有传递,那么它会说你必须输入ContentMD5Value。

  2. 正如我们给予亚马逊的ContentFeed一样。亚马逊为该文件创建contentMD5,然后将该contentMD5值与我们发送给亚马逊的contentMD5值进行比较。
    如果两者都匹配则OK,否则会抛出错误。但是如果假设我不发送文件,那么同样的错误也会与MD5不匹配。怎么可能?他们在为哪个文件计算MD5?因为我无法通过ContentFeed发送文件。

  3. 如果我在标题和参数中发送contentMD5并在正文中发送ContentFeed,我仍然会收到错误。

  4. 注意: - 我使用请求模块在标题和表单中的参数中发送contentMD5,并使用该模块计算签名,然后在正文中传递contentFeed

    我正在使用JavaScript(Meteor),我使用crpyto模块计算md5 首先,我认为我的md5是错的,但后来我尝试了一个在线网站,它会给我一个文件md5的md5。

    我的文件是:

      

    MD5值:d90e9cfde58aeba7ea7385b6d77a1f1e
         Base64Encodevalue:ZDkwZTljZmRlNThhZWJhN2VhNzM4NWI2ZDc3YTFmMWU =

    我从价格和数量更新中下载的平面文件: -

      

    https://sellercentral.amazon.in/gp/help/13461?ie=UTF8&Version=1&entries=0&

    我还在计算签名时给予ContentMD5Value来计算签名。

      

    的FeedType:' _POST_FLAT_FILE_PRICEANDQUANTITYONLY_UPDATE_DATA _'

    因为,我阅读了文档,因为我在标题中传递了MD5标题,并且还作为参数发送。

    亚马逊医生说:

      

    此前,亚马逊MWS接受MD5哈希作为Content-MD5标头   而不是参数。将其作为参数传递可确保MD5   value是方法签名的一部分,它可以阻止任何人访问   网络篡改了饲料内容。

         

    亚马逊MWS仍将接受Content-MD5标头,无论是否   包含ContentMD5Value参数。如果既有标题又有参数   使用,他们不匹配,你会收到一个   InvalidParameterValue错误。

    我正在使用request模块进行http请求。

    我正在以请求模块的形式传递所有必需的密钥,卖家ID等,并将FeedContent传递给正文。

    我尝试按如下方式发送文件:

    submitFeed的方法是: -

    submitFeed : function(){
        console.log("submitFeedAPI running..");
        app  = mwsReport({auth: {sellerId:'A4TUFSCXD64V3', accessKeyId:'AKIAJBU3FTBCJUIZWF', secretKey:'Eug7ZbaLljtrnGKGFT/DTH23HJ' }, marketplace: 'IN'});
        app.submitFeedsAPI({FeedType:'_POST_FLAT_FILE_PRICEANDQUANTITYONLY_UPDATE_DATA_'},Meteor.bindEnvironment(function(err,response){
            if(err){
                console.log("error in submit feed...")
                console.log(err)
            }
            else{
                console.log("suuccess submit feed....")
                console.log(response);
            }
        }))
    

    调用Amazon submitFeedAPI的方法是: -

        var submitFeedsAPI =  function(options, callback){
            console.log("submitFeedsAPI running...");
        var fileReadStream = fs.createReadStream('/home/parveen/Downloads/test/testting.txt');
            var contentMD5Value = crypto.createHash('md5').update(file).digest('base64');
    
        var reqForm = {query: {"Action": "SubmitFeed", "MarketplaceId": mpList[mpCur].id, "FeedType":options.FeedType,"PurgeAndReplace":false,"ContentMD5Value":contentMD5Value}}; 
                mwsReqProcessor(reqForm, 'submitFeedsAPI', "submitFeedsAPIResponse", "submitFeedsAPIResult", "mwsprod-0000",false,file, callback);
        }
    
    
    also try
    
        var fileReadStream = fs.createReadStream('/home/parveen/Downloads/test/testting.txt');
        var base64Contents = fileReadStream.toString('base64');
        var contentMD5Value = crypto.createHash('md5').update(base64Contents).digest('base64');
    

    mwsReqProcessor功能如下: -

     mwsReqProcessor = function mwsReqProcessor(reqForm, name, responseKey, resultKey, errorCode,reportFlag,file, callback) {
    
            reqOpt = {
                url: mwsReqUrl,
                method: 'POST',
                timeout: 40000,
                body:{FeedContent: fs.readFileSync('/home/parveen/feedContentFile/Flat.File.PriceInventory.in.txt')},
                json:true,
                form: null,
                headers: {
                    // 'Transfer-Encoding': 'chunked',
                    //'Content-Type': 'text/xml',
                   // 'Content-MD5':'ZDkwZTljZmRlNThhZWJhN2VhNzM4NWI2ZDc3YTFmMWU=',
                    // 'Content-Type': 'text/xml; charset=iso-8859-1'
                    'Content-Type':'text/tab-separated-values;charset=UTF-8'
                },
            }
            reqOpt.form = mwsReqQryGen(reqForm);
            var r = request(reqOpt, function (err, res, body){
                console.log(err)
                console.log(res)
            })
            // var form  = r.form();
            //form.append('FeedContent',fs.createReadStream('/home/parveen/feedContent//File/Flat.File.PriceInventory.in.txt'))
        }
    

    mwsReqQryGen生成方法: -

    mwsReqQryGen = function mwsReqQryGen(options) {
        var method     = (options && options.method) ? ('' + options.method) : 'POST',
            host       = (options && options.host)   ? ('' + options.host)   : mwsReqHost,
            path       = (options && options.path)   ? ('' + options.path)   : mwsReqPath,
            query      = (options && options.query)  ? options.query         : null,
    
    
            returnData = {
              "AWSAccessKeyId": authInfo.accessKeyId,
              "SellerId": authInfo.sellerId,
              "SignatureMethod": "HmacSHA256",
              "SignatureVersion": "2",
              "Timestamp": new Date().toISOString(),
               "Version":"2009-01-01",
            },
            key;
    
        if(query && typeof query === "object")
          for(key in query)
            if(query.hasOwnProperty(key)) returnData[key] = ('' + query[key]);
    
        if(authInfo.secretKey && method && host && path) {
    
          // Sort query parameters
          var keys = [],
              qry  = {};
    
          for(key in returnData)
            if(returnData.hasOwnProperty(key)) keys.push(key);
    
          keys = keys.sort();
          for(key in keys)
            if(keys.hasOwnProperty(key)) qry[keys[key]] = returnData[keys[key]];
          var sign = [method, host, path, qs.stringify(qry)].join("\n");
          console.log("..................................................")
          returnData.Signature = mwsReqSignGen(sign);
    
        }
    //console.log(returnData); // for debug
    
    return returnData;
    

    };

    我也试过以下: -

    reqOpt = {
        url: mwsReqUrl,
        method: 'POST',
        timeout: 40000,
        json:true,
        form: null,
        body:  {FeedContent: fs.createReadStream('/home/parveen/feedContentFile/Flat.File.PriceInventory.in.txt')},
        headers: {
            // 'Transfer-Encoding': 'chunked',
            //'Content-Type': 'text/xml',
           // 'Content-MD5':'ZDkwZTljZmRlNThhZWJhN2VhNzM4NWI2ZDc3YTFmMWU=',
            //   'Content-Type': 'text/xml; charset=iso-8859-1'
        },
    }
    

    我也试过没有JSON并直接发送文件读取流 身体,即:

    reqOpt = {
        url: mwsReqUrl,
        method: 'POST',
        timeout: 40000,
        form: null,
        body:  fs.createReadStream('/home/parveen/feedContentFile/Flat.File.PriceInventory.in.txt'),
        headers: {
            // 'Transfer-Encoding': 'chunked',
            //'Content-Type': 'text/xml',
         //   'Content-MD5':'ZDkwZTljZmRlNThhZWJhN2VhNzM4NWI2ZDc3YTFmMWU=',
            //   'Content-Type': 'text/xml; charset=iso-8859-1'
        },
    }
    

    但每次都会出现同样的错误:

      

    您为Feed传递的Content-MD5 HTTP标头与   我们为您的Feed计算的Content-MD5

    我想知道我在哪里做错了,或者提交Feed API并使用请求模块发送文件的正确方法是什么。

    我还尝试使用MWS上给出的代码来生成MD5但是相同 每次都发生错误。

    我的.txt文件如下:

    sku price   quantity
    TP-T2-00-M      2
    

    非常感谢任何帮助

4 个答案:

答案 0 :(得分:4)

最后我得到了解决方案,正如拉维上面所说的那样。 实际上,对于所有面临同样问题的人,我想在此清楚几点:

1)亚马逊市场 API doc没有提供适当的信息和示例。即使我猜文档没有更新。与在doc中一样,他们说ContentMD5Value参数值是可选的,这里是doc的链接: -

  

http://docs.developer.amazonservices.com/en_US/feeds/Feeds_SubmitFeed.html

您可以在那里检查他们是否明确提到该字段不是必需的,但如果您没有通过,则会给出错误,您必须传递内容MD5值。

  

这是错误的。 ContentMD5是必需属性。

2)他们在同一个文档中说,你需要发送文件数据,在字段键名称中输入xml或平面文件,即FeedContent

  

但是也不需要你可以发送任何名称为no的文件   需要为您只需要发送的文件提供FeedContent键   文件流。

3)他们会给出内容MDD5的相同错误,与你发送文件的天气不符,因为如果他们找不到文件而不是你发送的内容MD5将与之匹配。因此,如果您收到 ContentMD5不匹配错误,请检查以下内容: -

1)检查您是否为您的文件生成了正确的MD5代码,您可以通过他们在doc上提供的java代码来检查您是否生成了正确的代码。 您可以从以下链接获取:

  

http://docs.developer.amazonservices.com/en_US/dev_guide/DG_MD5.html

2)不要相信在线网站上生成MD5哈希和base64编码。

3)如果您的MD5与从他们给出的Java代码生成的MD5相匹配,那么很明显您的MD5是正确的,因此无需更改。

4)一旦你的MD5正确,之后如果你得到同样的错误: -

  

亚马逊MWS SubmitFeed Content-MD5 HTTP标头与   内容-MD5由亚马逊计算

ContentMD5不匹配。您只需要检查并且只有文件上传机制。 因为现在发送给亚马逊的文件不正确,或者你没有以正确的方式发送它。

检查文件上传: -

要查看是否发送了正确的文件,您需要查看以下内容: -

1)您需要将所需的参数(如sellerId,marketplaceId,AWSAccessKey等)作为查询参数发送。

2)如果使用node.js的请求模块,则需要将表单数据中的文件作为multipart发送,而不是看到Ravi给出的上述代码。

3)您只需将标题设置为: -

  

'Content-Type':'application / x-www-form-urlencoded'

不需要将标题发送为chunked或tab分隔等因为我不再需要它们甚至会让我感到困惑,因为有人在其他地方使用此标题时会使用此标题使用此标题。 所以最后因为我提交这个API,我不需要任何标题而不是application / x-www-form-urlencoded。

实施例: -

reqOpt = {
   url: mwsReqUrl,
   method: 'POST',
   formData: {
      my_file: fs.createReadStream('file.txt')
  },
  headers: {
     'Content-Type': 'application/x-www-form-urlencoded'
  },
qs: { }// all the parameters that you are using while creating signature.

创建contentMD5的代码是: -

var  fileData= fs.readFileSync('/home/parveen/Downloads/test/feed.txt','utf8');

var contentMD5Value = crypto.createHash('md5').update(fileData).digest('base64');

因为我面临的问题是因为我通过请求模块同时使用表单和表单数据所以我使用qs(查询字符串)转换表单数据,并将表单数据中的文件转换为multipart。

因此,您可以通过这种方式成功提交提交Feed的API。

欢迎任何疑问 谢谢

答案 1 :(得分:2)

亚马逊需要使用base64编码的文件的md5哈希值。

您的代码:

var fileReadStream = fs.createReadStream('/path/to/file.txt');
var file = fileReadStream.toString('base64'); //'[object Object]'
var contentMD5Value = crypto.createHash('md5').update(file).digest('base64');

错误地假定readStream的{​​{1}}将生成文件内容,实际上,此方法是从toString()继承并生成字符串Object

Base64编码字符串始终会产生您提到的'[object Object]'

如果要正确读取和编码哈希,可以执行以下操作:

'FEGnkJwIfbvnzlmIG534uQ=='

提供与以下PHP代码段相同的结果:

var fileContents = fs.readFileSync('/path/to/file.txt'); // produces a byte Buffer
var contentMD5Value = crypto.createHash('md5').update(fileContents).digest('base64'); // properly encoded

答案 2 :(得分:2)

抱歉迟到的回复,但为什么不尝试在form-data请求中的multipart中发送文件,并在请求模块的'qs'属性中发送其他queryStrings。 您可以按如下方式提交请求: -

 reqOpt = {
   url: mwsReqUrl,
   method: 'POST',
   formData: {
      my_file: fs.createReadStream('file.txt')
  },
  headers: {
     'Content-Type': 'application/x-www-form-urlencoded'
  },
  qs: { 
     AWSAccessKeyId: '<your AWSAccessKeyId>',
     SellerId: '<your SellerId>',
     SignatureMethod: '<your SignatureMethod>',
     SignatureVersion: '<your SignatureVersion>',
     Timestamp: '<your Timestamp>',
     Version: '<your Version>',
     Action: 'SubmitFeed',
     MarketplaceId: '<your MarketplaceId>',
     FeedType: '_POST_FLAT_FILE_PRICEANDQUANTITYONLY_UPDATE_DATA_',
     PurgeAndReplace: 'false',
     ContentMD5Value: '<your file.txt ContentMD5Value>',
     Signature: '<your Signature>'
 }
}

request(reqOpt, function(err, res){

})

答案 3 :(得分:0)

可能我来晚了,但是这里是C#的关键点:

1)多部分表单数据根本不起作用。完成以下操作(简化):

HttpContent content = new StringContent(xmlStr, Encoding.UTF8, "application/xml");
HttpClient client = new HttpClient();
client.PostAsync(query, content)

2)关于query

UriBuilder builder = new UriBuilder("https://mws.amazonservices.com/");
NameValueCollection query = HttpUtility.ParseQueryString(builder.Query);

query["AwsAccessKeyId"] = your_key_str;
query["FeedType"] = "_POST_ORDER_FULFILLMENT_DATA_";
... other required params
query["ContentMD5Value"] = Md5Base64(xmlStr);

builder.Query = query.ToString();
query = builder.ToString();

3)关于Md5base64

public static string Md5Base64(string xmlStr)
{
  byte[] plainTextBytes = Encoding.UTF8.GetBytes(xmlStr);
  MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider();
  byte[] hash = provider.ComputeHash(plainTextBytes);
  return Convert.ToBase64String(hash);
}