Steve F
Steve F

Reputation: 1567

Delphi - Calculating Amazon MWS signature

Using: Delphi 10.2.3 Tokyo IPWorks SSL and IPWorks Encrypt components

I am trying to use the Amazon MWS API from Delphi to get a list of orders, but am unsuccessful in making the request to Amazon MWS. Its mandatory that I use IPWorks components, as required by my client. The response from the API says that the signature is not correct:

Sender SignatureDoesNotMatch The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.

Obviously the issue has something to do with calculating the signature, encoding it to Base64, or something wrong with the request itself. I think its the encoding to Base64 that is not working for me. Maybe something to do with Delphi Unicode and UTF-8 encoding.

I've spent a whole day trying to figure this out and am stuck, so am posting it here. I hope someone can help. Below is my code:

On the form I have 2 components: TipwHTTP and TipcHash

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

object HashMaker: TipcHash
  Algorithm = haHMACSHA256
  EncodeHash = True
end


function GetISO8601DateTime_URLEncodedStr(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'; // remove the milliseconds part

  Result := TNetEncoding.URL.Encode(d);
end;

const
  DOMAIN_NAME = 'https://mws.amazonservices.com/Orders/2013-09-01';
var
  sl: TStringList;
  param, signature, aurl: String;
begin
  sl := TStringList.Create;
  try
    sl.Add('AWSAccessKeyId=' + edtAwsAccessKey.Text); { Index: 0 }
    sl.Add('&Action=ListOrders'); { Index: 1 }
    sl.Add('&CreatedAfter=' + GetISO8601DateTime_URLEncodedStr(dtpOrdersCreatedFrom.Date)); { Index: 2 }
    sl.Add('&MWSAuthToken=' + edtMarketplaceID.Text); { Index: 3 }
    sl.Add('&MarketplaceId.Id.1=' + edtMarketplaceID.Text); { Index: 4 }
    sl.Add('&SellerId=' + edtSellerID.Text); { Index: 5 }
    sl.Add('&SignatureMethod=HmacSHA256'); { Index: 6 }   // <---- Insert Signature here
    sl.Add('&SignatureVersion=2'); { Index: 7 }
    sl.Add('&Timestamp=' + GetISO8601DateTime_URLEncodedStr(Now)); { Index: 8 }
    sl.Add('&Version=2013-09-01'); { Index: 9 }

    param := StringReplace(sl.Text, #13#10, '', [rfReplaceAll]);

    HashMaker.Key := edtSecretKey.Text;
    HashMaker.Algorithm := haHMACSHA256;
    HashMaker.InputMessage := 'POST\n' + 'mws.amazonservices.com\n' + '/Orders/2013-09-01\n' + param;
    HashMaker.ComputeHash;
    signature := HashMaker.HashValue;

    SetStatus('Signature1: ' + signature);

    signature := TNetEncoding.Base64.Encode(TEncoding.UTF8.GetString(BytesOf(signature)));

    signature := TNetEncoding.URL.Encode(signature);

    SetStatus('Signature2: ' + signature);

    sl.Insert(6, '&Signature=' + signature);
    param := StringReplace(sl.Text, #13#10, '', [rfReplaceAll]);

    SetStatus('param: ' + param);

  finally
    sl.Free;
  end;

  with HTTPS do
  begin
    ContentType := 'application/x-www-form-urlencoded; charset=UTF-8';
    Accept := 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8';
    Timeout := 0;

    aurl := DOMAIN_NAME + '?' + param;
    Post(aurl);
  end;

end;

I would really appreciate if someone can help me solve this.

-Steve

Upvotes: 0

Views: 231

Answers (0)

Related Questions