I am developing a simple application for "communicating" with the Amazon MWS API. Since there is a lot of existing code here, I need to do this in Delphi 2010 with the Indy 10 components (10.5.5) that I have successfully used to integrate with many other APIs in the past. However, the Amazon API seems incredibly sensitive to the smallest detail, to the point where all my calls are denied the already infamous "SignatureDoesNotMatch" error message.
Here is what I have done so far:
1) My application will collect the request, sign it using HMAC-SHA256 (using the OpenSSL libraries) and send it to the endpoint of the Amazon server.
2) The HMAC signature alone turned out to be an end in itself, but now it works correctly in 100% of cases (as verified by requests generated by Amazon Scrachpad).
However, as I mentioned earlier, my requests are always rejected by the MWS server with a SignatureDoesNotMatch error, although they are reliably correct. The only thing I can think of that can cause problems is how Indy can handle POST requests, in particular the text encoding process.
Has anyone been successful in connecting a Delphi / Indy client to MWS? If so, what TIdHTTP settings were used? Here is what I have:
procedure TAmazon.TestGetOrder(OrderID:String);
const AwsAccessKey = 'MyAccessKey';
AwsSecretKey = 'MySecretKey';
MerchantID = 'MyMerchantID';
MarketplaceID = 'MyMarketplaceID';
ApiVersion = '2013-09-01';
CallUri = '/Orders/2013-09-01';
var HTTP:TIdHTTP;
SSL:TIdSSLIOHandlerSocketOpenSSL;
SS:TStringStream;
Params:TStringList;
S,Timestamp,QueryString,Key,Value:String;
i:Integer;
begin
HTTP:=TIdHTTP.Create(nil);
SSL:=TIdSSLIOHandlerSocketOpenSSL.Create(nil);
Params:=TStringList.Create;
try
Params.Delimiter:='&';
Params.StrictDelimiter:=True;
HTTP.HTTPOptions:=HTTP.HTTPOptions+[hoKeepOrigProtocol]-[hoForceEncodeParams];
HTTP.ConnectTimeout:=5000;
HTTP.ReadTimeout:=20000;
HTTP.ProtocolVersion:=pv1_1;
HTTP.IOHandler:=SSL;
HTTP.HandleRedirects:=True;
HTTP.Request.Accept:='text/plain, */*';
HTTP.Request.AcceptLanguage:='en-US';
HTTP.Request.ContentType:='application/x-www-form-urlencoded';
HTTP.Request.CharSet:='utf-8';
HTTP.Request.UserAgent:='MyApp/1.0 (Language=Delphi)';
HTTP.Request.CustomHeaders.AddValue('x-amazon-user-agent',HTTP.Request.UserAgent);
Timestamp:=TIso8601.UtcDateTimeToIso8601(TIso8601.ToUtc(Now));
Params.Add('Action=GetOrder');
Params.Add('SellerId='+MerchantID);
Params.Add('AWSAccessKeyId='+AwsAccessKey);
Params.Add('Timestamp='+Timestamp);
Params.Add('Version='+ApiVersion);
Params.Add('SignatureVersion=2');
Params.Add('SignatureMethod=HmacSHA256');
Params.Add('AmazonOrderId.Id.1='+OrderID);
Params.Add('Signature='+GetSignature(Params.Text,CallUri));
for i:=0 to Params.Count-1 do begin
Key:=Params.Names[i];
Value:=ParamEnc(Params.ValueFromIndex[i]);
QueryString:=QueryString+Key+'='+Value+'&';
end;
Delete(QueryString,Length(QueryString),1);
SS:=TStringStream.Create;
try
try
Log('POST '+CallUri+'?'+QueryString);
S:=HTTP.Post('https://mws.amazonservices.com'+CallUri+'?'+QueryString,SS);
except
on E1:EIdHTTPProtocolException do begin
Log('RawHeaders='+
Log('Protocol Exception:'+
end;
on E2:Exception do
Log('Unknown Exception: '+E2.Message);
end;
Log('ResponseText='+S);
finally
SS.Free;
end;
SS:=TStringStream.Create(QueryString,TEncoding.UTF8);
try
try
SS.Seek(0,0);
Log('POST '+CallUri+' (parameters in body/stream)');
S:=HTTP.Post('https://mws.amazonservices.com'+CallUri,SS);
except
on E1:EIdHTTPProtocolException do begin
Log('RawHeaders='+
Log('Protocol Exception:'+
end;
on E2:Exception do
Log('Unknown Exception: '+E2.Message);
end;
Log('ResponseText='+S);
finally
SS.Free;
end;
finally
Params.Free;
SSL.Free;
HTTP.Free;
end;
end;
GetOrder Scratchpad, , , .. Indy , MWS .
, MWS, , "" , , , " ".
Amazon , , ", " ( HMAC-SHA256 MD5 !!!!).
: Wireshark "" , C-Sharp Amazon, . , , Wireshark UTF-8 ASCII - . , UTC-8 - .
, API, , .