webfox / laravel-xero-oauth2
Laravel 集成 Xero,使用 OAuth 2.0 规范
Requires
- php: ^7.3|^8.0|^8.1
- laravel/framework: ^6.0|^7.0|^8.0|^9.0|^10.0|^11.0
- xeroapi/xero-php-oauth2: ^2.0|^3.0|^4.0|^5.0|^6.0|^7.0
Requires (Dev)
- phpunit/phpunit: ^8.2|^10.5
- symfony/var-dumper: ^4.3|^7.0
README
本软件包通过 OAuth 2.0 规范与 Laravel 集成 xeroapi/xero-php-oauth2 的推荐包。
安装
您可以使用以下命令通过 composer 安装此软件包
composer require webfox/laravel-xero-oauth2
该软件包将自动注册自身。
您应该使用以下密钥将您的 Xero 密钥添加到 .env
文件中
XERO_CLIENT_ID=
XERO_CLIENT_SECRET=
(在 Xero 开发者门户): 重要 在 Xero 中设置应用程序时,请确保您的重定向 URL 为
https://{your-domain}/xero/auth/callback
(流程是 xero/auth/callback 执行 OAuth 握手并存储您的令牌,然后重定向您到您的成功回调)
您可以使用以下命令发布配置文件
php artisan vendor:publish --provider="Webfox\Xero\XeroServiceProvider" --tag="config"
作用域
您需要在配置文件中设置应用程序所需的作用域。
默认的作用域集合包括 openid
、email
、profile
、offline_access
和 accounting.settings
。您可以在 官方 Xero 文档 中查看所有可用的作用域。
使用软件包
该软件包将两个绑定注册到服务容器中,您将对此感兴趣
\XeroAPI\XeroPHP\Api\AccountingApi::class
这是 Xero 的主要 API - 有关使用方法,请参阅 xeroapi/xero-php-oauth2 文档。当您第一次解决此依赖项时,如果存储的凭据已过期,它将自动刷新令牌。Webfox\Xero\OauthCredentialManager
这是凭据管理器 - 账户 API 要求我们在每个请求中传递一个租户 ID,这个类就是您访问它的方法。这也可以获取有关认证用户的详细信息。以下是一个示例。
app\Http\Controllers\XeroController.php
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use Webfox\Xero\OauthCredentialManager; class XeroController extends Controller { public function index(Request $request, OauthCredentialManager $xeroCredentials) { try { // Check if we've got any stored credentials if ($xeroCredentials->exists()) { /* * We have stored credentials so we can resolve the AccountingApi, * If we were sure we already had some stored credentials then we could just resolve this through the controller * But since we use this route for the initial authentication we cannot be sure! */ $xero = resolve(\XeroAPI\XeroPHP\Api\AccountingApi::class); $organisationName = $xero->getOrganisations($xeroCredentials->getTenantId())->getOrganisations()[0]->getName(); $user = $xeroCredentials->getUser(); $username = "{$user['given_name']} {$user['family_name']} ({$user['username']})"; } } catch (\throwable $e) { // This can happen if the credentials have been revoked or there is an error with the organisation (e.g. it's expired) $error = $e->getMessage(); } return view('xero', [ 'connected' => $xeroCredentials->exists(), 'error' => $error ?? null, 'organisationName' => $organisationName ?? null, 'username' => $username ?? null ]); } }
resources\views\xero.blade.php
@extends('_layouts.main')
@section('content')
@if($error)
<h1>Your connection to Xero failed</h1>
<p>{{ $error }}</p>
<a href="{{ route('xero.auth.authorize') }}" class="btn btn-primary btn-large mt-4">
Reconnect to Xero
</a>
@elseif($connected)
<h1>You are connected to Xero</h1>
<p>{{ $organisationName }} via {{ $username }}</p>
<a href="{{ route('xero.auth.authorize') }}" class="btn btn-primary btn-large mt-4">
Reconnect to Xero
</a>
@else
<h1>You are not connected to Xero</h1>
<a href="{{ route('xero.auth.authorize') }}" class="btn btn-primary btn-large mt-4">
Connect to Xero
</a>
@endif
@endsection
routes/web.php
/* * We name this route xero.auth.success as by default the config looks for a route with this name to redirect back to * after authentication has succeeded. The name of this route can be changed in the config file. */ Route::get('/manage/xero', [\App\Http\Controllers\XeroController::class, 'index'])->name('xero.auth.success');
错误处理
如果用户在 Xero 授权页面上拒绝访问,该软件包将从 AuthorizationCallbackController 抛出 OAuthException
。您可以根据自己的喜好捕获并处理此异常。
Laravel 11
要在 Laravel 11 中完成此操作,请在 bootstrap/app.php
中绑定自定义异常渲染器
return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__ . '/../routes/web.php', commands: __DIR__ . '/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { // }) ->withExceptions(function (Exceptions $exceptions) { // Handle when the user clicks cancel on the Xero authorization screen $exceptions->render(function (OAuthException $e, Request $request) { return redirect('/my/xero/connect/page')->with('errorMessage', $e->getMessage()); }); })->create();
Laravel 8-10
在 App\Exceptions\Handler
类中使用 reportable
方法
public function register() { $this->reportable(function (OAuthException $e) { // Handle when the user clicks cancel on the Xero authorization screen return redirect('/my/xero/connect/page')->with('errorMessage', $e->getMessage()); }); }
凭据存储
凭据使用 Laravel 文件系统的默认磁盘存储在 JSON 文件中,可见性设置为私有。这允许使用共享磁盘(如 S3)跨多个服务器共享凭据,而不管哪个服务器执行了 OAuth 流。
要使用不同的磁盘,请将 xero.credential_disk
配置项更改为 config/filesystem.php
中定义的另一个磁盘。
您可以通过两种方式之一切换凭据存储(例如,如果您想将凭据存储在您的用户中,可以使用自己的 UserStore
)
- 如果它是一个简单的存储,并且 Laravel 可以自动解析您的绑定,只需将
xero.credential_store
配置键更改为指向您的新实现即可。 - 如果它需要更复杂的逻辑(例如,使用当前用户检索凭据),则可以在
AppServiceProvider
或中间件中重新绑定,例如
$this->app->bind(OauthCredentialManager::class, function(Application $app) { return new UserStorageProvider( \Auth::user(), // Storage Mechanism $app->make('session.store'), // Used for storing/retrieving oauth 2 "state" for redirects $app->make(\Webfox\Xero\Oauth2Provider::class) // Used for getting redirect url and refreshing token ); });
可以在此处找到示例 UserStorageProvider 示例
使用 Webhooks
在Xero开发者门户中创建一个webhook以获取您的webhook密钥。
然后您可以将它添加到您的.env
文件中,如下所示:
XERO_WEBHOOK_KEY=...
然后您可以设置一个控制器来处理您的webhook并注入\Webfox\Xero\Webhook
,例如:
<?php namespace App\Http\Controllers; use Webfox\Xero\Webhook; use Illuminate\Http\Request; use Illuminate\Http\Response; use XeroApi\XeroPHP\Models\Accounting\Contact; use XeroApi\XeroPHP\Models\Accounting\Invoice; class XeroWebhookController extends Controller { public function __invoke(Request $request, Webhook $webhook) { // The following lines are required for Xero's 'itent to receive' validation if (!$webhook->validate($request->header('x-xero-signature'))) { // We can't use abort here, since Xero expects no response body return response('', Response::HTTP_UNAUTHORIZED); } // A single webhook trigger can contain multiple events, so we must loop over them foreach ($webhook->getEvents() as $event) { if ($event->getEventType() === 'CREATE' && $event->getEventCategory() === 'INVOICE') { $this->invoiceCreated($request, $event->getResource()); } elseif ($event->getEventType() === 'CREATE' && $event->getEventCategory() === 'CONTACT') { $this->contactCreated($request, $event->getResource()); } elseif ($event->getEventType() === 'UPDATE' && $event->getEventCategory() === 'INVOICE') { $this->invoiceUpdated($request, $event->getResource()); } elseif ($event->getEventType() === 'UPDATE' && $event->getEventCategory() === 'CONTACT') { $this->contactUpdated($request, $event->getResource()); } } return response('', Response::HTTP_OK); } protected function invoiceCreated(Request $request, Invoice $invoice) { } protected function contactCreated(Request $request, Contact $contact) { } protected function invoiceUpdated(Request $request, Invoice $invoice) { } protected function contactUpdated(Request $request, Contact $contact) { } }
示例调用
此包仅是一个桥梁,因此您无需在Laravel中处理Oauth2的技巧。
一旦您拥有一个\XeroAPI\XeroPHP\Api\AccountingApi::class实例,您就直接与Xero的API库打交道。
XeroAPI PHP Oauth2 App仓库有实现API调用的示例列表,例如发票创建等。
https://github.com/XeroAPI/xero-php-oauth2-app/blob/master/example.php
许可证
MIT许可证(MIT)。有关更多信息,请参阅许可证文件。