I am trying to address the OriginStamp.org site to post a file hash and to get its timestamp
curl -X GET -H "Content-Type: application/json" -H "Authorization: xxxxxxxxxxxxxxxxxxx" http://api.originstamp.org/api/yyyyyyyyyyy
I used various formats to access this API but this seemed to be most hopeful but with out success
PROCEDURE TInterNetFm.GetTIMESTAMP;
var
i : integer;
response : string;
JSONToSend : TStringStream;
IndyH : TIdHTTP;
begin
IndyH := TIdHTTP.Create(Application);
JSONToSend := TStringStream.Create('{}');
IndyH.Request.Connection := 'Keep-Alive';
IndyH.Request.CustomHeaders.Clear;
IndyH.Request.CustomHeaders.Values['Content-Type'] := 'application/json';
IndyH.Request.CustomHeaders.Values['Authorization']:='xxxxxxxxxxxxxxx';
IndyH.Request.ContentType := 'application/json';
response:=IdHttp1.post('http://originstamp.org/api/
05c2422f44ddd24ba3f25848773a8fcb48435f8f966381da4732c40a7255780c',
JSONToSend);
JSONToSend.free;
IndyH.free;
end;
This gives a HTTP/1.1 error 403 forbidden.
I have also tried IdHttp1.getand the Delphi REST debugger and various attampts at REST.
Any suggestions about how I am going wrong with this would be grately appreciated.
A 403 error implies that you are likely using an invalid API key in the Authorization header. Also, make sure that Request.BasicAuthentication is set to False so TIdHTTP doesn't try to send its own Authorization header.
That being said, I see several other issues with your code.
You are leaking memory if TIdHTTP.Post() raises an exception on failure.
You are sending 2 Content-Type headers (one from Request.ContentType and the other from CustomerHeaders.Values['Content-Type']). Use Request.ContentType only.
You are sending your request to originstamp.org when you should be sending it to api.originstamp.org instead.
You are creating and configuring a TIdHTTP object named IndyH, but you are performing the actual Post() using another object named IdHttp1 instead.
Try this:
function TInterNetFm.GetTIMESTAMP(const AHash: string): string;
var
IndyH : TIdHTTP;
Response : string;
begin
IndyH := TIdHTTP.Create(nil);
try
IndyH.Request.BasicAuthentication := False;
IndyH.Request.CustomHeaders.Values['Authorization'] := 'YOUR_API_KEY';
Response := IndyH.Get('http://api.originstamp.org/api/' + AHash);
finally
IndyH.Free;
end;
// parse Response JSON as needed...
Result := ...;
end;
function TInterNetFm.GetTIMESTAMP(const AHash, AData: string): string;
var
IndyH : TIdHTTP;
JSONToSend : TStringStream;
Response : string;
begin
IndyH := TIdHTTP.Create(nil);
try
IndyH.Request.BasicAuthentication := False;
IndyH.Request.ContentType := 'application/json';
IndyH.Request.CustomHeaders.Values['Authorization'] := 'YOUR_API_KEY';
JSONToSend := TStringStream.Create(AData, TEncoding.UTF8);
try
Response := IndyH.Post('http://api.originstamp.org/api/' + AHash, JSONToSend);
finally
JSONToSend.Free;
end;
finally
IndyH.Free;
end;
// parse Response JSON as needed...
Result := ...;
end;
Related
{"code":3702,"msg":"Invalid signature.","timestamp":1623848681308}
i use trbinance.com api. I keep getting the same error
procedure TForm1.Button1Click(Sender: TObject);
var
url, sign, queryString, nonce: string;
ST: SystemTime;
DT: TDateTime;
uTime: int64;
sHour, sMin, sSec, sMili: Word;
stream, s_url: string;
Post: TStringList;
IdHTTP10 : TIDhttp;
api_key , api_secret : String;
begin
api_key := '**';
api_secret := '**';
GetSystemTime(ST);
DT := EncodeDate(ST.wYear, ST.wMonth, ST.wDay) +
EncodeTime(ST.wHour, ST.wMinute, ST.wSecond, ST.wMilliseconds);
uTime := DateUtils.MilliSecondsBetween(DT, UnixDateDelta);
nonce:=inttostr(uTime);
url :='https://trbinance.com/open/v1/orders';
queryString := 'symbol=BTT_TRY&side=0&type=1&quantity=1&price=0.0022&recvWindow=5000×tamp='+nonce;
sign := THashSHA2.GetHMAC(queryString, api_secret, SHA256);
IdHTTP10 := TidHTTP.Create(nil);
IdHTTP10.HandleRedirects := True;
IdHTTP10.Request.CustomHeaders.Add('X-MBX-APIKEY:'+api_key);
IdHTTP10.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
Post:=TStringList.Create;
Post.Add('quantity=1&');
Post.Add('price=0.0022&');
Post.Add('recvWindow=5000&');
Post.Add('timestamp='+nonce+'&');
Post.Add('signature='+sign);
s_url := url + '?symbol=BTT_TRY&side=0&type=1';
try
stream:=IdHTTP10.POST(s_url, Post);
Memo1.Lines.Add(stream);
except
on E: Exception do
Memo1.Lines.Add(TimeToStr(time)+' <---> [Order] error: '+E.Message);
end;
IdHTTP10.Free;
Post.Free;
end;
Always problem :
{"code":3702,"msg":"Invalid signature.","timestamp":1623848681308}
Normaly Binance Symbol : BTTTRY
Turkey Api : BTT_TRY
Normaly Binance Order Url : httpps://api.binance.com/api/v3/order
Turkey Api Order Url : https://trbinance.com/open/v1/orders
Where is the Problem ?
Thank you so much.
Thank you for your answers. I made improvements to the code.
Friends in need can use this procedure in delphi. ( for Global Binance Api )
procedure TForm1.Button1Click(Sender: TObject);
const
Publickey= '***';
Secretkey= '***';
var
IdHTTP : TIdHTTP;
RBody : TStringstream;
QueryString : String;
Signature : String;
url : String;
begin
queryString :=
'symbol=BTTTRY&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1000&price=0.024400&recvWindow=5000×tamp='
+ ABDULLAH_GetTimestamp();
Signature := THashSHA2.GetHMAC(queryString, Secretkey, SHA256);
Try
IdHTTP := TIdHTTP.Create();
RBody := TStringStream.Create(queryString + '&signature=' + Signature);
IdHTTP.Request.CustomHeaders.Add('X-MBX-APIKEY:' + Publickey);
IdHTTP.Request.CustomHeaders.UnfoldLines := True;
IdHTTP.Request.Accept := 'application/json';
IdHTTP.Request.ContentType := 'application/json';
IdHTTP.Request.ContentEncoding := 'utf-8';
IdHTTP.Request.BasicAuthentication := True;
IdHTTP.HandleRedirects := True;
IdHTTP.HTTPOptions := [hoKeepOrigProtocol, hoForceEncodeParams,
hoNoProtocolErrorException, hoWantProtocolErrorContent];
IdHTTP.iohandler := IdSSLIOHandlerSocketOpenSSL1;
url := 'https://api.binance.com/api/v3/order?';
Try
Memo1.Lines.Add(IdHTTP.Post(url, RBody));
Except
ON E: EXCEPTION DO
Memo1.Lines.Add('Error Message:' + E.Message);
End;
Finally
FreeAndNil(IdHTTP);
FreeAndNil(RBody);
End;
end;
This code works for Global Binance Api.
Change Symbol : BTTTRY -> BTT_TRY
And Post Url : https://api.binance.com/api/v3/order -> https://trbinance.com/open/v1/orders
İt's not working and Result :
{"code":3702,"msg":"Invalid signature.","timestamp":1723648673412}
This Procedure works in 'Global Binance Api' but not working 'trbinance Api'
Same procedure gives 'invalid signature' error in 'trbinance api'. :(
trbinance api example :
HMAC SHA256 signature:
[linux]$ echo -n "symbol=BTC_USDT&side=0&type=1&quantity=0.16&price=7500×tamp=1581720670624&recvWindow=5000" | openssl dgst -sha256 -hmac "cfDC92B191b9B3Ca3D842Ae0e01108CBKI6BqEW6xr4NrPus3hoZ9Ze9YrmWwPFV"
(stdin)= 33824b5160daefc34257ab9cd3c3db7a0158a446674f896c9fc3b122ae656bfa
curl command:
(HMAC SHA256)
[linux]$ curl -H "X-MBX-APIKEY: cfDC92B191b9B3Ca3D842Ae0e01108CBKI6BqEW6xr4NrPus3hoZ9Ze9YrmWwPFV" -X POST 'https://www.trbinance.com/open/v1/orders' -d 'symbol=BTC_USDT&side=0&type=1&quantity=0.16&price=7500×tamp=1581720670624&recvWindow=5000&signature=33824b5160daefc34257ab9cd3c3db7a0158a446674f896c9fc3b122ae656bfa'
There are a number of issues with this code, but the biggest one I see is that you should NOT be including trailing & characters in your TStringList strings, eg:
Post:=TStringList.Create;
Post.Add('quantity=1');
Post.Add('price=0.0022');
Post.Add('recvWindow=5000');
Post.Add('timestamp='+nonce);
Post.Add('signature='+sign);
TIdHTTP.Post() will insert those & characters into the request body for you, thus the data being transmitted is different than the data you signed.
According to Binance TR API docs:
Use your secretKey as the key and totalParams as the value for the HMAC operation.
totalParams is defined as the query string concatenated with the request body.
Error from API with the text "Invalid signature." could give you a clue that the problem is with your signature algo:
sign := THashSHA2.GetHMAC(queryString, api_secret, SHA256);
Your code here signs only your query string, but according to API docs you must sign query string concatenated with the request body.
So, you need to change your code like this ("Post" variable must be initialized before this line):
sign := THashSHA2.GetHMAC(queryString + Post.text, api_secret, SHA256);
I found that using the TIdHTTP component in Delphi to connect to Binance has problems. I kept getting Http/1.1 400 Bad Request.
I switched to TNetHTTPClient instead to connect with Binance.
The following is the working program that can be used for placing an order on Binance:
uses
System.Net.HttpClient, System.Hash, DateUtils;
procedure TForm1.fapiorder(oSide: String; oCoin: String; oAmt: Integer);
var
sOrderAmt: string;
ClearEntryUrl: string;
Params: string;
Signature: string;
URL: string;
ResponseStream : TStringStream;
postStream: TStringStream;
uTime: int64;
nowDateTime: string;
ST: SystemTime;
DT: TDateTime;
HttpClient: THttpClient;
HttpResponse: IHttpResponse;
BaseURLUSDM, ApiKey, ApiSecret: string;
begin
//https://binance-docs.github.io/apidocs/futures/cn/#trade-3
BaseURLUSDM := 'https://fapi.binance.com/'; //合約永續
ClearEntryUrl := 'fapi/v1/order';
ApiSecret := 'YOUR API SECRET KEY';
//timestamp 開始
GetSystemTime(ST);
DT := EncodeDate(ST.wYear, ST.wMonth, ST.wDay) + EncodeTime(ST.wHour,
ST.wMinute, ST.wSecond, ST.wMilliseconds);
uTime := DateUtils.MilliSecondsBetween(DT, UnixDateDelta);
nowDateTime := IntToStr(uTime);
Writeln('DateTime = ' + nowDateTime);
//timestamp 結束
Params := 'recvWindow=3000×tamp=' + nowDateTime;
if (TDCoinMap.TryGetValue(oCoin, CoinMap) = True) then
begin
sOrderAmt := '0.07';
if (StrToFloat(sOrderAmt) <> 0) then
begin
Params := Params + '&symbol=' + oCoin;
Params := Params + '&side=' + oSide;
Params := Params + '&type=MARKET';
Params := Params + '&quantity=' + sOrderAmt;
Signature := THashSHA2.GetHMAC(Params, ApiSecret, SHA256);
URL := BaseURLUSDM + ClearEntryUrl + '?' + Params + '&signature=' + Signature;
postStream := TStringStream.Create('');
ResponseStream := TStringStream.Create('');
try
HttpClient := THTTPClient.Create;
HttpClient.HandleRedirects := True;
HttpClient.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0';
HttpClient.ContentType := 'application/x-www-form-urlencoded';
HttpClient.CustomHeaders['X-MBX-APIKEY'] := 'Your Binance APIKEY';
HttpResponse := HttpClient.Post(URL, ResponseStream);
jResult := HttpResponse.ContentAsString();
Writeln('jResult = ' + jResult);
finally
HttpClient.Free;
end;
end;
end;
end;
My goal is to be able to convert TMemoryStream to string. I have this code to get the data into the TMemoryStream:
var
idHttp : TIdHTTPEx;
url : string;
slTemp : TStringList;
memoryStream : TMemoryStream;
begin
try
idHttp := TIdHTTPEx.Create(nil);
slTemp := TStringList.Create;
memoryStream := TMemoryStream.Create;
try
url := GetURL;
SetParams(slTemp);
idHttp.Request.Accept := 'application/json, text/javascript, */*; q=0.01';
idHttp.Request.AcceptEncoding := 'gzip, deflate, br';
idHttp.Request.AcceptLanguage := 'en-US,en;q=0.9';
idHttp.Request.CacheControl := 'no-cache';
idHttp.Request.Connection := 'keep-alive';
idHttp.Request.ContentLength := 16;
idHttp.Request.ContentType := 'application/x-www-form-urlencoded; charset=UTF-8';
idHttp.Post(url, slTemp, memoryStream);
Result := MemoryStreamToString(memoryStream);
finally
memoryStream.Free;
slTemp.Free;
idHttp.Free;
end;
except on E : Exception do
begin
Result := 'e:' + E.Message;
end;
end;
end;
And this is my code to convert it to a string:
function MemoryStreamToString(MemoryStream : TMemoryStream): string;
var
StringStream: TStringStream;
begin
Result:='';
StringStream:= TStringStream.Create('', TEncoding.UTF8);
try
MemoryStream.Position := 0;
StringStream.CopyFrom(MemoryStream, MemoryStream.Size);
Result:= StringStream.DataString;
Result := Result;
finally
FreeAndNil(StringStream);
end;
end;
My function works fine in most of the conversion but not this. I checked these links: link1, link2 but they are different than my situation. I tried link3 too but still fail.
Any idea how to solve the problem?
You don't need to decode the raw data manually. Just let TIdHTTP do it for you. The Post() method has an overload that returns a decoded string:
Result := idHttp.Post(url, slTemp);
Also, you need to get rid of this line completely:
idHttp.Request.AcceptEncoding := 'gzip, deflate, br';
Otherwise TIdHTTP will not be able to decode the response correctly if the server decides to send a compressed response. You are manually giving the server permission to do so, but you are not setting up the TIdHTTP.Compressor property so TIdHTTP can handle decompression. Do not set the AcceptEncoding manually unless you are willing and able to manually detect and decode a response that has been actually been encoded in one of the formats you specify. Otherwise, just let TIdHTTP manage the AcceptEncoding property internally based on its actual capabilites.
I can use the following code to easily get the HTML source from the URL, but how can I get the actual URL itself? Because sometimes the initial URL goes through some redirects and the actual URL is not the same and I would like to capture it for usage. I cannot seem to find any good documentation on the usage of methods or properties for winHTTP in Delphi. Thanks!
var http: variant;
begin
http:=createoleobject('WinHttp.WinHttpRequest.5.1');
http.open('GET', 'http://URLtoWebsite.com', false);
http.send;
showmessage(http.responsetext);
end;
You can use something like this
function GetFinalURL(const AMainURL: string): string;
var
http: Variant;
begin
Result := '';
http := CreateOleObject('WinHttp.WinHttpRequest.5.1');
http.Option(6) := False;
http.open('GET', AMainURL, false);
http.send;
if http.Status = 302 then
Result := http.getResponseHeader('Location')
else
Result := AMainURL;
end;
Another way using Indy
function GetFinalURL(const AMainURL: string): string;
var
idHTTP: TIdHTTP;
begin
Result := '';
idHTTP := TIdHTTP.Create(nil);
try
idHTTP.HandleRedirects := True;
try
idHTTP.Get(AMainURL);
Result := idHTTP.Request.URL;
except
end;
finally
idHTTP.Free;
end;
end;
You can set WinHttpSetStatusCallback with WINHTTP_CALLBACK_FLAG_REDIRECT parameter to receive notifications about every redirect occurred during request.
I implemented same code (to post a form) using delphi and python. The python code works perfectly, but delphi code fails. In python, I can simply write httplib2.debuglevel=4 to see what content has actually been sent to the server. but I have no idea how to print the content in delphi.
def python_request_data(url, cookie, data):
httplib2.debuglevel = 4
conn = httplib2.Http()
conn.follow_all_redirects = True
headers = {'Cookie': cookie, 'Content-Type': 'application/x-www-form-urlencoded'}
response, contents = conn.request(url, 'POST', data, headers=headers)
procedure DelphiRequestData(const Url, Cookie, Data: string);
var
Client: TIdHttp;
Params: TStringList;
Response: string;
begin
Client := TIdHttp.Create(nil);
try
Client.HTTPOptions := [hoKeepOrigProtocol];
Client.Request.CustomHeaders.AddValue('Cookie', Cookie);
Params := TStringList.Create;
try
Params.QuoteChar := #0;
Params.Delimiter := '&';
Params.DelimiterText := Data;
Client.Request.ContentType := 'application/x-www-form-urlencoded';
Client.Request.ContentLength := Length(Params.DelimitedText);
Response := Client.Post(Url, Params);
finally
Params.Free;
end;
finally
Client.Free;
end;
end;
Any hints are appreciated.
You ca use TIdLogDebug as Intercept of your IdHttp.
The Events OnSend and OnReceive will deliver the desired Informations in a Array or TBytes.
Can anyone tell me why i'm having trouble accessing my calendar information? I'm getting 403 forbidden.
procedure TForm1.Button1Click(Sender: TObject);
var
stringStream: TStringStream;
slPost, slReply: TStringList;
sPostResult: string;
begin
slPost := TStringList.Create;
slReply := TStringList.Create;
try
slPost.LineBreak := '&';
slPost.Values['Email'] := 'me#gmail.com';
slPost.Values['Passwd'] := 'pass';
slPost.Values['service'] := 'cl';
slPost.Values['source'] := 'company-program-version';
stringStream := TStringStream.Create(slPost.Text);
try
IdHTTP1.Request.ContentType := 'application/x-www-form-urlencoded';
sPostResult := IdHTTP1.Post('https://www.google.com/accounts/ClientLogin', stringStream);
slReply.LineBreak:=#10;
slReply.Text:=sPostResult;
slReply.LineBreak:=#13#10;
Memo1.Lines.Add(slReply.Text);
Memo1.Lines.Add('response=' + IdHTTP1.ResponseText);
// 200 OK
sPostResult := IdHTTP1.Post('https://www.google.com/accounts/ClientLogin', stringStream);
IdHTTP1.Request.CustomHeaders.FoldLines:=false;
IdHTTP1.Request.CustomHeaders.Clear;
IdHTTP1.Request.CustomHeaders.Values['GData-Version']:='2.0';
IdHTTP1.Request.CustomHeaders.Values['Authorization']:='GoogleLogin auth=' + slReply.Values['auth'];
(* custom headers:
GData-Version: 2.0
Authorization: GoogleLogin (line continues) auth=DQwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhatever *)
IdHTTP1.Request.ContentType := 'application/atom+xml';
// 403 Forbidden
memo1.Lines.Add(IdHTTP1.Get('https://www.googleapis.com/calendar/v3/users/me/calendarList'));
finally
stringStream.Free;
end;
finally
slPost.Free;
slReply.Free;
end;
end;
thank you!
mp
After some reading, I think you need to deal with Redirect. So If response is redirect, get the new url, reattach the authorization to the new request header with the new url. Otherwise your redirection request will be missing the required authorization and give you 403 error.