Using PayPal REST client with Delphi XE2 - delphi

I am trying to interface with the PayPal REST client, following the instructions here:
https://developer.paypal.com/docs/integration/direct/make-your-first-call/
I can successfully obtain the access token using a TIdHttp component with this code:
http.Request.ContentType := 'application/x-www-form-urlencoded';
http.Request.Accept := 'application/json';
http.Request.AcceptLanguage := 'en_US';
http.Request.BasicAuthentication := True;
http.Request.Username := 'my paypal clientid';
http.Request.Password := 'my paypal secret';
slParameters := TStringList.Create;
Response := TStringStream.Create;
try
//get an access token
slParameters.Add('grant_type=client_credentials');
http.Post('https://api.sandbox.paypal.com/v1/oauth2/token', slParameters, Response);
json := Response.DataString;
PayPalObj := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(json), 0) as TJSONObject;
try
jTokenValue := PayPalObj.Get('access_token').JsonValue;
AccessToken := jTokenValue.Value;
jTokenValue := PayPalObj.Get('token_type').JsonValue;
TokenType := jTokenValue.Value;
finally
PayPalObj.Free;
end;
if TokenType <> 'Bearer' then
Exit;
if AccessToken = '' then
Exit;
....
finally
Response.Free;
slParameters.Free;
end;
Once I have the token I should be able to create a payment. On the PayPal website an example using cURL is given here:
https://developer.paypal.com/docs/integration/web/accept-paypal-payment/
This is what I have tried:
//create a payment
PayPalObj := TJSONObject.Create;
try
PayPalObj.AddPair(TJSONPair.Create('intent', TJSONString.Create('sale')));
RedirectObj := TJSONObject.Create;
try
RedirectObj.AddPair(TJSONPair.Create('return_url', TJSONString.Create('http://blahblah.com/return')));
RedirectObj.AddPair(TJSONPair.Create('cancel_url', TJSONString.Create('http://blahblah.com/cancel')));
except
RedirectObj.Free;
Exit;
end;
PayerObj := TJSONObject.Create;
try
PayerObj.AddPair(TJSONPair.Create('payment_method', TJSONString.Create('paypal')));
except
PayerObj.Free;
Exit;
end;
TransactionsArray := TJSONArray.Create;
AmountObj := TJSONObject.Create;
TransactionObj := TJSONObject.Create;
try
AmountObj.AddPair('total', TJSONString.Create('7.47'));
AmountObj.AddPair('currency', TJSONString.Create('USD'));
TransactionObj.AddPair('amount', AmountObj);
TransactionObj.AddPair('description', TJSONString.Create('payment description'));
TransactionsArray.Add(TransactionObj);
except
TransactionsArray.Free;
AmountObj.Free;
TransactionObj.Free;
Exit;
end;
PayPalObj.AddPair(TJSONPair.Create('redirect_urls', RedirectObj));
PayPalObj.AddPair(TJSONPair.Create('payer', PayerObj));
PayPalObj.AddPair(TJSONPair.Create('transactions', TransactionsArray));
slParameters.Clear;
Response.Clear;
http.Request.ContentType := 'application/json';
http.Request.CustomHeaders.Clear;
//http.Request.CustomHeaders.FoldLines := False; have tried this with no success
http.Request.CustomHeaders.AddValue('Authorization', Format('Bearer %s', [AccessToken])); //token obtained from first request
slParameters.Add(PayPalObj.ToString);
http.Post('https://api.sandbox.paypal.com/v1/payments/payment', slParameters, Response);
json := Response.DataString;
...
finally
PayPalObj.Free;
end;
I'm not getting any response. I am sure I have constructed the JSON string correctly as I have carefully compared it with the sample one. I have also tested the sample one using cURL and it does work. I'm not sure if it's right to add the JSON string into a string list as I have done. I'm also not sure if I need to include the "-d" cURL parameter somewhere. Any advice would be gratefully received.

In the second step, you cannot use a TStringList to post the JSON data. That only works for application/x-www-form-urlencoded posts. To post JSON, you need to use a TStream instead.
Also, you don't need to use a TStringStream to get a response as a String. Post() can return a String directly.
Try this:
json := http.Post('https://api.sandbox.paypal.com/v1/oauth2/token', slParameters);
...
ssJson := TStringStream.Create(PayPalObj.ToString, TEncoding.ASCII);
try
json := http.Post('https://api.sandbox.paypal.com/v1/payments/payment', ssJson);
finally
ssJson.Free;
end;

Related

How to encode the response of an TIdHTTP call?

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);
...

Delphi Devart SecureBridge POST Request

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;

How can I send a Push Notification through Parse.Com using Delphi?

I need to send a Push notification out through Parse.com's API using Delphi.
I see there is a TParseApi but the documentation is, as usual, rather sparse on the subject.
How can I do this?
Drop a TParseProvider and a TBackendPush component onto a form or datamodule. Connect them and enter your credentials in the appropriate properties of the provider. Set the backend Message property to the message to send and call Push.
There are at least three ways of doing this:
1) A direct method would be to create your own HTTP request with custom headers and JSON
Procedure TForm1.ParseDotComPushNotification(pushMessage: string);
var
parseDotComUrl: string;
JSON: TStringStream;
webRequest: TIDHttp;
response: string;
whereJson: TJSONObject;
alertJson: TJSONObject;
mainJsonObject: TJSONObject;
begin
parseDotComUrl := 'https://api.parse.com/1/push';
// Modify the JSON as required to push to whomever you want to.
// This one is set up to push to EVERYONE.
// JSON := TStringStream.Create('{ "where": {}, ' + '"data" : {"alert":"'
// + pushMessage + '"}' + '}', TEncoding.UTF8);
mainJsonObject := TJSONObject.Create;
whereJson := TJSONObject.Create;
mainJsonObject.AddPair(TJSONPair.Create('where', whereJson));
alertJson := TJSONObject.Create;
alertJson.AddPair(TJSONPair.Create('alert', pushMessage));
mainJsonObject.AddPair(TJSONPair.Create('data', alertJson));
JSON := TStringStream.Create(mainJsonObject.ToJSON);
mainJsonObject.Free; // free all the child objects.
webRequest := TIDHttp.Create(nil);
webRequest.Request.Connection := 'Keep-Alive';
webRequest.Request.CustomHeaders.Clear;
webRequest.Request.CustomHeaders.AddValue('X-Parse-Application-Id',
'YourApplicationID');
webRequest.Request.CustomHeaders.AddValue('X-Parse-REST-API-KEY',
'YourRestApiKey');
webRequest.Request.ContentType := 'application/json';
webRequest.Request.CharSet := 'utf-8';
webRequest.Request.ContentLength := JSON.Size;
try
try
response := webRequest.Post(parseDotComUrl, JSON);
except
on E: Exception do
begin
showmessage(response);
end;
end;
finally
webRequest.Free;
JSON.Free;
end;
end;
Thus bypassing the need for TParseApi
2) Based on UweRabbe's answer, you can also do it like this in code:
procedure TForm1.parseProviderCodeButtonClick(Sender: TObject);
var
myParseProvider: TParseProvider;
myBackendPush: TBackendPush;
myStrings: Tstrings;
whereJson: TJSONObject;
alertJson: TJSONObject;
mainJsonObject: TJSONObject;
begin
mainJsonObject := TJSONObject.Create;
whereJson := TJSONObject.Create;
mainJsonObject.AddPair(TJSONPair.Create('where', whereJson));
alertJson := TJSONObject.Create;
alertJson.AddPair(TJSONPair.Create('alert', pushMessage));
mainJsonObject.AddPair(TJSONPair.Create('data', alertJson));
myParseProvider := TParseProvider.Create(nil);
myParseProvider.ApiVersion := '1';
myParseProvider.ApplicationID := 'YourApplicationID';
myParseProvider.MasterKey := 'YourMasterKey';
myParseProvider.RestApiKey := 'YourRestApiKey';
myBackendPush := TBackendPush.Create(nil);
myBackendPush.Provider := myParseProvider;
// myBackendPush.Message := 'Hello world';
myStrings := TStringList.Create;
myStrings.Clear;
// I like putting the message in when I generate the JSON for the Target
// (since it seems I have to do it anyways, my not do it all in one place).
// You could however us TBackendPush.Message as I've commented out above.
// myStrings.Add('{ "where": { }, "data" : {"alert":"goodbye world"}}');
myStrings.Add(mainJsonObject.ToJSON);
myBackendPush.Target := myStrings;
myStrings.Free;
mainJsonObject.Free; // free all the child objects.
myBackendPush.Push;
myBackendPush.Free;
myParseProvider.Free;
end;
3) And to round this out into one complete answer (again based on UweRabbe's answer)
On your form/datamodule:
Place a TParseProvider
Place a TBackendPush - this should automatically set its Provider filed to the name of the TParseProvider you created in the previous step.
Set the TBackendPush's ApplicationID, MasterKey, RestApiKey, and Message properties
Set the TBackendPush's Push method from code.
e.g.,
procedure TForm1.Button1(Sender: TObject);
begin
BackendPush1.Push;
end;

MultiPartEntity in Indy [Delphi]

I am trying to add attachement to one service.
This taken from documentation:
I wonder if an entity can be added in indy as if it could be done in for example Java:
postRequest.setHeader("X-Atlassian-Token","nocheck");
MultipartEntity entity = new MultipartEntity();
entity.addPart("file", new FileBody(fileUpload));
postRequest.setEntity(entity);
HttpResponse response = httpClient.execute(postRequest);
Found it:
uses IdMultipartFormData
...
Stream: TIdMultipartFormDataStream;
EDITED:
For this particular problem with Jira REST API, solution would be something like:
Posting against URL: BASE_URL+/rest/api/2/issue/{issueIdOrKey}/attachments
try
lHTTP.Request.CustomHeaders.AddValue('X-Atlassian-Token', 'nocheck');
FileSize := lHTTP.Response.ContentLength;
FileStrm := TFileStream.Create(AFile, fmOpenRead or fmShareDenyWrite);
try
if FileSize < FileStrm.Size then
begin
FileStrm.Position := FileSize;
Stream := TIdMultipartFormDataStream.Create;
try
Stream.AddFile('file', AFile);
with lHTTP do
begin
with Request do
begin
ContentRangeStart := FileSize + 1;
ContentRangeEnd := FileStrm.Size;
end;
Post(self.BASE_URL + SEND_ATTACHEMENT_TO_AN_ISSUE_URL +
IntToStr(IssueID) + '/attachments', Stream);
Result := true;
end;
finally
Stream.Free;
end;
end;
finally
FileStrm.Free;
end;
except
Result := false;
end;
Note: After that one should not forget to change back the headers and to change the "Content Type" to the one that is needed for future requests

403/Forbidden using delphi application to access my Google calendar

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.

Resources