gpt4 book ai didi

delphi - 使用 Delphi/Indy 的亚马逊 MWS API 调用

转载 作者:行者123 更新时间:2023-12-03 18:36:40 28 4
gpt4 key购买 nike

我正在开发一个简单的应用程序来“对话”亚马逊 MWS API。因为这里有很多现有代码在起作用,所以我需要在 Delphi 2010 中使用 Indy 10 (10.5.5) 组件来完成这项工作,我过去曾成功地使用这些组件与许多其他 API 集成。但是,Amazon API 似乎对最小的细节非常敏感,以至于我的所有调用都被已经臭名昭著的“SignatureDoesNotMatch”错误消息拒绝。

这是我到目前为止所取得的成就:

1) 我的应用程序将组装一个请求,使用 HMAC-SHA256 对其进行签名(使用 OpenSSL 库)并将其发送到 Amazon 服务器端点。

2) 仅 HMAC 签名本身就被证明是一个挑战,但它现在 100% 的时间都可以正常工作(根据 Amazon Scrachpad 生成的请求进行验证)。

然而,正如我之前指出的,我的请求总是被 MWS 服务器拒绝并出现 SignatureDoesNotMatch 错误,即使它们可验证是正确的。我能想到的唯一可能导致问题的是 Indy 处理 POST 请求的方式,特别是文本编码过程。

有没有人成功地将 Delphi/Indy 客户端连接到 MWS?如果是这样,使用了什么样的 TIdHTTP 设置?这是我所拥有的:

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 Client Options
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);

// generate the timestamp per Amazon specs
Timestamp:=TIso8601.UtcDateTimeToIso8601(TIso8601.ToUtc(Now));
// we can change the timestamp to match a value from the Scratchpad as a way to validate the signature:
//Timestamp:='2014-05-09T20:32:28Z';

// add required parameters from API function GetOrder
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);
// generate the signature using the parameters above
Params.Add('Signature='+GetSignature(Params.Text,CallUri));

// after generating the signature, make sure all values are properly URL-Encoded
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);

// there are two ways to make the call...
// #1: according to the documentation, all parameters are supposed to be in
// the URL, and the body stream is supposed to be empty
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='+#$D#$A+HTTP.Request.RawHeaders.Text);
Log('Protocol Exception:'+#$D#$A+StringReplace(E1.ErrorMessage,#10,#$D#$A,[rfReplaceAll]));
end;
on E2:Exception do
Log('Unknown Exception: '+E2.Message);
end;
Log('ResponseText='+S);
finally
SS.Free;
end;

// #2: both the Scratchpad and the CSharp client sample provided by Amazon
// do things in a different way, though... they POST the parameters in the
// body of the call, not in the query string
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='+#$D#$A+HTTP.Request.RawHeaders.Text);
Log('Protocol Exception:'+#$D#$A+StringReplace(E1.ErrorMessage,#10,#$D#$A,[rfReplaceAll]));
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;

如果我在 Scratchpad 中组装一个 GetOrder 调用,然后将该调用的时间戳粘贴到上面的代码中,我在这里得到完全相同的查询字符串,具有相同的签名和大小等。但是我的 Indy 请求必须对事物进行不同的编码,因为MWS 服务器不喜欢这个电话。

我知道 MWS 至少在“读取”查询字符串,因为如果我将时间戳更改为旧日期,它会返回“请求过期”错误。

亚马逊的技术支持毫 headless 绪,每天都会发布一条消息,其中包含“确保 key 正确”之类的基本内容(好像在没有有效 key 的情况下使用 HMAC-SHA256 和 MD5 进行签名一样!!!!)。

还有一件事:如果我使用 Wireshark 从上面的代码和 C-Sharp Amazon 示例代码中“观察”原始请求,我也无法区分。但是,我不确定 Wireshark 是否区分 UTF-8 和 ASCII 或所显示文本的任何编码。我仍然认为这与糟糕的 UTC-8 编码或类似的东西有关。

欢迎和赞赏有关如何正确编码 API 调用以取悦亚马逊之神的想法和建议。

最佳答案

发现了问题:Indy(和 Synapse 也一样)将端口号添加到“主机”标题行,直到我用 Fiddler 更仔细地观察标题(感谢@Graymatter !!!),我才意识到这一点。

当我将端点更改为 mws.amazonservices.com:443(而不仅仅是 mws.amazonservices)时,我的签名的计算方式与 AWS 服务器的计算方式相同,并且一切正常。

关于delphi - 使用 Delphi/Indy 的亚马逊 MWS API 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23573799/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com