3

我们使用亚马逊产品广告 API 在他们的网站上进行重新定价。对于那些不知道它的人来说,它基本上可以让您查询亚马逊数据库,而无需执行诸如抓取网页之类的耗时操作。它对我们非常有效,或者已经有效。他们现在正在为卖家解散这个 API,并在他们的 MWS 服务上将我们转移到一个新的 API。

据我所知,这些电话略有不同。两个明显的区别是 PA API 使用 HTTP 和 GET,而 MWS 使用 HTTPS 和 POST。

我不完全确定 Amazon 文档中的示例是否正确,因为它们提供了创建为哈希签名 URL 请求的示例。这就是我们过去在使用 GET 方法的 PA-API 上成功执行此操作的方式。但是你可以为 POST 做到这一点吗?我认为这是不可能的,尽管我可能是错的,这肯定是这些例子所暗示的。

无论如何,创建签名 URL 是行不通的!所以我决定看看在 Delphi 中使用某种较低级别的 HTTPS POST。

我在这里搜索过,有几个不同的例子,但我无法让它们中的任何一个正常工作。一些示例似乎使用 INDY 10,不幸的是,我们被困在 INDY 9 上(出于兼容性原因)。我还查看了 WININET 包装器类型函数,但除了异常或错误之外,我无法返回结果。

所以这就是我在这里寻求帮助的原因。

如何使用 Delphi 对亚马逊 MWS 进行格式正确的调用?我尝试过以下页面中的示例:

如何在 Delphi 中发出 HTTPS POST 请求?

等等等等所有我能找到的!

但是我得到了像“错误请求”(异常)这样的错误。

我曾尝试使用 Fiddler(如其他地方所建议的那样)来查看正在发生的事情,但还不能完全理解它(尽管我可以在其中编写一个有效的调用!)

所以我正在寻找一些指示或方向。我真的不想升级 INDY 或添加新库。我只想保持原样并使用我可用的东西。我们正在使用 D2007。

为了说明需要什么,我需要进行这种调用(GetServiceStatus最简单的):

POST /Products/2011-10-01?AWSAccessKeyId=<ACCESSKEY>
  &Action=GetServiceStatus
  &SellerId=<SELLERID>
  &SignatureVersion=2
  &Timestamp=2012-02-14T13%3A26%3A42Z
  &Version=2011-10-01
  &Signature=dtAvv595blmv%2FnV0h2Yr5bCGzKYXid0hkOuCmZOb3bc%3D
  &SignatureMethod=HmacSHA256

到这个端点:

https://mws.amazonservices.co.uk/Products/2011-10-01

我认为我的问题出在我尝试过的所有示例中,我不知道如何正确设置调用,这可能就是我收到错误请求错误的原因。

所以,一个很好的简单解决方案将不胜感激!


文档链接和观察:

这是 API 的开发人员指南:

https://images-na.ssl-images-amazon.com/images/G/02/mwsportal/doc/en_US/bde/MWSDeveloperGuide._V161846143_.pdf

这是我们将使用的 API 的特定部分(替换当前的产品广告 API):

https://images-na.ssl-images-amazon.com/images/G/02/mwsportal/doc/en_US/bde/MWSDeveloperGuide._V161846143_.pdf

这里最基本的函数是 GetServiceStatus 函数。它不带任何参数。但是,它仍然需要使用来自亚马逊的凭证(卖家 ID、MWS 访问密钥和密钥(用于生成签名)进行身份验证和“签名”。我的感觉是,如果我可以让最简单的功能正常工作,那么其余的将随之而来。但是身份验证问题使找到解决方案变得如此困难。没有测试帐户。而且时间戳(以及因此调用的签名)到期和几分钟。

还有一个“迁移指南”:

https://images-na.ssl-images-amazon.com/images/G/02/mwsportal/doc/en_US/products/MWSProductsApiMigrationGuide._V140058392_.pdf

但是这个文件确实包含一些错误。例如,端点不正确。


我最新的代码公司 ssl、cookie 管理器等:

var
  LHTTP                : TIdHTTP;
  IdSSLIOHandlerSocket : TIdSSLIOHandlerSocket;
  IdCookieManager      : TIdCookieManager;
  LParams              : TStringList;
  LResponse            : string;

begin
  IdCookieManager:=TIdCookieManager.Create(self);
  IdSSLIOHandlerSocket:=TIdSSLIOHandlerSocket.Create(self);
  with IdSSLIOHandlerSocket do begin
    SSLOptions.Method := sslvSSLv3;
  end;

  LHTTP := TIdHTTP.Create(Self);
  with LHTTP do begin
    CookieManager:=IdCookieManager;
    AllowCookies:=true;
    IOHandler:=IdSSLIOHandlerSocket;
    Request.ContentType:='text/xml';
    Port:=443;
    HandleRedirects:=true;
    Host:='mws.amazonservices.co.uk';
    ProtocolVersion:=pv1_1
  end;


  LParams := TStringList.Create;
  try
    LParams.Add('AWSAccessKeyId=<ACCESSKEY>');
    LParams.Add('Action=GetServiceStatus');
    LParams.Add('SellerId=<SELLERID>)');
    LParams.Add('SignatureVersion=2');
    LParams.Add('Timestamp=2012-02-15T13%3A00%3A07Z');
    LParams.Add('Version=2011-10-01');
    LParams.Add('Signature=viPlDAbzEBwlTAwq4hNaZi%2Fa1Klf7qIXIP%2BKUsOcJTI%3D');
    LParams.Add('SignatureMethod=HmacSHA256');
    LResponse:=LHTTP.Post('https://mws.amazonservices.co.uk/Products/2011-10-01?', LParams);
    ShowMessage( LResponse );
  except
    on E: Exception do
      ShowMessage('ouch! ' + E.Message );
  end;
  LHTTP.Free;
  IdSSLIOHandlerSocket.Free;
  IdCookieManager.Free;
end;

这是基于亚马逊期望收到的暂存器。最后的用户代理是可选的,不是必需的,不是签名过程的一部分:

POST /Products/2011-10-01?AWSAccessKeyId=<ACCESSID>
  &Action=GetServiceStatus
  &SellerId=<SELLERID>
  &SignatureVersion=2
  &Timestamp=2012-02-15T13%3A00%3A07Z
  &Version=2011-10-01
  &Signature=viPlDAbzEBwlTAwq4hNaZi%2Fa1Klf7qIXIP%2BKUsOcJTI%3D
  &SignatureMethod=HmacSHA256 HTTP/1.1
Host: mws.amazonservices.co.uk
x-amazon-user-agent: AmazonJavascriptScratchpad/1.0 (Language=Javascript)
Content-Type: text/xml

我试图从 Fiddler 获取信息,但它没有显示来自软件的任何流量,但它必须与 Amazon 通信才能获得“400 bad request”错误。奇怪的。

4

3 回答 3

4

我会使用 TIdHTTP,在 HTTPOptions 中使用您的凭据(如果适用)进行设置,然后执行以下操作:

procedure ...
var
  LHTTP: TIdHTTP;
  LParams: TStringList;
  LResponse: string;
begin
  LHTTP := TIdHTTTP.Create;
  LParams := TStringList.Create;
  try
    // setup params, basically you're doing key-value pairs that will be encoded in the post
    // as KEY1=VALUE1&key2=value2&KEY3=value3, etc.
    // in the URL, stuff after ? are parameters
    // you don't have to worry about encoding parameters, indy will do it for you
    LParams['AWSAccessKeyId'] := '<ACCESSKEY>';
    LParams['Action'] := 'GetServiceStatus';
    LParams['SellerId'] := '<SELLERID>'
    LParams['SignatureVersion'] := '2';
    // adjust timestamp
    LParams['Timestamp'] := '2012-02-14T13%3A26%3A42Z';
    LParams['Version'] := '2011-10-01'
    // adjust signature...
    LParams['Signature'] := 'dtAvv595blmv%2FnV0h2Yr5bCGzKYXid0hkOuCmZOb3bc%3D';
    LParams['SignatureMethod'] := 'HmacSHA256';
    LResponse := LHTTP.Post('https://mws.amazonservices.co.uk/Products/2011-10-01', LParams);
    ShowMessage( LResponse );
  except
    on E: Exception do
      ShowMessage('ouch! ' + E.Message );
  end;
end;

我认为您需要将“/Products/2011-10-01”替换为您要搜索的日期,即“/Products/2012-02-14”等等......

此外,如果我没记错的话,需要 SSL 库......

于 2012-02-14T15:27:07.090 回答
1

只是为了让每个人都知道我放弃了尝试为此工作获取 POST 解决方案 - 我对无法更接近结果并浪费太多编程时间感到非常沮丧。

相反,我回到了基础,我将字符串更改为 GET 而不是 POST,然后创建了一个参数化的 URL,然后我调用它来获取 XML 结果。

也许在技术上或美学上不是最好的解决方案,但是,嘿,它有效!

但是感谢大家的帮助和建议,不胜感激。

于 2012-02-17T17:24:50.313 回答
1

只是为了让你知道,我分享了你的痛苦与这个问题,最终转到了 GET 格式,最后阅读了手册

使用自然字节顺序按参数名称对 UTF-8 查询字符串组件进行排序。参数可以来自 GET URI 或 POST 正文(当 Content-Type 为 application/x-www-form-urlencoded 时)。

如果您在标题中设置上述内容类型,它将起作用

于 2012-08-24T23:46:58.327 回答