sdekkers / exact-php-client
Exact API 的 PHP 客户端
Requires
- php: >=7.2.0
- guzzlehttp/guzzle: ~6.0 || ^7.0
Requires (Dev)
- phpunit/phpunit: ^4.8.36
- dev-master
- v3.24.0.1
- v3.24.0
- v3.23.0
- v3.22.0
- v3.21.0
- v3.20.0
- v3.19.0
- v3.18.0
- v3.17.0
- v3.16.0
- v3.15.1
- v3.15.0
- v3.14.0
- v3.13.0
- v3.12.1
- v3.12.0
- v3.11.0
- v3.10.0
- v3.9.0
- v3.8.0
- v3.7.2
- v3.7.1
- v3.7.0
- v3.6.0
- v3.5.0
- v3.4.0
- v3.3.0
- v3.2.0
- v3.1.0
- v3.0.0
- v2.15.0
- v2.14.0
- v2.13.0
- v2.12.1
- v2.12.0
- v2.11.0
- v2.10.0
- v2.9.1
- v2.9.0
- v2.8.0
- v2.7.0
- v2.6.1
- v2.6.0
- v2.5.0
- v2.4.0
- v2.3.0
- v2.2.0
- v2.1.3
- v2.1.2
- v2.1.1
- v2.1.0
- v2.0.0
- v1.x-dev
- v1.0.9
- v1.0.8
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
This package is auto-updated.
Last update: 2024-09-25 15:11:21 UTC
README
PHP 客户端库,用于使用 Exact Online API。
注意:对于 Guzzle 6 使用 v3,对于 Guzzle 3 使用 v1。
直接链接到 Exact Online 文档
https://support.exactonline.com/community/s/knowledge-base#All-All-DNO-Content-getting-started
Composer 安装
可以通过 Composer 安装此 PHP Exact 客户端。
composer require picqer/exact-php-client
用法
- 在 Exact App Center 设置应用以获取凭证
- 从您的应用中授权集成
- 解析回调并完成连接设置
- 使用库进行操作
步骤 1-3 仅在设置时需要一次。
在 Exact App Center 设置应用以获取凭证
在 Exact App Center 设置应用以获取您的 客户端 ID
和 客户端密钥
。您还需要设置正确的 回调 URL
以使 OAuth 舞蹈工作。
从您的应用中授权集成
下面的代码是一个示例 authorize()
函数。
$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();
这会将用户重定向到 Exact 以登录并授权您的集成。
解析回调并完成连接设置
Exact 将重定向回您提供的 回调 URL
。回调将接收一个 code
参数。这是 OAuth 的授权代码。存储此代码。
创建一个新的连接到 Exact,以便库可以交换代码并获取 accesstoken
和 refreshtoken
。accesstoken
是一个临时令牌,允许您的应用程序与 Exact 通信。refreshtoken
是一个用于获取新的 accesstoken
的令牌,它也会刷新 refreshtoken
。库将为您处理所有这些。下面的代码可以是一个通用的 connect()
函数,它返回 API 连接。
$connection = new \Picqer\Financials\Exact\Connection(); $connection->setRedirectUrl('CALLBACK_URL'); $connection->setExactClientId('CLIENT_ID'); $connection->setExactClientSecret('CLIENT_SECRET'); if (getValue('authorizationcode')) // Retrieves authorizationcode from database $connection->setAuthorizationCode(getValue('authorizationcode')); if (getValue('accesstoken')) // Retrieves accesstoken from database $connection->setAccessToken(unserialize(getValue('accesstoken'))); if (getValue('refreshtoken')) // Retrieves refreshtoken from database $connection->setRefreshToken(getValue('refreshtoken')); if (getValue('expires_in')) // Retrieves expires timestamp from database $connection->setTokenExpires(getValue('expires_in')); // Make the client connect and exchange tokens try { $connection->connect(); } catch (\Exception $e) { throw new Exception('Could not connect to Exact: ' . $e->getMessage()); } // Save the new tokens for next connections setValue('accesstoken', serialize($connection->getAccessToken())); setValue('refreshtoken', $connection->getRefreshToken()); // Optionally, save the expiry-timestamp. This prevents exchanging valid tokens (ie. saves you some requests) setValue('expires_in', $connection->getTokenExpires()); // Optionally, set the lock and unlock callbacks to prevent multiple request for acquiring a new refresh token with the same refresh token. $connection->setAcquireAccessTokenLockCallback('CALLBACK_FUNCTION'); $connection->setAcquireAccessTokenUnlockCallback('CALLBACK_FUNCTION');
关于部门(行政)
默认情况下,库将使用用户的默认行政。这意味着当用户在 Exact Online 中切换部门时,库也将开始使用此部门。
速率限制
Exact 使用每分钟和每日的速率限制。每天可以对公司进行的调用次数有限,为防止超量,他们还实施了一分钟的限制。这个 PR 将此信息存储在 Connection
中,并添加了读取速率限制的方法,以便您可以按应用程序的适当方式处理这些限制。Exact 关于速率限制的文档可在此处找到:https://support.exactonline.com/community/s/knowledge-base#All-All-DNO-Simulation-gen-apilimits
如果您遇到速率限制,将抛出带有代码 429 的 ApiException
。在那个时刻,您可以确定您是遇到了每分钟限制还是每日限制。如果您遇到每分钟限制,请在一分钟后再次尝试。如果您遇到每日限制,请在每日重置后再次尝试。
您可以使用以下 Connection
上的方法,这些方法将在您的第一次 API 调用后返回限制(基于 Exact 的标题)。
$connection->getDailyLimit(); // Retrieve your daily limit $connection->getDailyLimitRemaining(); // Retrieve the remaining amount of API calls for this day $connection->getDailyLimitReset(); // Retrieve the timestamp for when the limit will reset $connection->getMinutelyLimit(); // Retrieve your limit per minute $connection->getMinutelyLimitRemaining(); // Retrieve the amount of API calls remaining for this minute
使用库进行操作(示例)
// Optionally set administration, otherwise use the current administration of the user $connection->setDivision(123456); // Create a new account $account = new Account($connection); $account->AddressLine1 = $customer['address']; $account->AddressLine2 = $customer['address2']; $account->City = $customer['city']; $account->Code = $customer['customerid']; $account->Country = $customer['country']; $account->IsSales = 'true'; $account->Name = $customer['name']; $account->Postcode = $customer['zipcode']; $account->Status = 'C'; $account->save(); // Add a product in Exact $item = new Item($connection); $item->Code = $productcode; $item->CostPriceStandard = $costprice; $item->Description = $name; $item->IsSalesItem = true; $item->SalesVatCode = 'VH'; $item->save(); // Retrieve an item $item = new Item($connection); $item->find(ID); // List items $item = new Item($connection); $item->get(); // List items with filter (using a filter always returns a collection) $item = new Item($connection); $items = $item->filter("Code eq '$productcode'"); // Uses filters as described in Exact API docs (odata filters) // Create new invoice with invoice lines $invoiceLines[] = [ 'Item' => $itemId, 'Quantity' => $orderproduct['amount'], 'UnitPrice' => $orderproduct['price'] ]; $salesInvoice = new SalesInvoice($connection); $salesInvoice->InvoiceTo = $customer_code; $salesInvoice->OrderedBy = $customer_code; $salesInvoice->YourRef = $orderId; $salesInvoice->SalesInvoiceLines = $invoiceLines; $salesInvoice->save(); // Print and email the invoice $printedInvoice = new PrintedSalesInvoice($connection); $printedInvoice->InvoiceID = $salesInvoice->InvoiceID; $printedInvoice->SendEmailToCustomer = true; $printedInvoice->SenderEmailAddress = "from@example.com"; $printedInvoice->DocumentLayout = "401f3020-35cd-49a2-843a-d904df0c09ff"; $printedInvoice->ExtraText = "Some additional text"; $printedInvoice->save();
连接到荷兰以外的其他 Exact 国家
根据 Exact 开发者指南 选择正确的基本 URL
$connection = new \Picqer\Financials\Exact\Connection(); $connection->setRedirectUrl('CALLBACK_URL'); $connection->setExactClientId('CLIENT_ID'); $connection->setExactClientSecret('CLIENT_SECRET'); $connection->setBaseUrl('https://start.exactonline.de');
检查 src/Picqer/Financials/Exact 以获取所有可用的实体。
Webhooks
通过 WebhookSubscription 实体管理 webhook 订阅。
为了验证传入的 webhook 调用,您可以使用 Authenticatable 特性。将完整的 JSON 请求和 Exact 提供的 Webhook 密钥传递给 authenticate 方法,它将返回 true 或 false。
故障排除
出现错误信息 'Picqer\Financials\Exact\ApiException':'错误 400:请向查询字符串中添加 $select 或 $top=1 语句'。
在特定的实例中,遗憾的是 Exact 的 API 文档中没有说明这一点,但这是一个必需的条件。可能是为了防止请求过载。遇到此错误时,您需要在查询中添加 select 或 top。select 用于提供您想要提取的字段列表,而 $top=1 限制结果只包含一个项目。
示例
仅返回 EntryID 和 FinancialYear。
$test = new GeneralJournalEntry($connection); var_dump($test->filter('', '', 'EntryID, FinancialYear'));
$top=1 的添加方式如下
$test = new GeneralJournalEntry($connection); var_dump($test->filter('', '', '', ['$top'=> 1]));
认证错误
发生错误:未捕获的异常:无法连接到 Exact:客户端错误:POST https://start.exactonline.nl/api/oauth2/token 导致 400 Bad Request 响应:Bad Request 在 /var/www/html/oauth_call_connect.php:61 Stack trace: #0 {main} 抛出在 /var/www/html/oauth_call_connect.php 行 61`
此错误发生是因为您在重定向 URL 中获得的代码只对一次调用有效。当您再次使用 "已使用" 的代码调用认证过程时,您会得到此错误。请确保您只使用 Exact Online 提供的代码一次,以获取访问令牌。
代码示例
例如:请参阅 example/example.php
待办事项
- 当前实体不包含所有可用的属性。如果您需要它们,请自由提交带有添加或扩展实体的 PR。使用 greasemonkey 或 tampermonkey 中的
userscript.js
以一致和完整的方式生成实体。