Here is my code regarding the PayPal Payment Request. I am getting a 401 error but I am not sure where it is coming from or how to handle it. I believe it is from my Custom Header configuration.
How to fix it?
FUNCTION GetAccessToken :String ;
Var
AccessToken :TJSONValue ;
BEGIN
AccessToken := PayPalTokenObj.Get('access_token').JsonValue ;
Result := AccessToken.Value ;
PayPalForm.Memo1.Lines.Add('Access Token: ' + Result) ;
END ; {GetAccessToken}
FUNCTION GetTokenType :String ;
Var
AccessTokenType :TJSONValue ;
BEGIN
AccessTokenType := PayPalTokenObj.Get('token_type').JsonValue ;
Result := AccessTokenType.Value ;
PayPalForm.Memo1.Lines.Add('Token Type: ' + Result) ;
END ; {GetTokenType}
procedure SendPayment;
const
PaymentRequest = 'https://api.sandbox.paypal.com/v1/payments/payment';
var
PayPalPaymentRequestObj, PayerObj, AmountObj, TransactionObj, RedirectObj: TJSONObject;
TransactionsArray :TJSONArray ;
InputJSON: TStringStream;
Result :String ;
begin
//Create a JSON object called PayPalPaymentRequest
PayPalPaymentRequestObj := TJSONObject.Create;
try
PayPalPaymentRequestObj.AddPair(TJSONPair.Create('intent', TJSONString.Create('sale'))) ;
RedirectObj := TJSONObject.Create;
try
PayPalPaymentRequestObj.AddPair('redirect_urls', RedirectObj);
RedirectObj.AddPair(TJSONPair.Create('return_url', TJSONString.Create('http://www.SoftwareMomentum.com')));
RedirectObj.AddPair(TJSONPair.Create('cancel_url', TJSONString.Create('http://www.Yahoo.com')));
except
RedirectObj.Free;
raise;
end;
PayerObj := TJSONObject.Create;
try
PayPalPaymentRequestObj.AddPair('payer', PayerObj);
PayerObj.AddPair(TJSONPair.Create('payment_method', TJSONString.Create('paypal')));
except
PayerObj.Free;
raise;
end;
TransactionsArray := TJSONArray.Create;
AmountObj := TJSONObject.Create;
TransactionObj := TJSONObject.Create;
try
PayPalPaymentRequestObj.AddPair('transactions', TransactionsArray) ;
AmountObj.AddPair(TJSONPair.Create('total', TJSONString.Create('7.47')));
AmountObj.AddPair(TJSONPair.Create('currency', TJSONString.Create('USD')));
TransactionObj.AddPair('amount', AmountObj);
TransactionObj.AddPair(TJSONPair.Create('description', TJSONString.Create('payment description')));
// TransactionsArray.Add(AmountObj);
TransactionsArray.Add(TransactionObj);
except
TransactionsArray.Free;
AmountObj.Free;
TransactionObj.Free;
Exit;
end;
with Http do
begin
Request.Clear;
Request.CustomHeaders.Clear ;
Request.BasicAuthentication := False;
Request.CustomHeaders.Values['Authorization'] := Format(GetTokenType + ' %s', [GetAccessToken]);
Request.ContentType := 'application/json';
Request.Accept := 'application/json' ;
Request.ContentLength := 431 ;
Request.UserAgent := 'PayPalSDK/NIW/HATEOAS' ;
end; {with}
InputJSON := TStringStream.Create(PayPalPaymentRequestObj.ToString, TEncoding.UTF8);
try
HTTP.ProtocolVersion := pv1_1 ;
HTTP.AllowCookies := True ;
HTTP.HandleRedirects := True ;
//Result := HTTP.Post(PaymentRequest, PostData);
Result := HTTP.Post(PaymentRequest, InputJSON) ;
finally
InputJSON.Free;
end ; {try-finally}
finally
PayPalPaymentRequestObj.Free;
end ; {try-finally}
end;
You are ignoring the server's actual response. Your code is assuming that the response gets written to the same TStream that the request data is provided from, but that is not the case.
You are calling the version of Post() that takes a TStream as input and returns a String as output:
function Post(AURL: string; ASource: TStream): string; overload;
You are ignoring the returned String and instead using your original request data, thus the echoing that you are experiencing. The server is not echoing the request back to you, you are simply processing whatever you send as if it was returned.
You need to change this code:
HTTP.Post(PaymentRequest, Response) ;
Result := Response.DataString ;
To this instead:
Result := HTTP.Post(PaymentRequest, Response);
You should then rename the Response variable to a more suitable name. And also clean up your memory management as well, as you have some memory leaks if something goes wrong.
Try something like this:
procedure SendPayment;
const
PaymentRequest = 'https://api.sandbox.paypal.com/v1/payments/payment';
var
PayPalPaymentRequestObj, PayerObj, AmountObj, TransactionObj, RedirectObj: TJSONObject;
TransactionsArray: TJSONArray;
InputJSON: TStringStream;
Result: String;
begin
//Create a JSON object called PayPalPaymentRequest
PayPalPaymentRequestObj := TJSONObject.Create;
try
PayPalPaymentRequestObj.AddPair('intent', 'sale');
RedirectObj := TJSONObject.Create;
try
RedirectObj.AddPair('return_url', 'http://www.SoftwareMomentum.com');
RedirectObj.AddPair('cancel_url', 'http://www.Yahoo.com');
PayPalPaymentRequestObj.AddPair('redirect_urls', RedirectObj);
except
RedirectObj.Free;
raise;
end;
PayerObj := TJSONObject.Create;
try
PayerObj.AddPair('payment_method', 'paypal');
PayPalPaymentRequestObj.AddPair('payer', PayerObj);
except
PayerObj.Free;
raise;
end;
TransactionsArray := TJSONArray.Create;
try
TransactionObj := TJSONObject.Create;
try
AmountObj := TJSONObject.Create;
try
AmountObj.AddPair('total', '7.47');
AmountObj.AddPair('currency', 'USD');
TransactionObj.AddPair('amount', AmountObj);
except
AmountObj.Free;
raise;
end;
TransactionObj.AddPair('description', 'payment description');
TransactionsArray.Add(TransactionObj);
except
TransactionObj.Free;
raise;
end;
PayPalPaymentRequestObj.AddPair('transactions', TransactionsArray);
except
TransactionsArray.Free;
raise;
end;
with Http do
begin
ProtocolVersion := pv1_1;
AllowCookies := True;
HandleRedirects := True;
Request.Clear;
Request.BasicAuthentication := False;
Request.CustomHeaders.Values['Authorization'] := Format('%s %s', [GetTokenType, GetAccessToken]);
Request.ContentType := 'application/json';
Request.Accept := 'application/json';
Request.UserAgent := 'PayPalSDK/NIW/HATEOAS';
end;
InputJSON := TStringStream.Create(PayPalPaymentRequestObj.ToString, TEncoding.UTF8);
try
Result := HTTP.Post(PaymentRequest, InputJSON);
finally
InputJSON.Free;
end;
finally
PayPalPaymentRequestObj.Free;
end;
end;
Related
procedure TWebModule2.ClientGetItem(Request: TWebRequest; Response: TWebResponse);
var
o: TJSONObject;
CfKey: string;
a:TjsonArray;
begin
CfKey:='';
CfKey:= Request.QueryFields.Values['CF'] ;
if Request.QueryFields.Count>0 then begin
QryClientParam.SQL.Text := 'select * from TABLE where COD_FISC = :CF';
// qryclient.Params.ParamByName('CF').AsString := Request.QueryFields.Values['CF'];
QryClientParam.Params.ParamByName('CF').AsString := CfKey;
end else begin
QryClientParam.SQL.Text := 'select * from TABLE';
end;
QryClientParam.Active := true;
if QryClientParam.Active then begin
if QryClientParam.RecordCount>0 then begin
QryClientParam.First;
o := TJSONObject.Create;
// o.AddPair('EmployeeNumber',TJSONNumber.Create( qryClient.FieldByName('COD').AsInteger ));
o.AddPair('Descrizione', QryClientParam.FieldByName('DESK').AsString);
o.AddPair('Codice Fiscale', QryClientParam.FieldByName('COD_FISC').AsString);
o.AddPair('CAP', QryClientParam.FieldByName('CAP01').AsString);
o.AddPair('Indirizzo', QryClientParam.FieldByName('INDIRI').AsString);
o.AddPair('Citta', QryClientParam.FieldByName('CITTA01').AsString);
a.AddElement(o);
QryClientParam.Next;
Response.ContentType := 'application/json';
Response.Content := a.ToString;
// TFile.WriteAllText('JasonResult'+ '.json', a.ToString); //Scrivo Array del risultato in file
end;
end;
end;
I get this error:
Access violation at address 00E9D93D in module 'WebServerClienti.exe'. Read of address 00000008
The error is before this line:
a.AddElement(o);
The procedure works fine with my total query without parameter.
I don't understand - please help me
In a project I use TIdHTTP to call a webserver.
The webserver is an asp.net test application that returns the following json:
{
"message":"test ÀÈÉÌÒÙàèéìòù"
}
The response I get in Delphi is a kind of not encoded string:
{"message":"test ÃÃÃÃÃÃà èéìòù"}
this is how I use TIdHTTP:
Result := '';
IdHTTP := TIdHTTP.Create;
IdHTTP.Request.MethodOverride := 'ForwardCommand';
IdSSLIOHandlerSocketOpenSSL := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP);
IdSSLIOHandlerSocketOpenSSL.SSLOptions.Mode := sslmClient;
IdSSLIOHandlerSocketOpenSSL.SSLOptions.SSLVersions:= [sslvTLSv1_2];
IdHTTP.IOHandler := IdSSLIOHandlerSocketOpenSSL;
IdHTTP.HandleRedirects := True;
IdHTTP.Response.ContentEncoding := 'UTF-8'; // I tried this but it seems not enough!
try
url := 'testappUrl';
try
IdHTTP.ConnectTimeout := 2000;
IdHTTP.ReadTimeout := 4000;
Response := IdHTTP.Get(url);
ShowMessage(response);
except
on E:Exception do
begin
response := StringReplace(E.Message,#10,' ',[rfReplaceAll]);
response := StringReplace(response,#13,' ',[rfReplaceAll]);
response := '{"errormessage": "'+response+'"}';
end;
end;
Result := response;
finally
IdHTTP.Free;
end;
please tell me how I can see the response correctly.
Is there a way to force encoding so that accented chars are read correctly?
Thanks.
Try to use a TStringStream forcing the encoding (UTF-8).
Test this code to get the response:
var
ts:TStringStream;
begin
...
ts := TStringStream.Create(string.Empty, TEncoding.UTF8);
IdHTTP1.Get('url', ts);
ShowMessage(ts.DataString);
or
ShowMessage(ts.ToString);
...
I am using the trial version of DevArt's SecureBridge product. I am trying to process POST, but somehow I could not print the request data.
XML:
<test>
<a>test1</a>
<b>test2</b>
</test>
Delphi:
ScHttpWebRequest1.Method := rmPOST;
ScHttpWebRequest1.ContentType := 'text/xml';
ScHttpWebRequest1.RequestUri := 'https://test.com/api';
ScHttpWebRequest1.KeepAlive := True;
ScHttpWebRequest1.ContentLength := Length(XML);
ScHttpWebRequest1.WriteBuffer(pAnsiChar(XML), 0, Length(XML)); ///I think I'm making a mistake here.
ShowMessage(ScHttpWebRequest1.GetResponse.ReadAsString);
I have reviewed the documents, but there is a feature called RequestStream. This feature was not available in the version I downloaded. I think WriteBuffer is used instead or different. all I want to do is request a POST with XML content on the relevant site. How can I do it?
Thanks.
Here's a chunk of code that has worked for me:
var
Response: TScHttpWebResponse;
ResponseStr: string;
buf: TBytes;
begin
ScHttpWebRequest1.Method := rmPOST;
ScHttpWebRequest1.ContentType := 'text/xml';
ScHttpWebRequest1.RequestUri := 'https://test.com/api';
ScHttpWebRequest1.KeepAlive := True;
buf := TEncoding.UTF8.GetBytes(xml);
ScHttpWebRequest1.ContentLength := Length(buf);
ScHttpWebRequest1.WriteBuffer(buf);
Response:=ScHttpWebRequest1.GetResponse;
ResponseStr:=Response.ReadAsString;
end;
Based on Devart forums information you can post/put stream or strings parameters as below:
var
Request: TScHttpWebRequest;
Response: TScHttpWebResponse;
ResponseStr: string;
Stream: TFileStream;
begin
Request := TScHttpWebRequest.Create(URL);
Stream := TFileStream.Create(FileName, fmOpenRead);
try
Request.Method := rmPut;
Request.ContentType := 'application/pdf';
Request.TransferEncoding := 'binary';
Request.Headers.Add('Content-Disposition', 'form-data; name="FormFile"; filename="Document1.pdf"');
Request.ContentLength := Stream.Size;
Request.SendChunked := True;
Request.RequestStream := Stream;
Response := Request.GetResponse;
ResponseStr := Response.ReadAsString;
Response.Free;
finally
Stream.Free;
Request.Free;
end;
end;
I'm making requests to the webaddress to get XML files throught the HTTPS connection. But this connection works like 50%. In most cases it fails. Usual error is "socket error #10060". Or "Error connecting with SSL. EOF was observed that violates the protocol". What I'm doing wrong?
function SendRequest(parameters: string): IXMLDocument;
var
sPostData: TStringList;
sHttpSocket: TIdHTTP;
sshSocketHandler: TIdSSLIOHandlerSocketOpenSSL;
resStream: TStringStream;
xDoc: IXMLDocument;
begin
sPostData := TStringList.Create;
try
sPostData.Add('add some parameter to post' + '&');
sPostData.Add('add some parameter to post' + '&');
sPostData.Add('add some parameter to post' + '&');
sPostData.Add(parameters);
sHttpSocket := TIdHTTP.Create;
sshSocketHandler := TIdSSLIOHandlerSocketOpenSSL.Create;
sHttpSocket.IOHandler := sshSocketHandler;
sHttpSocket.Request.ContentType := 'application/x-www-form-urlencoded';
sHttpSocket.Request.Method := 'POST';
resStream := TStringStream.Create;
sHttpSocket.Post(Self.sUrl, sPostData, resStream);
xDoc := CreateXMLDoc;
xDoc.LoadFromStream(resStream);
Result := xDoc;
resStream.Free;
sHttpSocket.Free;
sshSocketHandler.Free;
sPostData.Free;
except on E: Exception do
begin
TCommon.ErrorLog('errorLog.txt', DateTimeToStr(Now) + ' ' + E.Message);
end
end;
end;
Maybe I can do this in another way, that works like 100%, when internet connection is available?
Regards,
evilone
An "EOF" error suggests you are connnecting to a server that is not actually using SSL to begin with, or the SSL data may be corrupted.
Besides that, why are you including explicit '&' characters between your post data parameters? Don't do that, Indy will just encode them and send its own '&' characters. Also, consider using TMemoryStream instead of TStringStream to ensure IXMLDocumect.LoadFromStream() is loading the server's original raw XML data as-is, and not an altered version that the RTL/VCL produces due to Unicode handling (TStringStream is TEncoding-enabled).
Edit: Given the URL you provided, an example of calling verifyUser() would look like this:
const
ERPLYAccountCode = '...';
function verifyUser(const user, pass: string; const sessionLength: Integer = 3600): IXMLDocument;
var
sPostData: TStringList;
sHttpSocket: TIdHTTP;
sshSocketHandler: TIdSSLIOHandlerSocketOpenSSL;
resStream: TMemoryStream;
xDoc: IXMLDocument;
begin
Result := nil;
try
resStream := TMemoryStream.Create;
try
sPostData := TStringList.Create;
try
sPostData.Add('clientCode=' + ERPLYAccountCode);
sPostData.Add('request=verifyUser');
sPostData.Add('version=1.0');
sPostData.Add('responseType=XML');
sPostData.Add('responseMode=normal');
sPostData.Add('username=' + user);
sPostData.Add('password=' + pass);
sPostData.Add('sessionLength=' + IntToStr(sessionLength));
sHttpSocket := TIdHTTP.Create;
try
sshSocketHandler := TIdSSLIOHandlerSocketOpenSSL.Create(sHttpSocket);
sHttpSocket.IOHandler := sshSocketHandler;
sHttpSocket.Request.ContentType := 'application/x-www-form-urlencoded';
sHttpSocket.Post('https://www.erply.net/api/', sPostData, resStream);
finally
sHttpSocket.Free;
end;
finally
sPostData.Free;
end;
resStream.Position := 0;
xDoc := CreateXMLDoc;
xDoc.LoadFromStream(resStream);
Result := xDoc;
finally
resStream.Free;
end;
except
on E: Exception do
begin
TCommon.ErrorLog('errorLog.txt', DateTimeToStr(Now) + ' ' + E.Message);
end;
end;
end;
Using Delphi 2010, UniDAC components, Firebird 2.5 SuperServer.
Database character set is ISO_8559_1 (my Windows default).
I am writing a data transfer application to transfer data from an Access database to a Firebird database that has identical table structure. I am using a ADOQuery component to select all rows from source table, and then looping through that recordset, and using UniSQL component with an INSERT statement with parameters, assigning parameter values from the corresponding source dataset field values.
When running the insert command, it throws a 'Malformed string' exception.
I am stuck and need help to resolve the issue.
Code follows:
function TDataTransfer.BeginTransfer(AProgressCallback: TProgressCallback): Boolean;
var
slSQLSelect, slSQLInsert: TStringList;
i, f, z: Integer;
cmdS, cmdI: String;
adods: TADODataSet;
fbcmd: TUniSQL;
fbscript: TUniscript;
q: String;
s : WideString;
begin
FProgressCallback := AProgressCallback;
fbscript := TUniscript.Create(nil);
try
fbscript.Connection := FirebirdConnection;
FirebirdConnection.StartTransaction;
try
fbscript.Delimiter := ';';
fbscript.ExecuteFile(ExtractFilePath(ParamStr(0)) + 'Firebird_Script_0.txt');
FirebirdConnection.CommitRetaining;
slSQLSelect := TStringList.Create;
slSQLInsert := TStringList.Create;
adods := TADODataSet.Create(nil);
fbcmd := TUniSQL.Create(nil);
try
adods.Connection := AccessConnection;
fbcmd.Connection := FirebirdConnection;
slSQLSelect.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Access_Select.txt');
slSQLInsert.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Firebird_Insert.txt');
z := slSQLSelect.Count - 1;
for i := 0 to z do begin
cmdS := slSQLSelect[i];
cmdI := slSQLInsert[i];
adods.CommandText := cmdS;
fbcmd.SQL.Text := cmdI;
adods.Open;
while not adods.Eof do begin
for f := 0 to adods.FieldCount - 1 do
try
if adods.FieldDefs[f].DataType = ftWideString then begin
s := adods.Fields[f].AsAnsiString ;
q := '"';
// if AnsiStrPos(PAnsiChar(#s), PAnsiChar(q)) <> nil then
// s := StringReplace(s, '"', '""', [rfReplaceAll]);
fbcmd.Params[f].Value := s;
end
else
if adods.FieldDefs[f].DataType = ftWideMemo then
fbcmd.Params[f].SetBlobData(adods.CreateBlobStream(adods.Fields[f], bmRead))
else
fbcmd.Params[f].Value := adods.Fields[f].Value;
except
raise;
end;
try
fbcmd.Execute;
// FirebirdConnection.CommitRetaining;
except
raise;
end;
adods.Next;
end;
adods.Close;
FProgressCallback((i + 1) * 100 div (z + 1), 10);
end;
finally
slSQLSelect.Free;
slSQLInsert.Free;
adods.Free;
fbcmd.Free;
end;
fbscript.ExecuteFile(ExtractFilePath(ParamStr(0)) + 'Firebird_Script_1.txt');
FirebirdConnection.Commit;
Result := True;
except
FirebirdConnection.Rollback;
Result := False;
end;
finally
fbscript.Free;
end;
end;
TIA,
SteveL
If you try to replace s := StringReplace(s, '"', '""', [rfReplaceAll]); with s := StringReplace(s, '''''', '''', [rfReplaceAll]); and uncomment the line;