globalvisionmedia / oauth2-myob
为PHP League OAuth2-Client提供的MYOB OAuth 2.0客户端提供者
v1.4
2022-09-17 04:00 UTC
Requires
- php: >=5.5.0
- league/oauth2-client: ^2.0
README
此软件包为PHP League的OAuth 2.0客户端提供MYOB OAuth 2.0支持。
安装
composer require globalvisionmedia/oauth2-myob
获取MYOB访问密钥
- 要获取密钥,您需要成为MYOB开发者计划的一部分(https://developer.myob.com/program/become-a-myob-developer-partner/)
- 获取账户后,登录并点击my.myob.com.au的“开发者”标签
- 点击“注册应用”按钮以创建密钥
- 重定向API必须与下面的redirectUri完全相同(包括http://或https://),这是您应用程序的URL
用法
用法与The League的OAuth客户端相同,使用\GlobalVisionMedia\OAuth2\MYOBClient\Provider\MYOB作为提供者,但以下不同:
-
MYOB不仅依赖OAuth2进行登录。OAuth2用于提供API访问权限,但不是访问您的MYOB数据文件。这需要一个二次登录,返回一个“公司URL”。此提供者处理这两个登录,但要求您除了标准的OAuth2凭据外,还提供您的MYOB登录详细信息。(见下面的授权码流)
-
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'));