intuit/src/API.php

226 lines
5.9 KiB
PHP
Raw Normal View History

2022-08-14 04:40:13 +00:00
<?php
namespace Intuit;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Intuit\Models\ProviderToken;
2022-08-18 13:18:14 +00:00
use Intuit\Response\{Customer,ListList};
2022-08-14 04:40:13 +00:00
final class API
{
// https://developer.intuit.com/app/developer/qbo/docs/learn/rest-api-features
// @todo implement wait - will get 429 when throttling occurs, wait 60s
// @todo 500 requests/min per realm
// @todo requests that take 120s will timeout
// @todo max entities is 1000, use pagination for more
private const LOGKEY = 'API';
private const CACHE_TIME = 60*60*12;
private const VERSION = 'v3';
private const MINOR_VERSION = 65;
private const CURLOPT_HEADER = FALSE;
private ProviderToken $token;
public function __construct(ProviderToken $token,bool $tryprod=FALSE)
{
$this->url = (config('app.env') == 'local' && ! $tryprod) ? 'https://sandbox-quickbooks.api.intuit.com' : 'https://quickbooks.api.intuit.com';
$this->token = $token;
Log::debug(sprintf('%s:Intuit API for id [%s]',static::LOGKEY,$token->realm_id));
}
/**
* Convert an Array to Curl Headers
*
* @param array $header
* @return array Curl Headers
*/
private function convertHeaders(array $header): array
{
return collect($header)
->map(function($value,$key) { return sprintf('%s:%s',$key,$value); })
->values()
->toArray();
}
/**
* Call the API
*
* @param string $path
* @param array $parameters
* @return object|array
* @throws \Exception
*/
private function execute(string $path,array $parameters=[])
{
$url = sprintf('%s/%s/company/%s/%s',$this->url,self::VERSION,$this->token->realm_id,$path);
$method = Arr::get($parameters,'method','GET');
if ($parameters)
Arr::forget($parameters,'method');
// If we are passed an array, we'll do a normal post.
switch ($method) {
case 'GET':
$request = $this->prepareRequest(
$url,
$parameters,
[
'Accept' => 'application/json',
'Authorization' => sprintf('Bearer %s',$this->token->access_token),
'Content-Type' => 'application/json',
]
);
break;
case 'POST':
$request = $this->prepareRequestPost(
$url,
$parameters,
[
2022-08-18 13:18:14 +00:00
'Accept' => 'application/json',
'Authorization' => sprintf('Bearer %s',$this->token->access_token),
'Content-Type' => 'application/json',
2022-08-14 04:40:13 +00:00
]
);
break;
2022-08-18 13:18:14 +00:00
/*
2022-08-14 04:40:13 +00:00
case 'PUT':
$request = $this->prepareRequestPut(
$url,
$parameters,
[
'accept: application/json',
'Api-Request-Id: '.$request_id,
'Api-Signature: '.$signature,
]
);
break;
*/
default:
throw new \Exception(sprintf('Unknown method: %s',$method));
}
$key = md5($path.serialize($parameters));
//Cache::forget($key);
$result = Cache::remember($key,self::CACHE_TIME,function() use ($request,$url) {
try {
$response = curl_exec($request);
switch($x=curl_getinfo($request,CURLINFO_HTTP_CODE)) {
case 400:
case 401:
case 403:
case 404:
dump([$xx=curl_getinfo($request),'response'=>$response]);
throw new \Exception(sprintf('CURL exec returned %d: %s (%s)',$x,curl_error($request),serialize($xx)));
}
curl_close($request);
return json_decode(self::CURLOPT_HEADER ? substr($response,curl_getinfo($request,CURLINFO_HEADER_SIZE)) : $response);
} catch (\Exception $e) {
dump(['error'=>$e->getMessage()]);
Log::error(sprintf('%s:Got an error while posting to [%s] (%s)',static::LOGKEY,$url,$e->getMessage()),['m'=>__METHOD__]);
curl_close($request);
throw new \Exception($e->getMessage());
}
});
return $result;
}
2022-08-18 13:18:14 +00:00
/**
* Get a specific customer record
*
* @param int $id
* @param array $parameters
* @return Customer
* @throws \Exception
*/
public function getCustomer(int $id,array $parameters=[]): Customer
{
Log::debug(sprintf('%s:Get a specific customer [%d]',static::LOGKEY,$id));
return new Customer($this->execute('customer/'.$id,$parameters));
}
2022-08-14 04:40:13 +00:00
/**
* Get a list of our clients
*
* select * from Account where Metadata.CreateTime > '2014-12-31'
*
* @param array $parameters
* @return ListList
* @throws \Exception
*/
public function getCustomers(array $parameters=[]): ListList
{
Log::debug(sprintf('%s:Get a list of customers',static::LOGKEY));
$key = 'customers';
$parameters['query'] = 'select * from Customer';
return new ListList($this->execute('query',$parameters),$key);
}
/**
* Setup the API call
*
* @param $url
* @param array $parameters
* @param array $headers
* @return \CurlHandle
*/
2022-08-18 13:18:14 +00:00
private function prepareRequest(string $url,array $parameters=[],array $headers=[]): \CurlHandle
2022-08-14 04:40:13 +00:00
{
$request = curl_init();
curl_setopt($request,CURLOPT_HEADER,self::CURLOPT_HEADER); // debugging set this to TRUE, but it affects our body.
curl_setopt($request,CURLOPT_URL,$url.'?'.http_build_query(array_merge($parameters,['minorversion'=>self::MINOR_VERSION])));
curl_setopt($request,CURLOPT_RETURNTRANSFER,TRUE);
curl_setopt($request,CURLOPT_CUSTOMREQUEST,'GET');
curl_setopt($request,CURLINFO_HEADER_OUT,TRUE);
curl_setopt($request,CURLOPT_SSL_VERIFYPEER,TRUE);
curl_setopt($request,CURLOPT_CONNECTTIMEOUT,15);
curl_setopt($request,CURLOPT_TIMEOUT,15);
if ($headers)
curl_setopt($request,CURLOPT_HTTPHEADER,$this->convertHeaders($headers));
return $request;
}
2022-08-18 13:18:14 +00:00
private function prepareRequestPost(string $url,array $parameters=[],array $headers=[]): \CurlHandle
{
$request = $this->prepareRequest($url,$parameters,$headers);
curl_setopt($request,CURLOPT_CUSTOMREQUEST,'POST');
curl_setopt($request,CURLOPT_POSTFIELDS,json_encode($parameters));
return $request;
}
/**
* Update a customer
*
* @param array $parameters
* @return Customer
* @throws \Exception
*/
public function updateCustomer(array $parameters=[]): Customer
{
Log::debug(sprintf('%s:Update customer',static::LOGKEY),['params'=>$parameters]);
return new Customer($this->execute('customer',array_merge($parameters,['method'=>'POST'])));
}
2022-08-14 04:40:13 +00:00
}