globalvisionmedia/oauth2-myob

为PHP League OAuth2-Client提供的MYOB OAuth 2.0客户端提供者

v1.4 2022-09-17 04:00 UTC

This package is auto-updated.

Last update: 2024-09-17 08:38:03 UTC


README

此软件包为PHP League的OAuth 2.0客户端提供MYOB OAuth 2.0支持。

安装

composer require globalvisionmedia/oauth2-myob

获取MYOB访问密钥

  1. 要获取密钥,您需要成为MYOB开发者计划的一部分(https://developer.myob.com/program/become-a-myob-developer-partner/
  2. 获取账户后,登录并点击my.myob.com.au的“开发者”标签
  3. 点击“注册应用”按钮以创建密钥
  4. 重定向API必须与下面的redirectUri完全相同(包括http://或https://),这是您应用程序的URL

用法

用法与The League的OAuth客户端相同,使用\GlobalVisionMedia\OAuth2\MYOBClient\Provider\MYOB作为提供者,但以下不同:

  1. MYOB不仅依赖OAuth2进行登录。OAuth2用于提供API访问权限,但不是访问您的MYOB数据文件。这需要一个二次登录,返回一个“公司URL”。此提供者处理这两个登录,但要求您除了标准的OAuth2凭据外,还提供您的MYOB登录详细信息。(见下面的授权码流)

  2. MYOB的API受到限制 - 文档中规定的限制是每秒8次调用(以及每天大量调用),但限制似乎存在错误,您可能会不幸地发现,无论您设置什么限制,您都会收到API访问限制超出错误。然而,如果您遵循下面的示例应用程序中的指南,并添加一个检测限制、暂停并重试的保险丝,您将能够创建一个相当可靠的应用程序。

实例化

$provider = new \GlobalVisionMedia\OAuth2\MYOBClient\Provider\MYOB([
    'clientId'                => 'yourId',          // The Key assigned to you by MYOB
    'clientSecret'            => 'yourSecret',      // The Secret assigned to you by MYOB
    'redirectUri'             => 'yourRedirectUri'  // The Redirect Uri you specified for your app on MYOB
    'username'                => 'yourUsername',    // The username you use when you log into MYOB
    'password'                => 'yourPassword',    // The password you use to log into MYOB
    'companyName'             => 'yourCompany'      // The name of your company file. This appears in the "Welcome" screen when you log into MYOB
]);

提示(也适用于其他提供者)

When you instantiate your provider, you can also pass a second parameter containing a collaborator for your httpClient.
Doing that means you can define your own Guzzle client and do things such as:

  1. Setting Guzzle into debug mode, or
  2. Adding a rate limiter mildeware (composer require spatie/guzzle-rate-limiter-middleware)
  

use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use Spatie\GuzzleRateLimiterMiddleware\RateLimiterMiddleware;

// Add a rate limiter
$stack=HandlerStack::create();
$stack->push(RateLimiterMiddleware::perSecond($this->getPerSecondRateLimit()));
$options=['debug' => $debug, 'exceptions' => false, 'handler' => $stack];
$httpClient = new Client($options);

$this->provider = \GlobalVisionMedia\OAuth2\MYOBClient\Provider\MYOB([
    'redirectUri'       => CALLBACK_URI,
    'clientId'          => MYOB_CLIENT_ID,
    'clientSecret'      => MYOB_CLIENT_SECRET,
    'username'          => MYOB_USERNAME,
    'password'          => MYOB_PASSWORD,
    'companyName'       => MYOB_COMPANY_NAME
  ],
  ['httpClient'         => $httpClient]);

示例应用程序

<?php
require __DIR__ . '/vendor/autoload.php';

// This is a prebuilt rate limiter for guzzle - unfortunately MYOB does not seem to work as documented any you may need to add additional sleep() calls.

use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use Spatie\GuzzleRateLimiterMiddleware\RateLimiterMiddleware;

define('CALLBACK_URI','https://xxxx.yyyyy.com/myApp.php');       // set to the URL for this script

define('MYOB_CLIENT_ID','xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'); // set to your client ID
define('MYOB_CLIENT_SECRET','xxxxxxxxxxxxxxxxxxxxxxxx');         // set to your secret
define('MYOB_COMPANY_NAME','My Company File Name');              // set to your company database file
define('MYOB_USERNAME','xxxxxx@myemail.com');                    // set to your MYOB login
define('MYOB_PASSWORD','xxxxxxxxxx');                            // set to your MYOB pass

define('CACHEDIR','/tmp/');                                      // a writeable area for storing tokens


class myMYOB {

  public function __construct($debug=false) {
    $this->cache=CACHEDIR.'_API_TOKEN_CACHE_'.md5(__FILE__.get_class($this));

    // Add the rate limiter
    $stack=HandlerStack::create();
    $stack->push(RateLimiterMiddleware::perSecond($this->getPerSecondRateLimit()));
    $options=['debug' => $debug, 'exceptions' => false, 'handler' => $stack];
    $httpClient = new Client($options);

    $this->provider = \GlobalVisionMedia\OAuth2\MYOBClient\Provider\MYOB([
        'redirectUri'       => CALLBACK_URI,
        'clientId'          => MYOB_CLIENT_ID,
        'clientSecret'      => MYOB_CLIENT_SECRET,
        'username'          => MYOB_USERNAME,
        'password'          => MYOB_PASSWORD,
        'companyName'       => MYOB_COMPANY_NAME
      ],
      ['httpClient'         => $httpClient]);

    // First check our cache to see if we have an existing token. This sppeds the application by avoiding the need to re-authenticate.
    if (file_exists($this->cache)) {
      $this->accessToken=unserialize(file_get_contents($this->cache));
      if ($this->accessToken->hasExpired()) {
        $this->accessToken=$this->provider->getAccessToken('refresh_token', ['refresh_token'=>8]);
      }
    } elseif (!isset($_GET['code'])) {
      // If we don't have an authorization code then get one
      $authUrl = $this->provider->getAuthorizationUrl();
      $_SESSION['oauth2state'] = $this->provider->getState();

      header('Location: '.$authUrl);
      exit;

      // Check given state against previously stored one to mitigate CSRF attack
    } elseif (empty($_GET['state']) ||
              (isset($_SESSION['oauth2state']) && $_GET['state'] !== $_SESSION['oauth2state'])) {
      if (isset($_SESSION['oauth2state'])) unset($_SESSION['oauth2state']);
      exit('Invalid state');

      // Try to get an access token using the authorisation code grant.
    } else try {
      $this->accessToken = $this->provider->getAccessToken('authorization_code', [ 'code' => $_GET['code'] ]);

      // Cache the token
      file_put_contents($this->cache,serialize($this->accessToken));

    } catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {
      // Failed to get the access token or user details.
      exit($e->getMessage());
    }
  }

  public function apiCall($method, $url, $pageSize=1000, $filter=null) {
    if (strpos($url,"https://")===false) {  // is this a nextpage link? if so leave url unchanged
      // have we logged into our datafile yet?
      if (!isset($this->companyURL)) {
        $this->companyURL=$this->provider->getCompanyUrl($this->accessToken);
      }
      $url=$this->companyURL.$url.'?'.$filter; // add any OData filters - see https://apisupport.myob.com/hc/en-us/articles/360000496136-OData-filter-Tips-Tricks
    }
    $request=$this->provider->getAuthenticatedRequest($method, $url, $this->accessToken);
    try {
      $response = $this->provider->getResponse($request);
    } catch (\GuzzleHttp\Exception\ClientException $e) {

      // See if we have been throttled despite our best efforts...
      if (strpos($message,"API key has exceeded the per-second rate limit")!==false) {
        sleep(5);  // wait 5 seconds and try again
        return $this->apiCall($method,$url);
      }
      die("Error: ".$e->getMessage());
    }

    if (strtolower($method)=='get') {
      $parsed = json_decode($response->getBody(),true);
      if (json_last_error() !== JSON_ERROR_NONE) {
        die ("Invalid JSON received from API");
      }
      return $parsed;
    } else {
      return true;
    }
  }

  // This function retrieves paginated data as a single array
  public function fetchAll($method, $url, $pageSize=1000, $filter=null) {
    $allResults=array();
    do {
      $result=$this->apiCall($method,$url,$pageSize,$filter);
      $allResults=array_merge($allResults,$result['Items']);
      $url = $result['NextPageLink'];
    } while (!empty($url));
    return $allResults;
  }

}

session_start();
$myob=new myMYOB();
print_r($myob->fetchAll('GET', '/GeneralLedger/Job'));