2

It is nasty to debug Amazon MWS Feed submissions because whatever you might be doing wrong Ama just returns the same senseless error message:

<Error>
  <Type>Sender</Type>
  <Code>InvalidParameterValue</Code>
  <Message>Either Action or Operation query parameter must be present.</Message>
</Error>

I am 100% positive that I build StringToSign correctly as well as calculate HMAC-SHA256, etc. It took me a few weeks to collect and adopt a stack of useful functions for hashing, signing, base64ing, etc. MWS requests. Written in pure Pascal they were all tested on Orders and Products APIs.

Now when it comes to Feeds API I am stuck on the above error. All parameters are equal to those generated by MWS Scratchpad. I tested the submission StringToSign generated by MWS Scratchpad, but no luck.

What I noticed so far: there is a difference between the number/values of headers generated by MWS Scratchpad and my app.

The Scratchpad generates the following headers (it least they are displayed):

  Host: mws.amazonservices.ca
  x-amazon-user-agent: AmazonJavascriptScratchpad/1.0 (Language=Javascript)
  Content-Type: text/xml

My app uses Indy (in XE4) TIdHTTP to make a request. When Amazon returns the above error, Request.RawHeaders.Text contains the following:

  Content-Length: 251
  x-amazon-user-agent: MyApp/1.1(Language=Delphi;Platform=Windows7)
  Content-Type: text/xml
  Host: mws.amazonservices.ca
  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  Accept-Encoding: identity
  User-Agent: MyApp/1.1(Language=Delphi;Platform=Windows7)

Looks like the extra the headers are added to Request object by default. My first question is: can those extra headers theoretically influence on the behavior? I.e. can they be the showstoppers?

My second question: is there any option to mofidy the list of default heades in Indy HTTP.Request? To go on with the debugging I would rather exclude extra headers to see if the request works.

UPDATE: (SignedString)

  AWSAccessKeyId=<AWSAccessKeyId>
  &Action=GetFeedSubmissionList
  &Merchant=<MerchantId>
  &SignatureMethod=HmacSHA256
  &SignatureVersion=2
  &Timestamp=2015-07-26T09%3A04%3A59Z
  &Version=2009-01-01
  &Signature=1OI0PVgL3uh5sFXxjCzaaWEwGmW6h5e0dgLUFkPgoXg%3D

UPDATE: (Complete HTTP Request/Response provided by TIdLogFile)

  Stat Connected.
  Sent 28.07.2015 12:28:11:
    POST / HTTP/1.1<EOL>
    Content-Type: text/xml; charset=us-ascii<EOL>
    Content-Length: 279<EOL>
    x-amazon-user-agent: MyAppNameAndVer<EOL>
    Host: mws.amazonservices.ca<EOL>
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8<EOL>
    Accept-Encoding: identity<EOL>
    User-Agent: Mozilla/3.0 (compatible; Indy Library)<EOL><EOL>

  Sent 28.07.2015 12:28:11: 
    AWSAccessKeyId=<AWSAccessKeyId>
    &Action=GetFeedSubmissionList
    &MWSAuthToken=<AWSAuthToken>
    &Merchant=<MerchantId>
    &SignatureMethod=HmacSHA256
    &SignatureVersion=2
    &Timestamp=2015-07-28T10%3A28%3A09Z
    &Version=2009-01-01
    &Signature=I6euLIiVDzjZ8bbdtF840K0TJCkGh4NrUbQPtQtu78A%3D

  Recv 28.07.2015 12:28:11:
    HTTP/1.1 400 Bad Request<EOL>
    Date: Tue, 28 Jul 2015 10:28:10 GMT<EOL>
    Server: AmazonMWS<EOL>
    x-mws-request-id: 7e63280d-1db5-4d10-af40-587747d032a8<EOL>
    x-mws-timestamp: 2015-07-28T10:28:11.048Z<EOL>
    x-mws-response-context: OmAlguEmW20QT07uIdb9d25xkX+JSS7uFr1rDvXIoqXMvbUFzUMt1b5Xl2WzDaJszbwr25N/J4c=<EOL>
    Content-Type: text/xml<EOL>
    Content-Length: 324<EOL>
    Vary: User-Agent<EOL>
    nnCoection: close<EOL><EOL>
    <?xml version="1.0"?><LF><ErrorResponse xmlns="https://mws.amazonservices.com/"><LF>  <Error><LF>    <Type>Sender</Type><LF>    <Code>InvalidParameterValue</Code><LF>    <Message>Either Action or Operation query parameter must be present.</Message><LF>  </Error><LF>  <RequestID>7e63280d-1db5-4d10-af40-587747d032a8</RequestID><LF></ErrorResponse><LF>
  Stat Disconnected.

Please note: I formatted the response for your convenience adding CRs after <EOL>

Confusing nnCoection header is explained here: Cneonction and nnCoection HTTP headers


My question is now resolved. Below is my way to thank people not breaking the Community standards ;-)

Days := 1;
repeat
  IsSuccessful := DebugAmazonFeedSubmissionProc;
  Inc(Days);
until IsSuccessful or (Days = 14);

if not IsSuccessful then
begin
  AskTheGods(@StackOverflow);
  Sleep(1000); // ...wake up and drink a cup'o'tea :)
  Expert := TRemyLebeau.Create('delphi');
  try
    Expert.ReviewTheCode;
    Expert.GetTheJobDone;
  finally
    // You may dispose the instance here,
    // but the Class is one of the most valuable assets of the Community.
    // Thank you, Remy!
  end;
end;
Community
  • 1
  • 1
Interface Unknown
  • 713
  • 10
  • 26
  • 1
    The error is self explanatory if you actually read it. The URL you are posting to is missing an `Action` or `Operation` parameter in the query portion of the URL, eg: `http://server/path?action=...` or `http://server/path?operation=...` That has nothing to do with HTTP headers at all. – Remy Lebeau Jul 26 '15 at 03:57
  • @RemyLebeau, Unfortunately this is not the reason. I would be happy if that was so easy... In initial request has the `Action` parameter. There are all required parameters in there. – Interface Unknown Jul 26 '15 at 09:04
  • What does the **complete** HTTP request look like? Including the request line that precedes the request headers. – Remy Lebeau Jul 26 '15 at 16:46
  • @RemyLebeau, can you please suggest how to get a complete (raw) Request? Is it stored in `Request.Source`? Before posting I set up all headers first, then put Amazon data to TStringStream and then `HTTP.Post(AUrl, StringStream)`. I set a breakpoint and noticed `Post` throws exception which is handled by exception handler, which returns `Request.RawHeaders.Text` – Interface Unknown Jul 27 '15 at 09:45
  • `Request.Source` is just a pointer to the `TStream` that you are posting. Which BTW, the data you are posting can be posted using a `TStringList` instead of a `TStream`. Either way, to get the complete request, you can use a packet sniffer like Wireshark (unless the URL is HTTPS), or a debugging proxy like Fiddler (which supports HTTPS), or attach one of Indy's `TIdLog...` components to the `TIdHTTP.Intercept` property. The exception is because the server is sending an error response. – Remy Lebeau Jul 27 '15 at 16:03
  • @RemyLebeau OP updated. Added HTTP Request/Response by `TIdLogFile`. Thank you! – Interface Unknown Jul 28 '15 at 11:06

1 Answers1

2

Your request Content-Type header is wrong. You have it set to text/xml; charset=us-ascii, but you are not actually sending XML data to begin with. You need to set the Content-Type (via the TIdHTTP.Request.ContentType property) to application/x-www-form-urlencoded instead, per the MWS GetFeedSubmissionList documentation.

The TStrings version of TIdHTTP.Post() would set the ContentType for you, but you are using the TStream version instead so you have to set the ContentType manually. In this case, I do suggest you switch to the TStrings version, and let it handle encoding the form fields for you:

var
  PostData: TStringList;
begin
  PostData := TStringList.Create;
  try
    PostData.Add('AWSAccessKeyId=<AWSAccessKeyId>');
    PostData.Add('Action=GetFeedSubmissionList');
    PostData.Add('MWSAuthToken=<AWSAuthToken>');
    PostData.Add('Merchant=<MerchantId>');
    PostData.Add('SignatureMethod=HmacSHA256');
    PostData.Add('SignatureVersion=2');
    PostData.Add('Timestamp=2015-07-28T10:28:09Z'); // <-- NOT percent encoded yet!
    PostData.Add('Version=2009-01-01');
    PostData.Add('Signature=I6euLIiVDzjZ8bbdtF840K0TJCkGh4NrUbQPtQtu78A=');  // <-- NOT percent encoded yet!

    IdHTTP1.Request.CustomHeaders.Values['x-amazon-user-agent'] := 'MyAppNameAndVer';
    IdHTTP1.Post('http://mws.amazonservices.ca', PostData);
  finally
    PostData.Free;
  end;
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770