I try to connect me to my stripe payment test account with delphi.
The connect API is here:
Stripe connect API
Curl example:
curl https://api.stripe.com/v1/charges \
-u sk_test_CpkBxhx9gcmNYYQTZIXU43Bv:
I tried using Indy TIdHTTP component with TIdSSLIOHandlerSocketOpenSSL
and calling post with Tstringlist or TIdMultipartFormDataStream as parameter
but I receive always response: 401 - Unauthorized
Here my code:
var
Data: TIdMultipartFormDataStream;
https: TIdHTTP;
ssl: TIdSSLIOHandlerSocketOpenSSL;
begin
https := TIdHTTP.Create(self);
ssl := TIdSSLIOHandlerSocketOpenSSL.Create(Self);
https.IOHandler := ssl;
https.Request.BasicAuthentication := True;
Data := TIdMultipartFormDataStream.Create;
//Data.AddFormField('api_key', 'sk_test_CpkBxhx9gcmNYYQTZIXU43Bv');
Data.AddFormField('apikey', 'sk_test_CpkBxhx9gcmNYYQTZIXU43Bv');
https.Post('https://api.stripe.com/v1/charges', Data);
Memo1.lines.Add( https.ResponseText );
Data.Free;
end;
Any help or suggestion would bee very appreciated.
Thanks,
Peter
You must not use a form field to transfer the API key. Instead, set the Request.Username property. The password is empty, so Request.Passwort is unused. From the API docs on your linked page:
Authentication to the API occurs via HTTP Basic Auth. Provide your API
key as the basic auth username. You do not need to provide a password.
This example works with Indy 10.6.2 and OpenSSL libraries in the program folder:
program Project31229779;
{$APPTYPE CONSOLE}
uses
IdHTTP, SysUtils;
var
HTTP: TIdHTTP;
begin
HTTP := TIdHTTP.Create;
try
HTTP.Request.BasicAuthentication := True;
HTTP.Request.Username := 'sk_test_CpkBxhx9gcmNYYQTZIXU43Bv';
try
WriteLn(HTTP.Get('https://api.stripe.com/v1/charges'));
except
on E: EIdHTTPProtocolException do
begin
WriteLn(E.Message);
WriteLn(E.ErrorMessage);
end;
on E: Exception do
begin
WriteLn(E.Message);
end;
end;
finally
HTTP.Free;
end;
ReadLn;
end.
Note: you may also put the user name / password in the URL:
HTTP.Request.BasicAuthentication := True;
try
WriteLn(HTTP.Get('https://sk_test_CpkBxhx9gcmNYYQTZIXU43Bv:#api.stripe.com/v1/charges'));
I have a TIdPop3Server in one application that has a IdServerIOHandlerSSLOpenSSL1 attached to it and retrieves emails and sends them to a TIdPop3 client in another application (having TIdSSLIOHandlerSocketOpenSSL attached to it). Everything's fine when the connections are made insecure using port 110. But when I try to use SSL connection through port 995 I get error Connection Closed Gracefully after connect attemp from the client fails. This is my Pop3SeverOnConnect event :
procedure TMainForm.Pop3ServerConnect(AContext: TIdContext);
begin
if (AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase) then
TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough :=
(AContext.Binding.Port <> 995);
showmessage('SSL connection made!');
end;
And this is the client-side :
procedure TMainForm.btnCheckMailBoxClick(Sender: TObject);
begin
IdSSLIOHandlerSocketOpenSSL1.PassThrough := False;
POP3Client.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
with POP3Client do begin
AuthType := patUserPass;
Host := myHost;
UserName := myUserName;
Password := myPass;
Port := myPort;
end;
try
POP3Client.Connect;
Except on e : Exception do
showmessage('error=' + e.Message);
end;
// code for retrieving message data
end;
And I always get an exception from Pop3Client.Connect like I've already mentioned above (The message SSL connection made! in the server application never shows up). If I use however another mail client like for example Mozilla Thunderbird I achieve a successful SSL connection for port 995. So the problem should be somewhere in the client's procedure but who knows - that's why I'm asking you guys for help.
In your client code, you need to set the TIdPOP3.UseTLS property instead of the TIdSSLIOHandlerSocketOpenSSL.PassThrough property directly, eg:
procedure TMainForm.btnCheckMailBoxClick(Sender: TObject);
begin
with POP3Client do
begin
IOHandler := IdSSLIOHandlerSocketOpenSSL1;
AuthType := patUserPass;
UseTLS := utUseImplicitTLS; // <-- here
Host := myHost;
UserName := myUserName;
Password := myPass;
Port := myPort;
end;
try
POP3Client.Connect;
try
// code for retrieving message data
finally
POP3Client.Disconnect;
end;
except
on e : Exception do
ShowMessage('error=' + e.Message);
end;
end;
In your server code, you need to get rid of the ShowMessage(). TIdPOP3Server is multi-threaded, the OnConnect event is fired in the context of a worker thread, and ShowMessage() is not thread-safe. If you must display a popup message, use Windows.MessageBox() instead.
I want to make the registration process a user name and password. I use Delphi 7. Component which can be done? (I know very little english, sorry.)
http request is easiest method (with GET, or POST), but preferred you'll want to use SSL if you dont want passwords/usernames passed along to your webserver un-encrypted.
Example of using POST request:
uses
IdHTTP;
function PostData(const URL: string; Params: TStrings): string;
var
IdHTTP: TIdHTTP;
begin
Result := '';
IdHTTP := TIDHttp.Create(nil);
try
IdHTTP.HandleRedirects := True;
IdHTTP.ReadTimeout := 5000;
Result := IdHTTP.Post(URL, Params);
finally
IdHTTP.Free;
end;
end;
Optionally you can write your own socket, but that will be more difficult because you'll have to write your own listener. (Which is usually not allowed on most shared hosting plans.)
I'm trying to access the URL Shortener ( http://goo.gl/ ) via its API from within Delphi.
However, the only result I get is: HTTP/1.0 400 Bad Request (reason: parseError)
Here is my code (on a form with a Button1, Memo1 and IdHTTP1 that has IdSSLIOHandlerSocketOpenSSL1 as its IOHandler. I got the necessary 32-bit OpenSSL DLLs from http://indy.fulgan.com/SSL/ and put them in the .exe's directory):
procedure TFrmMain.Button1Click(Sender: TObject);
var html, actionurl: String;
makeshort: TStringList;
begin
try
makeshort := TStringList.Create;
actionurl := 'https://www.googleapis.com/urlshortener/v1/url';
makeshort.Add('{"longUrl": "http://slashdot.org/stories"}');
IdHttp1.Request.ContentType := 'application/json';
//IdHTTP1.Request.ContentEncoding := 'UTF-8'; //Using this gives error 415
html := IdHTTP1.Post(actionurl, makeshort);
memo1.lines.add(idHTTP1.response.ResponseText);
except on e: EIdHTTPProtocolException do
begin
memo1.lines.add(idHTTP1.response.ResponseText);
memo1.lines.add(e.ErrorMessage);
end;
end;
memo1.Lines.add(html);
makeshort.Free;
end;
Update: I have left off my API key in this example (should usually work well without one for a few tries), but if you want to try it with your own, you can substitute the actionurl string with
'https://www.googleapis.com/urlshortener/v1/url?key=<yourapikey>';
The ParseError message leads me to believe that there might be something wrong with the encoding of the longurl when it gets posted but I wouldn't know what to change.
I've been fuzzing over this for quite a while now and I'm sure the mistake is right before my eyes - I'm just not seeing it right now.
Any help is therefore greatly appreciated!
Thanks!
As you discovered, the TStrings overloaded version of the TIdHTTP.Post() method is the wrong method to use. It sends an application/x-www-form-urlencoded formatted request, which is not appropriate for a JSON formatted request. You have to use the TStream overloaded version of the TIdHTTP.Post() method instead`, eg:
procedure TFrmMain.Button1Click(Sender: TObject);
var
html, actionurl: String;
makeshort: TMemoryStream;
begin
try
makeshort := TMemoryStream.Create;
try
actionurl := 'https://www.googleapis.com/urlshortener/v1/url';
WriteStringToStream(makeshort, '{"longUrl": "http://slashdot.org/stories"}', IndyUTF8Encoding);
makeshort.Position := 0;
IdHTTP1.Request.ContentType := 'application/json';
IdHTTP1.Request.Charset := 'utf-8';
html := IdHTTP1.Post(actionurl, makeshort);
finally
makeshort.Free;
end;
Memo1.Lines.Add(IdHTTP1.Response.ResponseText);
Memo1.Lines.Add(html);
except
on e: Exception do
begin
Memo1.Lines.Add(e.Message);
if e is EIdHTTPProtocolException then
Memo1.lines.Add(EIdHTTPProtocolException(e).ErrorMessage);
end;
end;
end;
From the URL shortener API docs:
Every request your application sends to the Google URL Shortener API
needs to identify your application to Google. There are two ways to
identify your application: using an OAuth 2.0 token (which also
authorizes the request) and/or using the application's API key.
Your example does not contain code for OAuth or API key authentication.
To authenticate with an API key, the docs are clear:
After you have an API key, your application can append the query
parameter key=yourAPIKey to all request URLs.
My story is that I am designing a new app which must communicate with a Windows service. After much research I have come to the conclusion that Named Pipes are the recommended method ( How do I send a string from one instance of my Delphi program to another? ) however, it appears that I can't use SendMessage or Named Pipes in Win7 due to security problems... the messages never reach outside the service to the application.
I am using the Russell Libby's named Pipe components, which work without a hitch between normal desktop apps, but the Windows service seems to be throwing a wrench in the solution. Further research tells me that it may be possible to open up security on both sides to let them communicate, however, my knowledge level on this is minimal at best, and I haven't been able to make heads or tails of the possible API calls.
Based on the Delphi component pipes.pas, what needs to be done to open up this baby so both sides can start talking? I'm sure the following two functions from the pipes.pas file identify the security attributes, is anyone able to help me out here?
Thanks!
procedure InitializeSecurity(var SA: TSecurityAttributes);
var
sd: PSecurityDescriptor;
begin
// Allocate memory for the security descriptor
sd := AllocMem(SECURITY_DESCRIPTOR_MIN_LENGTH);
// Initialize the new security descriptor
if InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION) then
begin
// Add a NULL descriptor ACL to the security descriptor
if SetSecurityDescriptorDacl(sd, True, nil, False) then
begin
// Set up the security attributes structure
SA.nLength := SizeOf(TSecurityAttributes);
SA.lpSecurityDescriptor := sd;
SA.bInheritHandle := True;
end
else
// Failed to init the sec descriptor
RaiseWindowsError;
end
else
// Failed to init the sec descriptor
RaiseWindowsError;
end;
procedure FinalizeSecurity(var SA: TSecurityAttributes);
begin
// Release memory that was assigned to security descriptor
if Assigned(SA.lpSecurityDescriptor) then
begin
// Reource protection
try
// Free memory
FreeMem(SA.lpSecurityDescriptor);
finally
// Clear pointer
SA.lpSecurityDescriptor := nil;
end;
end;
end;
Windows Vista, Seven and 2008 enforce a more secure use of named pipes, see for example http://blogs.technet.com/b/nettracer/archive/2010/07/23/why-does-anonymous-pipe-access-fail-on-windows-vista-2008-windows-7-or-windows-2008-r2.aspx
When we migrated our product from Win 2K to Win7, we ran our Named Pipes quit working. After 2 weeks talking with MS (and $275), we discovered it was being caused by the Use Shared Folders file settings. Unchecking this feature allowed us to continue with pipes.
I tried to implement this one:
function GetUserSid(var SID: PSID; var Token: THandle): boolean;
var TokenUserSize: DWORD;
TokenUserP: PSIDAndAttributes;
begin
result := false;
if not OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, Token) then
if (GetLastError <> ERROR_NO_TOKEN) or
not OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, Token) then
Exit;
TokenUserP := nil;
TokenUserSize := 0;
try
if not GetTokenInformation(Token, TokenUser, nil, 0, TokenUserSize) and
(GetLastError <> ERROR_INSUFFICIENT_BUFFER) then
Exit;
TokenUserP := AllocMem(TokenUserSize);
if not GetTokenInformation(Token, TokenUser, TokenUserP,
TokenUserSize, TokenUserSize) then
Exit;
SID := TokenUserP^.Sid;
result := true;
finally
FreeMem(TokenUserP);
end;
end;
function ConvertSidToStringSidA(aSID: PSID; var aStr: PAnsiChar): BOOL; stdcall; external advapi32;
function ConvertStringSecurityDescriptorToSecurityDescriptorA(
StringSecurityDescriptor: PAnsiChar; StringSDRevision: DWORD;
SecurityDescriptor: pointer; SecurityDescriptorSize: Pointer): BOOL; stdcall; external advapi32;
const
SDDL_REVISION_1 = 1;
procedure InitializeSecurity(var SA: TSecurityAttributes; var SD; Client: boolean);
var OK: boolean;
Token: THandle;
pSidOwner: PSID;
pSid: PAnsiChar;
SACL: AnsiString;
begin
fillchar(SD,SECURITY_DESCRIPTOR_MIN_LENGTH,0);
// Initialize the new security descriptor
OK := false;
if InitializeSecurityDescriptor(#SD, SECURITY_DESCRIPTOR_REVISION) then begin
if Client or (OSVersionInfo.dwMajorVersion<6) then
// before Vista: add a NULL descriptor ACL to the security descriptor
OK := SetSecurityDescriptorDacl(#SD, true, nil, false)
else begin
// since Vista: need to specify special ACL
if GetUserSid(pSidOwner,Token) then
try
if ConvertSidToStringSidA(pSidOwner,pSid) then
try
SACL := 'D:(A;;GA;;;'+pSID+')(A;;GWGR;;;AN)(A;;GWGR;;;WD)S:(ML;;NW;;;S-1-16-0)';
OK := ConvertStringSecurityDescriptorToSecurityDescriptorA(
pointer(SACL),SDDL_REVISION_1,#SD,nil);
finally
LocalFree(PtrUInt(pSid));
end;
finally
FreeSid(pSidOwner);
CloseHandle(Token);
end;
end;
end;
if OK then begin
// Set up the security attributes structure
SA.nLength := sizeof(TSecurityAttributes);
SA.bInheritHandle := true;
SA.lpSecurityDescriptor := #SD;
end else
fillchar(SA,sizeof(SA),0); // mark error: no security
end;
It seems to work on the server side (i.e. the security attributes are created as expected), and you will have to write the client side code, without forgetting to add the pipe name in SYSTEM\CurrentControlSet\Services\lanmanserver\parameters\NullSessionPipes registry key, as expected.
I seem to remember that RemObjects has a named pipe client/server control in their package. Unless you are on a budget I would strongly recommend that you have a look at finished components for things like this. It is both time consuming and tricky to get right.
Alternatively, Justin Smyth has an article on named pipes right now. Check out his blog on the subject here: http://smythconsulting.blogspot.com/2011/07/smartmediaplayer-pipes-part4.html
Good luck!
I had the same kind of problem and just solved it. For me the reason it didn't work was because Russels TPipe implementetion has a check on the threads ID's just before the Pipe gets created: if not(Sync.SyncBaseTID = FNotifyThread) then..
It turned out I was creating the TPipeServer at the wrong place in my service. (I overrided DoStart etc instead of using the event OnStart... don't do that!)
I am now creating the TPipeServer instance in the same thread I later on activate it in.