Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
KGOrders
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Cam Hayes
KGOrders
Commits
ac843731
Commit
ac843731
authored
Mar 31, 2025
by
Cam Hayes
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Customer API is now working, and cleaned up the INI file
parent
067a586d
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
163 additions
and
67 deletions
+163
-67
qbAPI.dfm
kgOrdersServer/Source/qbAPI.dfm
+32
-9
qbAPI.pas
kgOrdersServer/Source/qbAPI.pas
+125
-57
kgOrdersServer.ini
kgOrdersServer/kgOrdersServer.ini
+6
-1
No files found.
kgOrdersServer/Source/qbAPI.dfm
View file @
ac843731
...
@@ -2,31 +2,54 @@ object fQB: TfQB
...
@@ -2,31 +2,54 @@ object fQB: TfQB
Left = 0
Left = 0
Top = 0
Top = 0
Caption = 'fQB'
Caption = 'fQB'
ClientHeight =
441
ClientHeight =
579
ClientWidth =
624
ClientWidth =
962
Color = clBtnFace
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Color = clWindowText
Font.Height = -12
Font.Height = -12
Font.Name = 'Segoe UI'
Font.Name = 'Segoe UI'
Font.Style = []
Font.Style = []
OnCreate = FormCreate
TextHeight = 15
TextHeight = 15
object Memo1: TMemo
object Memo1: TMemo
Left = 0
Left = 0
Top = 142
Top = 58
Width = 623
Width = 962
Height = 299
Height = 260
Align = alBottom
Lines.Strings = (
Lines.Strings = (
'Memo1')
'Memo1')
ScrollBars = ssVertical
TabOrder = 0
TabOrder = 0
end
end
object Button1: TButton
object Button1: TButton
Left =
18
Left =
42
Top =
54
Top =
18
Width =
75
Width =
87
Height = 25
Height = 25
Caption = '
Button1
'
Caption = '
Company Info
'
TabOrder = 1
TabOrder = 1
OnClick = Button1Click
OnClick = Button1Click
end
end
object Memo2: TMemo
Left = 0
Top = 318
Width = 962
Height = 261
Align = alBottom
Lines.Strings = (
'Memo2')
ScrollBars = ssVertical
TabOrder = 2
end
object Button2: TButton
Left = 148
Top = 18
Width = 87
Height = 25
Caption = 'Get Customers'
TabOrder = 3
OnClick = Button2Click
end
end
end
kgOrdersServer/Source/qbAPI.pas
View file @
ac843731
...
@@ -11,23 +11,32 @@ uses
...
@@ -11,23 +11,32 @@ uses
System
.
Hash
,
Api
.
Database
,
Vcl
.
ExtCtrls
,
WEBLib
.
Forms
,
WEBLib
.
Controls
,
WEBLib
.
StdCtrls
,
System
.
Hash
,
Api
.
Database
,
Vcl
.
ExtCtrls
,
WEBLib
.
Forms
,
WEBLib
.
Controls
,
WEBLib
.
StdCtrls
,
WEBLib
.
ExtCtrls
,
WEBLib
.
REST
,
WEBLib
.
WebTools
,
System
.
Net
.
HttpClient
,
WEBLib
.
ExtCtrls
,
WEBLib
.
REST
,
WEBLib
.
WebTools
,
System
.
Net
.
HttpClient
,
System
.
Net
.
URLClient
,
System
.
Net
.
HttpClientComponent
,
System
.
netencoding
,
System
.
Net
.
URLClient
,
System
.
Net
.
HttpClientComponent
,
System
.
netencoding
,
IdHTTP
,
IdSSLOpenSSL
,
IdSSLOpenSSLHeaders
;
IdHTTP
,
IdSSLOpenSSL
,
IdSSLOpenSSLHeaders
,
System
.
DateUtils
,
System
.
IniFiles
;
type
type
TfQB
=
class
(
TForm
)
TfQB
=
class
(
TForm
)
Memo1
:
TMemo
;
Memo1
:
TMemo
;
Button1
:
TButton
;
Button1
:
TButton
;
Memo2
:
TMemo
;
Button2
:
TButton
;
procedure
Button1Click
(
Sender
:
TObject
);
procedure
Button1Click
(
Sender
:
TObject
);
procedure
FormCreate
(
Sender
:
TObject
);
procedure
Button2Click
(
Sender
:
TObject
);
private
private
{ Private declarations }
{ Private declarations }
httpReqTokenRefresh
:
TWebHttpRequest
;
httpReqTokenRefresh
:
TWebHttpRequest
;
var
AccessToken
,
RefreshToken
,
CompanyID
,
Client
,
Secret
:
string
;
LastRefresh
:
TDateTime
;
public
public
{ Public declarations }
{ Public declarations }
procedure
getCompanyInfo
();
procedure
getCompanyInfo
();
function
RefreshAccessToken
():
string
;
function
RefreshAccessToken
():
string
;
procedure
ConfigureSSL
(
IOHandler
:
TIdSSLIOHandlerSocketOpenSSL
);
procedure
ConfigureSSL
(
IOHandler
:
TIdSSLIOHandlerSocketOpenSSL
);
procedure
SaveTokens
(
AccessToken
,
RefreshToken
:
string
);
procedure
SaveTokens
(
AccessToken
,
RefreshToken
:
string
);
procedure
getCustomers
();
end
;
end
;
var
var
...
@@ -35,24 +44,33 @@ var
...
@@ -35,24 +44,33 @@ var
implementation
implementation
uses
Common
.
Logging
;
{$R *.dfm}
{$R *.dfm}
procedure
TfQB
.
Button1Click
(
Sender
:
TObject
);
procedure
TfQB
.
Button1Click
(
Sender
:
TObject
);
begin
begin
//getAccessToken();
getCompanyInfo
();
//getCompanyInfo();
refreshAccessToken
();
end
;
end
;
procedure
TfQB
.
SaveTokens
(
AccessToken
,
RefreshToken
:
string
);
procedure
TfQB
.
SaveTokens
(
AccessToken
,
RefreshToken
:
string
);
var
var
f
:
TStringList
;
f
:
TStringList
;
line
:
string
;
iniStr
,
line
:
string
;
iniFile
:
TIniFile
;
begin
begin
iniFile
:=
TIniFile
.
Create
(
ExtractFilePath
(
Application
.
ExeName
)
+
'kgOrdersServer.ini'
);
try
iniFile
.
WriteString
(
'Quickbooks'
,
'RefreshToken'
,
RefreshToken
);
LastRefresh
:=
Now
;
Logger
.
Log
(
1
,
'Tokens Successfully Saved'
);
finally
IniFile
.
Free
;
end
;
f
:=
TStringList
.
Create
;
f
:=
TStringList
.
Create
;
f
.
Add
(
'QUJnTzE0dXZqaDhYcUx1ZDdzcFE4bGtiOThBVXBjZEE3SGJ5TUpmQ0F0bDY1c1E1eXk6YlEwNlRSZW1IZUFHRnpWSFJhVFV2VW9CVTlqcFU5aXRLNk1PTWdxTg==#'
+
AccessToken
+
'#'
+
RefreshToken
);
// Save to file (overwrites existing file)
// Save to file (overwrites existing file)
f
.
SaveToFile
(
'QB.txt'
);
f
.
SaveToFile
(
'QB.txt'
);
...
@@ -60,6 +78,56 @@ begin
...
@@ -60,6 +78,56 @@ begin
end
;
end
;
procedure
TfQB
.
Button2Click
(
Sender
:
TObject
);
begin
GetCustomers
();
end
;
procedure
TfQB
.
getCustomers
();
var
restClient
:
TRESTClient
;
restRequest
:
TRESTRequest
;
restResponse
:
TRESTResponse
;
param
:
TRESTRequestParameter
;
res
:
string
;
jsValue
:
TJSONValue
;
jsObj
,
companyInfo
:
TJSONObject
;
begin
restClient
:=
TRESTClient
.
Create
(
nil
);
restClient
.
BaseURL
:=
'https://sandbox-quickbooks.api.intuit.com'
;
restRequest
:=
TRESTRequest
.
Create
(
nil
);
restRequest
.
Client
:=
restClient
;
restResponse
:=
TRESTResponse
.
Create
(
nil
);
restRequest
.
Response
:=
restResponse
;
if
MinutesBetween
(
Now
,
LastRefresh
)
>
58
then
begin
RefreshAccessToken
();
end
;
restRequest
.
Method
:=
rmGET
;
res
:=
'/v3/company/'
+
companyID
+
'/query?query=select * from Customer&minorversion=75'
;
restRequest
.
Resource
:=
res
;
param
:=
restRequest
.
Params
.
AddItem
;
param
.
Name
:=
'Authorization'
;
param
.
Kind
:=
pkHTTPHEADER
;
param
.
Options
:=
param
.
Options
+
[
TRESTRequestParameterOption
.
poDoNotEncode
];
param
.
Value
:=
'Bearer '
+
AccessToken
;
restRequest
.
Execute
;
memo1
.
Lines
.
Add
(
restresponse
.
Content
)
;
restClient
.
Free
;
restRequest
.
Free
;
restResponse
.
Free
;
end
;
procedure
TfQB
.
ConfigureSSL
(
IOHandler
:
TIdSSLIOHandlerSocketOpenSSL
);
procedure
TfQB
.
ConfigureSSL
(
IOHandler
:
TIdSSLIOHandlerSocketOpenSSL
);
begin
begin
// For Indy 10.6.2+ (Delphi 10.2 Tokyo+)
// For Indy 10.6.2+ (Delphi 10.2 Tokyo+)
...
@@ -78,20 +146,51 @@ begin
...
@@ -78,20 +146,51 @@ begin
IOHandler
.
SSLOptions
.
Mode
:=
sslmClient
;
IOHandler
.
SSLOptions
.
Mode
:=
sslmClient
;
end
;
end
;
procedure
TfQB
.
FormCreate
(
Sender
:
TObject
);
var
iniFile
:
TIniFile
;
begin
iniFile
:=
TIniFile
.
Create
(
ExtractFilePath
(
Application
.
ExeName
)
+
'kgOrdersServer.ini'
);
Client
:=
iniFile
.
ReadString
(
'Quickbooks'
,
'ClientID'
,
''
);
Secret
:=
iniFile
.
ReadString
(
'Quickbooks'
,
'ClientSecret'
,
''
);
CompanyID
:=
iniFile
.
ReadString
(
'Quickbooks'
,
'CompanyID'
,
''
);
RefreshToken
:=
iniFile
.
ReadString
(
'Quickbooks'
,
'RefreshToken'
,
''
);
end
;
function
TfQB
.
RefreshAccessToken
:
string
;
function
TfQB
.
RefreshAccessToken
:
string
;
// Refresh Token changes so make sure to save refresh token.
// Refresh Token changes so make sure to save refresh token.
var
var
IdHTTP
:
TIdHTTP
;
IdHTTP
:
TIdHTTP
;
SSLIO
:
TIdSSLIOHandlerSocketOpenSSL
;
SSLIO
:
TIdSSLIOHandlerSocketOpenSSL
;
RequestStream
:
TStringStream
;
RequestStream
:
TStringStream
;
EncodedAuth
,
PostData
,
response
,
RefreshToken
,
AccessToken
:
string
;
EncodedAuth
,
EncodedAuth2
,
PostData
,
response
:
string
;
f
:
TStringList
;
fi
:
string
;
JSObj
:
TJSONObject
;
JSObj
:
TJSONObject
;
iniFile
:
TIniFile
;
Encoder
:
TBase64Encoding
;
begin
begin
// 1. Encode credentials (same as working Postman request)
// 1. Encode credentials (same as working Postman request)
EncodedAuth
:=
'QUJnTzE0dXZqaDhYcUx1ZDdzcFE4bGtiOThBVXBjZEE3SGJ5TUpmQ0F0bDY1c1E1eXk6YlEwNlRSZW1IZUFHRnpWSFJhVFV2VW9CVTlqcFU5aXRLNk1PTWdxTg=='
;
// TNetEncoding.Base64.Encode adds a new line every 72 chars, this stops that
Encoder
:=
TBase64Encoding
.
Create
(
0
);
if
(
(
Client
=
''
)
or
(
Secret
=
''
)
)
then
begin
Logger
.
Log
(
1
,
'Missing Client ID or Client Secret in INI File'
);
Exit
();
end
;
EncodedAuth
:=
Encoder
.
Encode
(
Client
+
':'
+
Secret
);
Memo1
.
Lines
.
Add
(
EncodedAuth
);
if
RefreshToken
=
''
then
begin
Logger
.
Log
(
3
,
'Missing Refresh Token, Please Manually Get a New One and Store in INI File'
);
Exit
();
end
;
// 2. Prepare POST data (EXACTLY as in Postman)
// 2. Prepare POST data (EXACTLY as in Postman)
PostData
:=
'grant_type=refresh_token&refresh_token=
AB11751646575Ns7nA3AniegiOuRWAak6CvqMigWqSIdwhAOEe'
;
PostData
:=
'grant_type=refresh_token&refresh_token=
'
+
RefreshToken
;
// 3. Configure HTTP client
// 3. Configure HTTP client
IdHTTP
:=
TIdHTTP
.
Create
(
nil
);
IdHTTP
:=
TIdHTTP
.
Create
(
nil
);
...
@@ -110,25 +209,20 @@ begin
...
@@ -110,25 +209,20 @@ begin
// 4. Create and send request
// 4. Create and send request
RequestStream
:=
TStringStream
.
Create
(
PostData
,
TEncoding
.
UTF8
);
RequestStream
:=
TStringStream
.
Create
(
PostData
,
TEncoding
.
UTF8
);
try
try
Memo1
.
Lines
.
Add
(
'Sending request...'
);
Memo1
.
Lines
.
Add
(
'URL: https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer'
);
Memo1
.
Lines
.
Add
(
'Headers:'
);
Memo1
.
Lines
.
Add
(
'Authorization: Basic '
+
EncodedAuth
);
Memo1
.
Lines
.
Add
(
'Body: '
+
PostData
);
// Execute POST
// Execute POST
try
try
response
:=
IdHTTP
.
Post
(
'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer'
,
RequestStream
);
response
:=
IdHTTP
.
Post
(
'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer'
,
RequestStream
);
Memo1
.
Lines
.
Add
(
response
);
JSObj
:=
TJSONObject
.
ParseJSONValue
(
response
)
as
TJSONObject
;
JSObj
:=
TJSONObject
.
ParseJSONValue
(
response
)
as
TJSONObject
;
RefreshToken
:=
JSObj
.
GetValue
(
'refresh_token'
).
ToString
;
RefreshToken
:=
JSObj
.
GetValue
(
'refresh_token'
).
ToString
.
Trim
([
'"'
])
;
AccessToken
:=
JSObj
.
GetValue
(
'access_token'
).
ToString
;
AccessToken
:=
JSObj
.
GetValue
(
'access_token'
).
ToString
.
Trim
([
'"'
])
;
SaveTokens
(
AccessToken
,
RefreshToken
);
SaveTokens
(
AccessToken
,
RefreshToken
);
Memo1
.
Lines
.
Add
(
RefreshToken
);
Result
:=
AccessToken
;
Memo1
.
Lines
.
Add
(
AccessToken
);
Logger
.
Log
(
1
,
'qbAPI - Tokens Successfully Saved'
);
Memo1
.
Lines
.
Add
(
'Tokens Successfully Saved'
);
except
except
on
E
:
EIdHTTPProtocolException
do
on
E
:
EIdHTTPProtocolException
do
Memo
1
.
Lines
.
Add
(
'Error: '
+
E
.
Message
+
#
13
#
10
+
'Response: '
+
E
.
ErrorMessage
);
Memo
2
.
Lines
.
Add
(
'Error: '
+
E
.
Message
+
#
13
#
10
+
'Response: '
+
E
.
ErrorMessage
);
end
;
end
;
finally
finally
RequestStream
.
Free
;
RequestStream
.
Free
;
...
@@ -146,21 +240,9 @@ var
...
@@ -146,21 +240,9 @@ var
restResponse
:
TRESTResponse
;
restResponse
:
TRESTResponse
;
param
:
TRESTRequestParameter
;
param
:
TRESTRequestParameter
;
res
:
string
;
res
:
string
;
sid
:
string
;
jsValue
:
TJSONValue
;
jsValue
:
TJSONValue
;
jsObj
,
companyInfo
:
TJSONObject
;
jsObj
,
companyInfo
:
TJSONObject
;
jaCalls
:
TJSONArray
;
joCall
:
TJSONObject
;
row
:
integer
;
i
:
integer
;
uri
,
temp
:
string
;
sql
:
string
;
companyID
,
client
,
secret
:
string
;
pair
:
TJSONPair
;
begin
begin
client
:=
'ABgO14uvjh8XqLud7spQ8lkb98AUpcdA7HbyMJfCAtl65sQ5yy'
;
secret
:=
'bQ06TRemHeAGFzVHRaTUvUoBU9jpU9itK6MOMgqN'
;
companyID
:=
'9341454272655710'
;
restClient
:=
TRESTClient
.
Create
(
nil
);
restClient
:=
TRESTClient
.
Create
(
nil
);
restClient
.
BaseURL
:=
'https://sandbox-quickbooks.api.intuit.com'
;
restClient
.
BaseURL
:=
'https://sandbox-quickbooks.api.intuit.com'
;
...
@@ -171,40 +253,26 @@ begin
...
@@ -171,40 +253,26 @@ begin
restRequest
.
Response
:=
restResponse
;
restRequest
.
Response
:=
restResponse
;
if
MinutesBetween
(
Now
,
LastRefresh
)
>
58
then
begin
RefreshAccessToken
();
end
;
restRequest
.
Method
:=
rmGET
;
restRequest
.
Method
:=
rmGET
;
res
:=
'/v3/company/
9341454272655710/companyinfo/9341454272655710'
;
res
:=
'/v3/company/
'
+
companyID
+
'/companyinfo/'
+
companyID
;
restRequest
.
Resource
:=
res
;
restRequest
.
Resource
:=
res
;
param
:=
restRequest
.
Params
.
AddItem
;
param
:=
restRequest
.
Params
.
AddItem
;
param
.
Name
:=
'Authorization'
;
param
.
Name
:=
'Authorization'
;
param
.
Kind
:=
pkHTTPHEADER
;
param
.
Kind
:=
pkHTTPHEADER
;
param
.
Options
:=
param
.
Options
+
[
TRESTRequestParameterOption
.
poDoNotEncode
];
param
.
Options
:=
param
.
Options
+
[
TRESTRequestParameterOption
.
poDoNotEncode
];
param
.
Value
:=
'Bearer
eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiZGlyIn0..jQN6f8KqIOzuz9vUug_-pQ.EmzmjomzD8h1L3jLN2-LnwbeegkYJw2WSqm5FFhXCTBGfaK9Hc16l_NuoKgesfYUNBMFuAO59wgc7bKni6QoCZmA6R40_RuC-fq7aSLms_JHLCvaWe-TiIY0iEDgtCgFP7Bfnw7VAP8QL-A8vkG0wVqFhrjLSbpZWIDMo9n_WG5U_tobHt052YKczM4A-wLtmouLnRNewvjk4YqU36867Z4fOZQ4c2AHm5yhA1CRfYNiKflT4Wv_ERMEG_A1U5FCj_vTs3njbjCrSxooKeW6yJy3pzbAv0eVH_0KFsAi5LjTfyDXPRfVa2XMjtJcfEKnFYvnFw4owOYP8z4TPVB2vNSeYSimHivOWlaWLFQzc1jbGgUpdeMdMqA9Wmpt0JgYZxCSMxDfTmSdYhDOR0ng6osf0qTaeSLsbweYMZRFZTZ1Ob8MNiAhTEPARTrJYgJ3YHdY1G_nX4-LhTJetkduiNzjK_LekBplwy0NnLAujqaiL0D6Wrwc-LNrYgQ4Qc3j46U4jGkOLJ88CIg85pDSAeFtZk5-vL8mRDpZ7s6KX62mIOpzdx_nTTeQgsTDhvUfusQcBshoEw93Tmv5s-tr96pSHXcCq4WLp6bRMsay65cEapQVE4GnZrXoWBwBh0cnpE6DdmXS2GQXXvW6fFsQcq8eTPyLGs-qR6nJL-yybGwI_XTmEsaYzbdT4cFFRRqTstbN_UWnv2BtIYOwgDPcOsBg8CKPa5ZZfJFunkrRI8HDLckgkSoLVMqXKzmMQleu.-tUa8rbIzo9JQylw3T_f1g'
;
param
.
Value
:=
'Bearer
'
+
AccessToken
;
restRequest
.
Execute
;
restRequest
.
Execute
;
//restresponse.Content
memo1
.
Lines
.
Add
(
restresponse
.
Content
)
;
//jsValue := restResponse.JSONValue;
//jsValue := restResponse.JSONValue;
jsObj
:=
TJSONObject
.
ParseJSONValue
(
restresponse
.
Content
)
as
TJSONObject
;
//jsObj := TJSONObject.ParseJSONValue(restresponse.Content) as TJSONObject;
companyInfo
:=
TJSONObject
(
jsObj
.
GetValue
(
'CompanyInfo'
));
//companyInfo := TJSONObject(jsObj.GetValue('CompanyInfo'));
for
pair
in
companyInfo
do
begin
// Get the value as string
if
pair
.
JsonValue
is
TJSONString
then
temp
:=
TJSONString
(
pair
.
JsonValue
).
Value
else
temp
:=
pair
.
JsonValue
.
ToString
;
// For non-string values
// Add the key-value pair to the Memo
Memo1
.
Lines
.
Add
(
pair
.
JsonString
.
Value
+
': '
+
temp
);
end
;
//temp := companyInfo.Values['CompanyName'].Value;
//Memo1.Lines.Add(temp);
i
:=
1
;
restClient
.
Free
;
restClient
.
Free
;
restRequest
.
Free
;
restRequest
.
Free
;
...
...
kgOrdersServer/kgOrdersServer.ini
View file @
ac843731
[Settings]
[Settings]
MemoLogLevel
=
4
MemoLogLevel
=
4
FileLogLevel
=
5
FileLogLevel
=
5
LogFileNum
=
397
LogFileNum
=
466
webClientVersion
=
1.0.0
webClientVersion
=
1.0.0
[Database]
[Database]
...
@@ -12,3 +12,8 @@ Server=192.168.159.132
...
@@ -12,3 +12,8 @@ Server=192.168.159.132
--Username
=
--Username
=
--Password
=
emsys!012
--Password
=
emsys!012
[Quickbooks]
CompanyID
=
9341454272655710
ClientID
=
ABgO14uvjh8XqLud7spQ8lkb98AUpcdA7HbyMJfCAtl65sQ5yy
ClientSecret
=
bQ06TRemHeAGFzVHRaTUvUoBU9jpU9itK6MOMgqN
RefreshToken
=
RT1-67-H0-1752156152pdyylsncyvlmxnx11uv4
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment