intuit / QuickBooks-V3-PHP-SDK

Official PHP SDK for QuickBooks REST API v3.0: https://developer.intuit.com/
Apache License 2.0
246 stars 246 forks source link

Error handler returns nothing for 400 errors #521

Open rgasch opened 2 months ago

rgasch commented 2 months ago

We have fundamentally working integration. However, sometimes we get a 400 Error and in this case, the following code

$errorHandler = $this->_connector->getLastError();
CliLog::error("API: Http Status Code = " . $errorHandler->getHttpStatusCode());
CliLog::error("API: Error Message = " . $errorHandler->getIntuitErrorMessage());
CliLog::error("API: Error Element = " . $errorHandler->getIntuitErrorElement());
CliLog::error("API: Error Detail = " . $errorHandler->getIntuitErrorDetail());
CliLog::error("API: Error Code = " . $errorHandler->getIntuitErrorCode());
CliLog::error("API: Error Type = " . $errorHandler->getIntuitErrorType());

All these calls other than the getHttpStatusCode() call return nothing.

Is this a bug or are we using an incorrect method to get the cause of a 400 error?

kodnificent commented 2 months ago

Experiencing this same issue

sashidharg commented 2 months ago

Have the same problem.

This might be because the code expects the quickbooks response header as "application/xml" while the actual response has "application/xml;charset=UTF-8".

The code at IntuitResponse::setFaultHandler() expects "application/xml".

if($this->getResponseContentType() != null && (strcasecmp($this->getResponseContentType(), CoreConstants::CONTENTTYPE_APPLICATIONXML) == 0 || strcasecmp($this->getResponseContentType(), CoreConstants::CONTENTTYPE_TEXTXML) == 0)){ $this->faultHandler->parseResponse($body); }

rgasch commented 2 months ago

OK, I tried to figure out how to fix this, but didn' t get all that far, since for some reason the parsing fails, there's nothing more to work with using the available SDK code. However, I have a workaround:

When you call your API Client function - in this example it's Add() - do this:

return $this->_getConnector()->throwExceptionOnError(true)->Add($entity);

Then in the code that calls this function, switch to try/catch:

try {
    $response = $this->add($object);
} catch (\Exception $e) {
    $this->_printApiError($e);
    return null;
}

Finally, here's the method that parses the exception text using the Laravel Str helper class in a very quick and dirty manner, I spent like 5 minutes writing this:

    private function _printApiError(\Exception $e): int
    {
        $xmlString = Str::between($e->getMessage(), '">', '</IntuitResponse>');
        $xml       = new \SimpleXMLElement($xmlString);

        // Extract error details
        $errorCode    = (int) $xml->Error['code'];
        $errorMessage = (string) $xml->Error->Message;
        $errorDetail  = (string) $xml->Error->Detail;

        // Log the extracted error details
        CliLog::error("API: Http Status Code = " . $errorCode);
        CliLog::error("API: Error Code = " . $errorCode);
        CliLog::error("API: Error Message = " . $errorMessage);
        CliLog::error("API: Error Detail = " . $errorDetail);

        return $errorCode;
    }

Like I said, this is quick and dirty and could certainly be improved, but at least now you're able to see what error you got and work with this info.

kodnificent commented 2 months ago

This worked for me.

$error = $this->data_service->getLastError();

if ($error) {
   $err = $error->getResponseBody();
}

getIntuitErrorMessage() was returning an empty string. With the solution above, I was able to get the error from the response body