Exact PHP Client
repository·main·Indexed 41 days ago
https://github.com/picqer/exact-php-clientA PHP client library for the Exact Online API that enables integration for managing invoices, journal entries, and products. It provides a Connection class for OAuth2 authentication, token exchange, and HTTP requests, while featuring automatic handling of API rate limits, PHP Generators for large datasets, and support for Guzzle 6 and 7 (starting from version 3).
What's inside Exact PHP Client
- The current entity models provided by the library may not contain every available property from the Exact API. If you encounter a property that is missing from the existing entities, you can extend them or submit a Pull Request to the repository to include them.
Handle API rate limits
mainExact Online enforces minutely and daily rate limits. If you exceed these, the library throws an
ApiExceptionwith code429.Monitoring Limits
You can inspect current limits using the following
Connectionmethods:$connection->getDailyLimit(): Total daily calls allowed.$connection->getDailyLimitRemaining(): Remaining calls for today.$connection->getDailyLimitReset(): Timestamp when the daily limit resets.$connection->getMinutelyLimit(): Calls allowed per minute.$connection->getMinutelyLimitRemaining(): Remaining calls for this minute.$connection->getMinutelyLimitReset(): Timestamp when the minutely limit resets.
Automatic Waiting
You can instruct the client to automatically pause execution until the minutely limit resets by calling:
$connection->setWaitOnMinutelyRateLimitHit(true);Install the Exact PHP Client via Composer
mainTo install the library, use Composer to add
picqer/exact-php-clientto your project dependencies.composer require picqer/exact-php-clientManage large datasets with PHP Generators
mainTo prevent memory exhaustion when retrieving large sets of data, use the generator methods. These allow you to iterate over results one by one instead of loading the entire collection into memory.
Use
getAsGenerator()for a full list orfilterAsGenerator($filter)to iterate over a filtered subset.$item = new \Picqer\Financials\Exact\Item($connection); $item->getAsGenerator(); $item->filterAsGenerator('IsWebshopItem eq 1');Authorize the integration with Exact Online
mainBefore you can make API calls, you must authorize your application. This involves redirecting the user to Exact Online to log in and grant permissions. You will need your
Client ID,Client Secret, and theCallback URLconfigured in the Exact App Center.Use the
redirectForAuthorization()method on aConnectioninstance to initiate the oAuth dance.$connection = new \Picqer\Financials\Exact\Connection(); $connection->setRedirectUrl('CALLBACK_URL'); // Same as entered online in the App Center $connection->setExactClientId('CLIENT_ID'); $connection->setExactClientSecret('CLIENT_SECRET'); $connection->redirectForAuthorization();Complete the connection setup and token exchange
mainAfter authorization, Exact redirects the user back to your
Callback URLwith anauthorizationcodeparameter. You must exchange this code for anaccesstokenand arefreshtokenusing theconnect()method.To maintain the connection without re-authorizing the user every time, you should store the
accesstoken,refreshtoken, and the token's expiration timestamp in your database. On subsequent requests, populate these values into theConnectionobject before callingconnect().To prevent multiple simultaneous requests from trying to refresh the token at the same time, you can optionally set lock and unlock callbacks using
setAcquireAccessTokenLockCallback()andsetAcquireAccessTokenUnlockCallback().$connection = new \Picqer\Financials\Exact\Connection(); $connection->setRedirectUrl('CALLBACK_URL'); $connection->setExactClientId('CLIENT_ID'); $connection->setExactClientSecret('CLIENT_SECRET'); if (getValue('authorizationcode')) { $connection->setAuthorizationCode(getValue('authorizationcode')); } if (getValue('accesstoken')) { $connection->setAccessToken(unserialize(getValue('accesstoken'))); } if (getValue('refreshtoken')) { $connection->setRefreshToken(getValue('refreshtoken')); } if (getValue('expires_in')) { $connection->setTokenExpires(getValue('expires_in')); } try { $connection->connect(); } catch (\Exception $e) { throw new Exception('Could not connect to Exact: ' . $e->getMessage()); } // Save tokens for next time setValue('accesstoken', serialize($connection->getAccessToken())); setValue('refreshtoken', $connection->getRefreshToken()); setValue('expires_in', $connection->getTokenExpires()); // Prevent race conditions during token refresh $connection->setAcquireAccessTokenLockCallback('CALLBACK_FUNCTION'); $connection->setAcquireAccessTokenUnlockCallback('CALLBACK_FUNCTION');Handle OAuth2 authorization flow
mainIf your application does not yet have an access token or refresh token, you must redirect the user to Exact Online to authorize your app.
- Use
getAuthUrl()to generate the URL for the authorization request. - Use
redirectForAuthorization()to immediately trigger a PHP header redirect to that URL. - Optionally use
setState(string $state)to pass a CSRF token to the authorization URL, which will be returned in the callback. - Use
setForceLogin(bool $forceLogin)to force the user to enter their credentials even if they are already logged into Exact Online.
// Generate the URL to redirect the user to $authUrl = $connection->getAuthUrl(); // Or redirect immediately $connection->redirectForAuthorization();- Use
Troubleshoot 'Error 400: Please add a $select or a $top=1' error
mainIf you encounter an
ApiExceptionstating that a$selector$top=1statement is required, it is because the Exact API requires explicit field selection or result limiting to prevent over-fetching.To resolve this, pass the selection or top parameter to the
filter()method.Example: Selecting specific fields
$test = new \Picqer\Financials\Exact\GeneralJournalEntry($connection); var_dump($test->filter('', '', 'EntryID, FinancialYear'));Example: Limiting to one result ($top=1)
$test = new \Picqer\Financials\Exact\GeneralJournalEntry($connection); var_dump($test->filter('', '', '', ['$top'=> 1]));Manage Webhook subscriptions and authentication
mainWebhook subscriptions are managed via the
WebhookSubscriptionentity.To authenticate incoming webhook calls, use the
Authenticatabletrait. Pass the full JSON request body and your Exact-supplied Webhook secret to theauthenticatemethod; it returns a boolean indicating if the request is valid.Configure the base URL for different Exact countries
mainBy default, the client connects to the Netherlands (NL) instance. To connect to other Exact regions (e.g., Germany), use
setBaseUrl()with the appropriate URL from the Exact developers guide.$connection = new \Picqer\Financials\Exact\Connection(); $connection->setRedirectUrl('CALLBACK_URL'); $connection->setExactClientId('CLIENT_ID'); $connection->setExactClientSecret('CLIENT_SECRET'); $connection->setBaseUrl('https://start.exactonline.de');Check Guzzle compatibility
mainTheexact-php-clientsupports Guzzle 6 and 7 starting from version 3. If your project requires Guzzle 3, you must use version 1 of this client.BulkGLClassification Data Model
mainThe
BulkGLClassificationclass represents Bulk General Ledger (GL) Classification resources in Exact Online. It is a storable model that can be used to retrieve, create, or update GL classification elements.Key properties include:
ID: The primary key.Code: A unique identifier code.Name: A unique name within the namespace.Division: The division ID. This is mandatory for division-specific taxonomies and empty for general taxonomies.Type: The type of the element.Balance: Used for amount concepts; can be'debit'or'credit'.PeriodType: Used for item concepts; can be'instant'(for balance types) or'duration'(for P&L types).Parent: The parent element (used for reporting schemes).Description: Description of the element (used for division-specific taxonomies).
This model uses the
Query\FindableandPersistance\Storabletraits, allowing it to be queried and persisted via the API endpointbulk/Financial/GLClassifications.