picqer / bol-retailer-php-client
Bol.com 零售商 API 客户端
Requires
- php: ^8.1
- ext-json: *
- guzzlehttp/guzzle: ^6.3|^7.0
Requires (Dev)
- phpstan/phpstan: ^0.12
- phpunit/phpunit: ^7|^8
- squizlabs/php_codesniffer: ^3.0
- vimeo/psalm: ^3.5
README
这是一个 Bol.com 零售商 API 第 10.6 版本的 PHP 开源客户端。
安装
该项目可以通过 Composer 轻松安装
composer require picqer/bol-retailer-php-client "^10"
用法
创建客户端实例并使用 客户端凭据流程 进行认证
$client = new \Picqer\BolRetailerV10\Client(); $client->authenticateByClientCredentials('your-client-id', 'your-client-secret');
然后您可以通过在客户端上调用 getOrders() 方法来获取开放订单的第一页
$reducedOrders = $client->getOrders(); foreach ($reducedOrders as $reducedOrder) { echo 'hello, I am order ' . $reducedOrder->orderId . PHP_EOL; }
为了节省对 Bol.com 的请求,您可以重复使用访问令牌
$accessToken = ... // your implementation of getting the access token from the storage $client = new \Picqer\BolRetailerV10\Client(); $client->setAccessToken($accessToken); $client->setAccessTokenExpiredCallback(function(\Picqer\BolRetailerV10\Client $client) { // Called at the beginning of a request to the Retailer API when the access token was expired (or // non-existent) and after a request that resulted in an error about an expired access token. // Authenticate and fetch the new access token $client->authenticateByClientCredentials('{your-client-id}', '{your-client-secret}'); $accessToken = $client->getAccessToken(); ... // store $accessToken for future use });
代码流认证
当使用 代码流 进行认证时,在您的回调 URI 上接收和验证短代码后,您需要检索第一个访问令牌和刷新令牌
$client = new \Picqer\BolRetailerV10\Client(); $refreshToken = $client->authenticateByAuthorizationCode( '{your-client-id}', '{your-client-secret}', '{received-shortcode}', '{callback-uri}' ); $accessToken = $client->getAccessToken(); ... // store $accessToken and $refreshToken for future use $orders = $client->getOrders();
访问令牌需要用于对零售商 API 发出请求。
$client = new \Picqer\BolRetailerV10\Client(); $accessToken = ... // your implementation of getting the access token from the storage $client->setAccessToken($accessToken); $orders = $client->getOrders();
访问令牌在有限的时间内有效(编写时为 600 秒),因此需要定期使用刷新令牌进行刷新
$client = new \Picqer\BolRetailerV10\Client(); $accessToken = ... // your implementation of getting the access token from the storage $client->setAccessToken($accessToken); $client->setAccessTokenExpiredCallback(function(\Picqer\BolRetailerV10\Client $client) { // Called at the beginning of a request to the Retailer API when the access token was expired or // non-existent and after a request that resulted in an error about an expired access token. // This callback can attempt to refresh the access token. If after this callback the Client has // a valid access token, the request will continue or retried once. Otherwise, it will be // aborted with an Exception. $refreshToken = ... // your implementation of getting the refresh token from the storage $client->authenticateByRefreshToken('{your-client-id}', '{your-client-secret}', $refreshToken); $accessToken = $client->getAccessToken(); ... // store $accessToken for future use }); $orders = $client->getOrders();
上述示例假设您的 Bol.com 集成账户使用的是在每次使用后不会更改的刷新令牌(Bol.com 命名为“方法 1”)。
如果您的刷新令牌在每次使用后都会更改(“方法 2”),那么您需要在刷新后存储新的刷新令牌。在这种情况下,刷新令牌只能使用一次。当多个进程同时刷新时,由于竞争条件,可能会存储最后一个已使用的刷新令牌。这意味着从那时起就无法刷新,用户需要手动重新登录。为了防止这种情况,您需要使用锁,以保证只存储和使用最新的刷新令牌。下面的示例使用了一个阻塞互斥锁。
$client = new \Picqer\BolRetailerV10\Client(); $accessToken = ... // your implementation of getting the access token from the storage $client->setAccessToken($accessToken); $client->setAccessTokenExpiredCallback(function(\Picqer\BolRetailerV10\Client $client) use ($mutex) { // Called at the beginning of a request to the Retailer API when the access token was expired or // non-existent and after a request that resulted in an error about an expired access token. // Ensure only 1 process can be in the critical section, others are blocked and one is let in // when that process leaves the critical section $mutex->withLock(function () use ($client) { // your implementation of getting the latest access token from the storage (it might be // refreshed by another process) $accessToken = ... if (! $accessToken->isExpired()) { // No need to refresh the token, as it was already refreshed by another proces. Make sure the // client uses it. $client->setAccessToken($accessToken); return; } $refreshToken = ... // your implementation of getting the refresh token from the storage $newRefreshToken = $client->authenticateByRefreshToken( '{your-client-id}', '{your-client-secret}', $refreshToken ); $accessToken = $client->getAccessToken(); ... // store $accessToken and $newRefreshToken for future use } }); $orders = $client->getOrders();
异常
客户端上的方法可能会抛出异常。所有异常都有父类 Picqer\BolRetailerV10\Exception\Exception
- 当连接出现问题时(例如,API 服务器关闭或网络问题),会抛出
ConnectException
。您可以稍后重试。 - 当服务器出现问题时(例如,500 内部服务器错误),会抛出
ServerException
(扩展ConnectException
)。您可以稍后重试。 - 当收到的响应无法处理时(例如,格式不正确或类型不预期),会抛出
ResponseException
。重试不会有所帮助,需要进行调查。 - 当服务器响应 400 未授权(例如,凭据无效)时,会抛出
UnauthorizedException
。 - 当 API 用户的节流限制达到时,会抛出
RateLimitException
。 - 当 HTTP 库中发生错误,且上述情况未涵盖时,会抛出
Exception
。我们旨在尽可能地将错误映射到ConnectionException
或ResponseException
。
贡献
如果您想贡献,请遵循以下指南。
- 添加您想要贡献的版本的最新 API 规范,并生成模型和客户端(见:“生成的模型和客户端”)。
- 有时由于错误或输出意外的代码而导致生成失败。请修复生成器类,不要手动修改生成的类。
- 如果生成器由于Bol.com API规范中的某个问题而需要更改,请将此情况添加到本README的“已知问题”部分。如果当前已知的这些问题仍然相关,请检查一下。
- 如果您贡献了一个新的大版本,任何对'v10'的引用都必须替换为新的版本
- 重命名
/src
、/tests
和composer.json
中的命名空间。 - 在测试用例和
BaseClient
中将'v10'替换为新的版本。 - 更新本README,包含指向新的迁移指南的链接,并将'v10'替换为新的版本。
- 重命名
生成的模型和客户端
客户端和所有模型都是由提供的零售商API规范(src/OpenApi/retailer.json
)和共享API规范(src/OpenApi/shared.json
)生成的。这些规范是合并的。生成代码确保没有错别字,不是每个操作都需要测试,并且可以轻松地应用规范的未来(小)更新。
生成的类包含所有将方法参数和响应数据正确映射到模型所需的数据:规范仅用于生成它们。
要为最新的Bol零售商API版本构建类,让代码使用此脚本下载最新的规范
composer run-script download-specs
客户端
客户端包含规范中指定的所有操作。'operationId'的值转换为camelCase,并用作每个操作的方法名。
规范为每个请求和响应定义了类型(如果需要发送数据)。如果此类模型封装了一个字段,那么它会从操作中抽象出来,以获得更流畅的开发体验
- 客户端中的方法接受该字段作为参数,而不是模型
- 客户端中的方法返回该模型中的字段,而不是模型本身
要生成客户端,可以使用以下composer脚本
composer run-script generate-client
模型
模型的类名等于规范中'definitions'数组的关键字。
要生成模型,可以使用以下composer脚本
composer run-script generate-models
已知问题
- 规范中的一些类型定义是句子,例如“用于取消的订单项的容器。”这些被转换为camelCase,并移除点。
- 规范中的一些操作(get-offer-export和get-unpublished-offer-report)没有指定响应模式(类型),尽管有响应。目前,这仅适用于返回CSV的操作。
- 规范中定义了一个名为'Return'的类型。由于这是PHP中的保留关键字,因此不能用作模型(在PHP <= 7)的类名,因此目前将其替换为'ReturnObject'。
- 如果响应中的数组字段为空,则该字段有时会被省略。例如,getOrders的原始JSON响应为
您可能期望的是{ }
{ "orders": [ ] }
- 操作'get-invoices'指定响应为字符串,而JSON或XML中明显返回了一些数据模型。
- 操作'get-invoices'的描述中包含一个标记为'ENSP'的奇怪空格。