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)); } /* STATIC */ public static function url(bool $tryprod=FALSE): string { return (config('app.env') == 'local' && ! $tryprod) ? 'https://app.sandbox.qbo.intuit.com/app' : 'https://app.qbo.intuit.com/app'; } /** * 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, [ 'Accept' => 'application/json', 'Authorization' => sprintf('Bearer %s',$this->token->access_token), 'Content-Type' => 'application/json', ] ); break; /* 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 0: switch (curl_errno($request)) { // DNS Resolving issue case 6: throw new ConnectionIssueException(sprintf('%s:%s',self::LOGKEY,curl_error($request))); default: dump(['getinfo' => curl_getinfo($request),'curlerror' => curl_error($request),'errno' => curl_errno($request)]); abort(500,'Error 0'); } 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); if (! $response) throw new \Exception(sprintf('%s:Request to [%s] returned no data?',self::LOGKEY,$url),['code'=>$x]); return json_decode(self::CURLOPT_HEADER ? substr($response,curl_getinfo($request,CURLINFO_HEADER_SIZE)) : $response); } catch (ConnectException|ConnectionIssueException $e) { throw new ConnectionIssueException($e->getMessage()); } 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; } /** * Get a list of our classes * * @param array $parameters * @return ListList * @throws \Exception */ public function getClasses(array $parameters=[]): ListList { Log::debug(sprintf('%s:Get a list of classes',static::LOGKEY)); $key = 'Class'; $parameters['query'] = 'select * from Class'; return new ListList($this->execute('query',$parameters),$key); } /** * Find a customer by their email address * * @param string $id * @param array $parameters * @return Customer * @throws InvalidQueryResultException */ public function getAccountQuery(string $id,array $parameters=[]): Customer { Log::debug(sprintf('%s:Get a specific account [%s]',static::LOGKEY,$id)); $parameters['query'] = sprintf("select * from Customer where PrimaryEmailAddr='%s'",$id); $x = $this->execute('query',$parameters); if ((! $x->QueryResponse) || (! $x->QueryResponse->Customer) || (count($x->QueryResponse->Customer) !== 1)) throw new InvalidQueryResultException(sprintf('%s:Query response malformed',self::LOGKEY)); return new Customer($x->QueryResponse); } /** * 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)); } /** * Get a list of our clients * * @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 = 'Customer'; $parameters['query'] = 'select * from Customer'; return new ListList($this->execute('query',$parameters),$key); } /** * Find an invoice by its Document Number * * @param string $id * @param array $parameters * @return Invoice * @throws InvalidQueryResultException */ public function getInvoiceQuery(string $id,array $parameters=[]): Invoice { Log::debug(sprintf('%s:Get a specific invoice [%s]',static::LOGKEY,$id)); $parameters['query'] = sprintf("select * from Invoice where DocNumber='%s'",$id); $x = $this->execute('query',$parameters); if ((! $x->QueryResponse) || (! $x->QueryResponse->Invoice) || (count($x->QueryResponse->Invoice) !== 1)) throw new InvalidQueryResultException(sprintf('%s:Query response malformed',self::LOGKEY)); return new Invoice($x->QueryResponse); } /** * Get a list of our invoices * * @param array $parameters * @return ListList * @throws \Exception */ public function getInvoices(array $parameters=[]): ListList { Log::debug(sprintf('%s:Get a list of invoices',static::LOGKEY)); $key = 'Invoice'; $parameters['query'] = 'select * from Invoice'; return new ListList($this->execute('query',$parameters),$key); } /** * Get a list of our items * * @param array $parameters * @return ListList * @throws \Exception */ public function getItems(array $parameters=[]): ListList { Log::debug(sprintf('%s:Get a list of items',static::LOGKEY)); $key = 'Item'; $parameters['query'] = 'select * from Item'; return new ListList($this->execute('query',$parameters),$key); } /** * Get a specific payment record * * @param int $id * @param array $parameters * @return Payment * @throws \Exception */ public function getPayment(int $id,array $parameters=[]): Payment { Log::debug(sprintf('%s:Get a specific payment [%d]',static::LOGKEY,$id)); $x = $this->execute('payment/'.$id,$parameters); if (! $x->Payment) throw new InvalidQueryResultException(sprintf('%s:Query response malformed',self::LOGKEY)); return new Payment((array)$x->Payment); } /** * Get a list of our payments * * @param array $parameters * @return ListList * @throws \Exception */ public function getPayments(array $parameters=[]): ListList { Log::debug(sprintf('%s:Get a list of payments',static::LOGKEY)); $key = 'Payment'; $parameters['query'] = 'select * from Payment'; return new ListList($this->execute('query',$parameters),$key); } /** * Get a list of our invoices * * @param array $parameters * @return ListList * @throws \Exception */ public function getTaxCodes(array $parameters=[]): ListList { Log::debug(sprintf('%s:Get a list of taxes',static::LOGKEY)); $key = 'TaxCode'; $parameters['query'] = 'select * from Taxcode'; return new ListList($this->execute('query',$parameters),$key); } /** * Setup the API call * * @param string $url * @param array $parameters * @param array $headers * @return \CurlHandle */ private function prepareRequest(string $url,array $parameters=[],array $headers=[]): \CurlHandle { $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; } 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; } /** * Create/Update an invoice * * @param array $parameters * @return Invoice * @throws \Exception */ public function updateInvoice(array $parameters=[]): Invoice { Log::debug(sprintf('%s:Update invoice',static::LOGKEY),['params'=>$parameters]); return new Invoice($this->execute('invoice',array_merge($parameters,['method'=>'POST']))); } /** * Create/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']))); } }