consilience / xero-api-client
适用于Xero的PSR-18 API客户端
Requires
- php: ^7.1
- php-http/message: ~1.7
Suggests
- http-interop/http-factory-diactoros: Diactoros PSR-17 HTTP factories
- http-interop/http-factory-discovery: PSR-17 factory discovery
- http-interop/http-factory-guzzle: Guzzle PSR-17 HTTP factories
- php-http/guzzle6-adapter: PSR-18 client implementation
Provides
This package is auto-updated.
Last update: 2024-08-29 06:12:48 UTC
README
目录
Xero认证访问的API包。利用PSR-7、PSR-17和PSR-18。
处理合作伙伴应用的OAuth 1.0a请求和令牌续订。它通过提供一个装饰过的PSR-18客户端来实现这一点。
提供支持以捕获OAuth 1.0a令牌的认证流程。
特性包括
- 目前仅支持合作伙伴应用。(注意:正在添加私有应用的支持,并将很快进行整理。
- 根据本地令牌年龄或远程Xero API标记的过期标志自动续订令牌。
- 在令牌续订时,提供一个应用程序的钩子以持久化OAuth1令牌凭据。这样可以减轻应用程序的续订负担。
简单用法 - 合作伙伴应用
令牌持久性
Xero合作伙伴令牌每30分钟过期,需要续订。续订的令牌然后必须保存以用于下一个API调用。以下示例中的持久化将调用假想的类TokenStorage
。它将具有以下方法
TokenStorage::get($tokenKey): string;
TokenStorage::save($tokenKey, string $tokenDetails);
令牌详细信息将被编码为JSON字符串的数组。
$token键仅用于标识存储中的令牌。我们将仅使用123
作为我们的键。
授权应用
此阶段是为了允许用户授权应用程序访问其Xero组织。结果将是一组令牌凭据(访问令牌),可用于访问API。
您可以使用其他包来获取授权,例如Guzzle和Invoiced/oauth1-xero。或者使用此包来减少依赖关系,以最适合您需求的任何方式。
在OAuth 1.0a流程中有几个步骤来获取令牌
- 获取临时令牌。
- 将用户发送到Xero进行授权。
- 用户返回并带有验证密钥(CSRF令牌)。
- 使用临时令牌和验证密钥交换为长期访问令牌。
以下是详细信息。对于每个阶段,您都需要授权客户端
use Consilience\XeroApi\Client\Oauth1\Authorise; // The public certificate for your RSA-SHA1 public/private key pair will // be registered with the Partner application on the gateway. // See: https://developer.xero.com/documentation/auth-and-limits/partner-applications $authoriseClient = new Authorise([ 'consumer_key' => 'S8IVZHU6...HUABRRK', 'consumer_secret' => 'PLWK9PBG...VHXAQOH', 'callback_uri' => 'your callback URL where the user will return to', 'redirect_on_error' => true, 'signature_method' => Authorise::SIGNATURE_METHOD_RSA, 'private_key_file' => 'certs/privatekey.pem', 'private_key_passphrase' => '', ]);
首先从Xero获取临时令牌
$temporaryToken = $authoriseClient->getTemporaryToken(); if ($temporaryToken->isError()) { throw new Exception(sprintf( 'Failed to get temporary token; error %s (%s)', $temporaryToken->getErrorCode(), $temporaryToken->getErrorReason() )); } // Store the token object in the session for later. // JSON serialise it; we will rebuild it. Session::set('temporary_token', json_encode($temporaryToken));
然后使用临时令牌将用户重定向到Xero
// Your framework will probably have its own way to do a redirect // that allows it to exit cleanly. $authoriseUrl = $authoriseClient->authoriseUrl($temporaryToken); header('Location: ' . (string)$authoriseUrl); exit;
用户将带有验证器(验证令牌)返回到回调URL
// The verifier will be supplied as a GET parameter. $oauthVerifier = $_GET['oauth_verifier']; // Retrieve (and rebuild) the temporary token object we saved earlier. $temporaryToken = new Token(json_decode(Session::get('temporary_token'), true)); // Use these details to get the final Access Token. $accessToken = $authoriseClient->getAccessToken( $temporaryToken, $oauthVerifier );
现在可以存储访问令牌以用于访问Xero API。我们将将其与令牌键123
关联,以便以后可以获取它。
$tokenKey = 123; TokenStorage::save($tokenKey, json_encode($accessToken));
工厂
Authorise
客户端需要一些额外的对象才能运行
- PSR-17 HTTP工厂(用于生成请求和URI)。
- 它装饰的PSR-18客户端。
这些可以从Guzzle安装
composer require http-interop/http-factory-guzzle
$authoriseClient->withUriFactory(new \Http\Factory\Guzzle\UriFactory); $authoriseClient->withRequestFactory(new \Http\Factory\Guzzle\RequestFactory); $authoriseClient->withClient(new \Http\Adapter\Guzzle6\Client);
或使用diactoros
composer require http-interop/http-factory-diactoros
$authoriseClient->withUriFactory(new \Http\Factory\Diactoros\UriFactory); $authoriseClient->withRequestFactory(new \Http\Factory\Diactoros\RequestFactory);
可以使用任何其他PSR-17 HTTP URI和请求工厂以及PSR-18客户端。
或者,您可以使用自动发现并让Authorise
客户端发现已安装的工厂并为自身创建客户端。
composer require http-interop/http-factory-discovery
composer require php-http/guzzle6-adapter
访问Xero API
创建OAuth1令牌对象
要访问API,首先创建一个OAuth 1.0a令牌对象。
对于合作伙伴应用,这将成为一个可更新的可续订令牌,每次续订时都会在存储中更新。
use Consilience\XeroApi\Client\Oauth1\Token; use Consilience\XeroApi\Client\OauthTokenInterface; // Get the current token details from storage. $tokenKey = 123; $accessTokenData = TokenStorage::get($tokenKey); $accessToken = new Token(json_decode($accessTokenData, true)); // Add a callback to persist any refresh to the token. $onPersist = function (OauthTokenInterface $accessToken) use ($tokenKey) { TokenStorage::save($tokenKey, json_encode($accessToken)); }; // Tell the client how to persist token refreshes. $oauth1Token = $oauth1Token->withOnPersist($onPersist); // We will add a guard time of five minutes. // The token will be renewed five minutes early each cycle just to // cut down on the round-trip API accesses. $oauth1Token = $oauth1Token->withGuardTimeSeconds(60 * 5);
对于私有应用,oauth令牌要简单得多。将令牌设置为设置私有应用时给出的消费者密钥。
// Example Private Application consumer key. $oauth1Token = new Token ([ 'oauth_token' => 'PQ4351VSH4FHXTJTPN3JBBBNYSAYXM', ]);
配置HTTP客户端
现在我们设置一个合作伙伴或私有应用客户端。
use Consilience\XeroApi\Client\AbstractClient; use Consilience\XeroApi\Client\App\Partner; use Consilience\XeroApi\Client\App\AppPrivate; // This will create a PSR-18 client decorator. // Just use it like a PSR-18 client. // $client can be a Psr\Http\Client\ClientInterface PSR-18 client // of your choice, or `null` for auto-discovery. $app = new AppPrivate($client, $oauth1Token, [ // or $app = new Partner($client, $oauth1Token, [ // The key and secret are needed for signing. 'consumer_key' => 'PQ4351VSH4FHXTJTPN3JBBBNYSAYXM', 'consumer_secret' => '1FWE9NCU8SYB8S9ROFDTCUDCC3UXMF', // RSA is required for Xero. 'signature_method' => AbstractClient::SIGNATURE_METHOD_RSA, // Key file. // Xero will already have the public part of your key. 'private_key_file' => 'certs/privatekey.pem', 'private_key_passphrase' => '', ]);
应用应提供,用作用户代理。这有助于Xero在查看日志时。
$app = $app->withApplicationName('My Ace Application');
为了支持作为底层PSR-18客户端的Guzzle,以及通过自动发现底层PSR-17消息工厂,您需要通过Composer安装适配器
- guzzlehttp/psr7
- guzzlehttp/guzzle
- php-http/guzzle6-adapter
- http-interop/http-factory-guzzle [这是为了php-http/guzzle6-adapter适配器能正常工作,不知道为什么]
- http-interop/http-factory-discovery [稍后]
否则,在实例化时可以传递消息工厂和客户端。
现在我们可以对API进行请求。任何请求都可以 - GET、POST、PUT以及到任何您的应用程序支持的所有Xero端点。
use Http\Discovery\MessageFactoryDiscovery; // The request factory is just an example. // If you have a concrete request class, then just use that. // We are fetching the organisation from the 2.0 Accounting // API and requesting a JSON response. $messageFactory = MessageFactoryDiscovery::find(); // This is a very simple request, with no parameters and no payload. // Builing more complex requests is a job for another package, and // that will be auto-generated from the Xero OpenAPI specs. $request = $messageFactory->createRequest( 'GET', 'https://api.xero.com/api.xro/2.0/organisation' )->withHeader('Accept', 'application/json'); $response = $app->sendRequest($request); $payloadData = json_decode((string)$response->getBody(), true); var_dump($payloadData);
array(5) { ["Id"]=> string(36) "2f7b676d-2b01-4699-9148-f660b8331671" ["Status"]=> string(2) "OK" ["ProviderName"]=> string(25) "Acme Payments" ["DateTimeUTC"]=> string(21) "/Date(1553978254788)/" ["Organisations"]=> array(1) { [0]=> array(30) { ["APIKey"]=> string(30) "STVXGL8FXHG6VIYX1BVKBRGRICMF08" ["Name"]=> string(24) "Acme Payments Company" ["LegalName"]=> string(24) "Acme Payments Company" ["PaysTax"]=> bool(true) ["Version"]=> string(2) "UK" ["OrganisationType"]=> string(7) "COMPANY" ["BaseCurrency"]=> string(3) "GBP" ["CountryCode"]=> string(2) "GB" ...snip... ["Phones"]=> array(0) { } ["ExternalLinks"]=> array(0) { } ["PaymentTerms"]=> array(0) { } } } }
就这些。使用正确的方法、URL、Accept
头和负载(如果使用POST
)后,您可以发送请求到Xero API的所有部分。令牌续订 - 至少对于合作伙伴应用 - 将自动且对应用程序不可见地为您处理。
另一个包将处理负载解析和请求构建。这个包只关注使用OAuth 1.0a凭据的HTTP访问。Xero API操作包正在开发中(目前可用)在这里:https://github.com/consilience/xero-api-sdk
待办事项
- 测试(如通常一样)。
- 是否有更好的方式来处理密钥文件,可能作为流,这样就可以作为路径、字符串、文件资源等提供?
- 一些更好的异常处理,以便我们可以捕获失败的令牌、删除的授权、一般网络错误等,并适当处理。