allangallop/laravel-xero-oauth2-multi-tenant 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

allangallop/laravel-xero-oauth2-multi-tenant

Composer 安装命令:

composer require allangallop/laravel-xero-oauth2-multi-tenant

包简介

A fork of webfox/laravel-xero-oauth2 that permits mutli-tenant usage

README 文档

README

Latest Version on Packagist Total Downloads

This package integrates the new recommended package of xeroapi/xero-php-oauth2 using the Oauth 2.0 spec with Laravel.

Installation

You can install this package via composer using the following command:

composer require allangallop/laravel-xero-oauth2-multi-tenant

The package will automatically register itself.

You should add your Xero keys to your .env file using the following keys:

XERO_CLIENT_ID=
XERO_CLIENT_SECRET=

(on Xero developer portal): IMPORTANT When setting up the application in Xero ensure your redirect url is:

https://{your-domain}/xero/auth/callback

(The flow is xero/auth/callback performs the oAuth handshake and stores your token, then redirects you over to your success callback)

You can publish the configuration file with:

php artisan vendor:publish --provider="Webfox\Xero\XeroServiceProvider" --tag="config"

Scopes

You'll want to set the scopes required for your application in the config file.

The default set of scopes are openid, email, profile, offline_access, and accounting.settings. You can see all available scopes on the official Xero documentation.

Using the Package

This package registers two bindings into the service container you'll be interested in:

  • \XeroAPI\XeroPHP\Api\AccountingApi::class this is the main api for Xero - see the xeroapi/xero-php-oauth2 docs for usage. When you first resolve this dependency if the stored credentials are expired it will automatically refresh the token.
  • Webfox\Xero\OauthCredentialManager this is the credential manager - The Accounting API requires we pass through a tenant ID on each request, this class is how you'd access that. This is also where we can get information about the authenticating user. See below for an example.

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');

Multi-Tenant

For multi-tenant scenerios you can call the method getTenants() from the Webfox\Xero\OauthCredentialManager which will return an array of currently connected organisations

$xeroCredentials->getTenants();
/** 
* Array
* (
*    [0] => Array
*    (
*    [Id] =>c5929985-9c2b-4ec9-84ee-406be3596249
*    [Name] => Example Company A
*    )
*    [1] => Array
*    (
*    [Id] => 939d5c21-7069-4010-b6f1-7520172f214c
*    [Name] => Example Company B
*    )
* )
 **/

Likewise getTenantId() has been extended to accept the index key as a parameter (defaults to 0 if left empty)

$xeroCredentials->getTenantId();
// Returns c5929985-9c2b-4ec9-84ee-406be3596249

$xeroCredentials->getTenantId(0);
// Returns c5929985-9c2b-4ec9-84ee-406be3596249

$xeroCredentials->getTenantId(1);
// Returns 939d5c21-7069-4010-b6f1-7520172f214c

These provide the means to implement your own logic for multi-tenant organisations, for example:

...

    /**
     * Retrieve all contacts for given tenant
     * @access public
     * @param  integer $tenant
     * @throws Exception Credentials invalid
     * @return Array Contacts
     **/
    public function getContacts(int $tenant): array
    {
        if ($xeroCredentials->exists()) {
            $xero = resolve(\XeroAPI\XeroPHP\Api\AccountingApi::class);    
            return = $xero->getContact($xeroCredentials->getTenantId($tenant));
        }else{
            throw new \Exception("Credentials are invalid");
        }
    }
...

Credential Storage

Credentials are stored in a JSON file using the default disk on the Laravel Filesystem, with visibility set to private. This allows credential sharing across multiple servers using a shared disk such as S3, regardless of which server conducted the OAuth flow.

To use a different disk, change the xero.credential_disk config item to another disk defined in config/filesystem.php.

You can switch out the credential store (e.g. for your own UserStore if you wanted to store the credentials against your user) in one of two ways:

  1. If it's a simple store and Laravel can automatically resolve your bindings, simply change the xero.credential_store config key to point to your new implementation.
  2. If it requires more advanced logic (e.g. using the current user to retrieve the credentials) then you can rebind this in your AppServiceProvider or a Middleware e.g.
$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
    );
});

An example UserStorageProvider can been found here

Using Webhooks

On your application in the Xero developer portal create a webhook to get your webhook key.

You can then add this to your .env file as

XERO_WEBHOOK_KEY=...

You can then setup a controller to handle your webhook and inject \Webfox\Xero\Webhook e.g.

<?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)
    {
    }

}

Example calls

This package is simply a bridge so you don't have to deal with the Oauth2 gymnastics in Laravel.

Once you've have an instance of \XeroAPI\XeroPHP\Api\AccountingApi::class you're dealing directly with Xero's api library.

The XeroAPI PHP Oauth2 App repository has this list of examples of implementing calls to the API: e.g. invoice creation etc.

https://github.com/XeroAPI/xero-php-oauth2-app/blob/master/example.php

License

The MIT License (MIT). Please see License File for more information.

allangallop/laravel-xero-oauth2-multi-tenant 适用场景与选型建议

allangallop/laravel-xero-oauth2-multi-tenant 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 6 次下载、GitHub Stars 达 0, 最近一次更新时间为 2022 年 06 月 09 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「oauth2」 「laravel」 「xero」 「webfox」 「laravel-xero-oauth2」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 allangallop/laravel-xero-oauth2-multi-tenant 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 allangallop/laravel-xero-oauth2-multi-tenant 我们能提供哪些服务?
定制开发 / 二次开发

基于 allangallop/laravel-xero-oauth2-multi-tenant 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 6
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 2
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 37
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2022-06-09