You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi使用IPWorks调用Amazon MWS接口签名不匹配问题求助

Delphi调用Amazon MWS API SignatureDoesNotMatch错误修复方案

使用环境

  • Delphi 10.2.3 Tokyo
  • IPWorks SSL、IPWorks Encrypt组件

核心错误点

1. 参数拼接错误

你使用&作为参数分隔符,这是HTML转义后的符号,实际URL/表单参数应该直接用&,导致参数解析错误,签名计算的输入串和服务端不一致。

2. 参数未按要求排序

AWS签名规则要求所有请求参数必须按照键的ASCII码升序排序后再拼接,你当前的参数顺序是自定义的,不符合规则。

3. 换行符转义错误

Delphi中字符串内的\n不会被解析为实际换行符,需要用#10表示LF换行,导致签名输入串的结构和服务端要求不符。

4. 重复Base64编码

你已经将TipcHash组件的EncodeHash属性设为True,调用ComputeHash后得到的HashValue已经是Base64编码后的结果,后续额外做了一次Base64编码属于重复操作,直接导致签名值错误。

5. 请求参数放置位置错误

POST请求的表单参数应该放在请求体中,不应该拼接在URL的query部分,不符合MWS API的请求规范。

6. URL编码规则不匹配

Delphi自带的TNetEncoding.URL.Encode默认会将空格转为+,而AWS要求空格必须转为%20,且需要符合RFC 3986编码规则,编码不一致也会导致签名错误。

修复后代码示例

object HTTPS: TipwHTTP
  FollowRedirects = frAlways
  SSLCertStore = 'MY'
  OnTransfer = HTTPSTransfer
end

object HashMaker: TipcHash
  Algorithm = haHMACSHA256
  EncodeHash = True
end

// 符合AWS要求的URL编码方法,空格转%20,遵循RFC3986
function AwsUrlEncode(const S: string): string;
var
  I: Integer;
  C: Char;
begin
  Result := '';
  for I := 1 to Length(S) do
  begin
    C := S[I];
    if C in ['A'..'Z', 'a'..'z', '0'..'9', '-', '_', '~', '.'] then
      Result := Result + C
    else if C = ' ' then
      Result := Result + '%20'
    else
      Result := Result + '%' + IntToHex(Ord(C), 2);
  end;
end;

function GetISO8601DateTimeStr(const ADateTime: TDateTime): String;
var
  d: String;
  l: Integer;
begin
  d := DateToISO8601(TTimeZone.Local.ToUniversalTime(ADateTime), True);
  l := Length(d);
  d := Copy(d, 1, l - 5) + 'Z'; // 移除毫秒部分
  Result := d;
end;

const
  DOMAIN_NAME = 'https://mws.amazonservices.com/Orders/2013-09-01';
var
  sl: TStringList;
  param, signature, signInput: String;
  I: Integer;
begin
  sl := TStringList.Create;
  try
    // 先添加未编码的参数,键值分开,方便后续排序和编码
    sl.Values['AWSAccessKeyId'] := edtAwsAccessKey.Text;
    sl.Values['Action'] := 'ListOrders';
    sl.Values['CreatedAfter'] := GetISO8601DateTimeStr(dtpOrdersCreatedFrom.Date);
    sl.Values['MWSAuthToken'] := edtMWSAuthToken.Text; // 注意这里原代码写错了,把MWSAuthToken的值填成了MarketplaceID
    sl.Values['MarketplaceId.Id.1'] := edtMarketplaceID.Text;
    sl.Values['SellerId'] := edtSellerID.Text;
    sl.Values['SignatureMethod'] := 'HmacSHA256';
    sl.Values['SignatureVersion'] := '2';
    sl.Values['Timestamp'] := GetISO8601DateTimeStr(Now);
    sl.Values['Version'] := '2013-09-01';

    // 参数按ASCII升序排序
    sl.Sorted := True;

    // 编码后拼接参数串
    param := '';
    for I := 0 to sl.Count - 1 do
    begin
      if I > 0 then param := param + '&';
      param := param + AwsUrlEncode(sl.Names[I]) + '=' + AwsUrlEncode(sl.ValueFromIndex[I]);
    end;

    // 构造签名输入串,用#10表示换行
    signInput := 'POST'#10 +
                 'mws.amazonservices.com'#10 +
                 '/Orders/2013-09-01'#10 +
                 param;

    // 计算签名,不要重复编码
    HashMaker.Key := edtSecretKey.Text;
    HashMaker.Algorithm := haHMACSHA256;
    HashMaker.InputMessage := signInput;
    HashMaker.ComputeHash;
    signature := HashMaker.HashValue;

    // 签名只需要做一次URL编码
    signature := AwsUrlEncode(signature);

    // 把签名加到参数里
    param := param + '&Signature=' + signature;

    SetStatus('最终参数: ' + param);
  finally
    sl.Free;
  end;

  // 发送POST请求,参数放在请求体
  with HTTPS do
  begin
    ContentType := 'application/x-www-form-urlencoded; charset=UTF-8';
    Accept := 'text/xml';
    Timeout := 30000;
    PostData := param;
    Post(DOMAIN_NAME);
  end;
end;

额外注意点

  • 请确认AWS Secret Access Key没有前后空格,复制时不要带多余字符。
  • 本地时间和服务器时间差不能超过15分钟,否则会直接触发签名无效错误。

内容的提问来源于stack exchange,提问作者Steve F

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 05:54:03