Skip to content

Commit

Permalink
tokens refreshing changes
Browse files Browse the repository at this point in the history
  • Loading branch information
hotrush committed Jul 30, 2019
1 parent 5be45eb commit 1954004
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public function up()
$table->string('realm_id')->nullable();
$table->timestamp('issued_at')->nullable();
$table->timestamp('expire_at')->nullable();
$table->timestamp('refresh_at')->nullable();
$table->timestamp('refresh_expire_at')->nullable();
});
}
Expand Down
10 changes: 9 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,12 @@ Each event has next arguments:
- `entityName` - the name of the entity type that changed (Customer, Invoice, etc.)
- `entityId` - changed entity id
- `lastUpdated` - carbon-parsed date object
- `deletedId` - the ID of the entity that was deleted and merged (only for Merge events)
- `deletedId` - the ID of the entity that was deleted and merged (only for Merge events)

## Tokens refresh schedule

Schedule refreshing in `App\Console\Kernel`. [Schedule docs](https://laravel.com/docs/5.8/scheduling#defining-schedules).

```php
$schedule->command(RefreshTokensCommand::class)->everyMinute();
```
49 changes: 49 additions & 0 deletions src/Commands/RefreshTokensCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace Hotrush\QuickBooksManager\Commands;

use Hotrush\QuickBooksManager\QuickBooksManager;
use Hotrush\QuickBooksManager\QuickBooksToken;
use Illuminate\Console\Command;
use QuickBooksOnline\API\Exception\ServiceException;

class RefreshTokensCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'qbm:refresh-tokens';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Refresh access tokens.';

/**
* Execute the console command.
*
* @param QuickBooksManager $manager
*
* @return mixed
*/
public function handle(QuickBooksManager $manager)
{
$tokens = QuickBooksToken::where('refresh_at','<=',now())->where('refresh_expire_at','>',now())->get();

if (!$tokens->count()) {
return;
}

foreach ($tokens as $token) {
try {
$manager->connection($token->connection)->refreshToken();
} catch (ServiceException $e) {
$this->error(sprintf('Error refreshing token for connection "%s": %s', $token->connection, $e->getMessage()));
}
}
}
}
38 changes: 6 additions & 32 deletions src/QuickBooksConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,10 @@ private function initClient($forceRefresh = false)
'baseUrl' => $this->config['base_url'],
'QBORealmID' => $this->token ? $this->token->realm_id : null,
'accessTokenKey' => $this->token && !$this->token->isExpired() ? $this->token->access_token : null,
'refreshTokenKey' => $this->token && ($this->token->isExpired() || $forceRefresh) && $this->token->isRefreshable() ? $this->token->refresh_token : null,
'refreshTokenKey' => $this->token && $forceRefresh && $this->token->isRefreshable() ? $this->token->refresh_token : null,
])
->setLogLocation(config('quickbooks_manager.logs_path'))
->throwExceptionOnError(true);

if ($this->token && ($this->token->isExpired() || $forceRefresh) && $this->token->isRefreshable()) {
$this->refreshToken();
}
}

/**
Expand Down Expand Up @@ -102,8 +98,10 @@ private function loadTokenFromDatabase()
/**
* @throws \QuickBooksOnline\API\Exception\ServiceException
*/
private function refreshToken()
public function refreshToken()
{
$this->initClient(true);

$accessToken = $this->client->getOAuth2LoginHelper()->refreshToken();

$this->updateAccessToken($accessToken);
Expand All @@ -117,6 +115,8 @@ private function updateAccessToken(OAuth2AccessToken $accessToken)
$this->client->updateOAuth2Token($accessToken);

$this->token = QuickBooksToken::createFromToken($this->name, $accessToken);

QuickBooksToken::removeExpired($this->name, [$this->token->id]);
}

/**
Expand All @@ -126,33 +126,7 @@ private function updateAccessToken(OAuth2AccessToken $accessToken)
* @throws ServiceException
*/
public function __call($method, $parameters)
{
try {
return $this->executeSdkMethod($method, $parameters);
} catch (ServiceException $e) {
if ($this->detectTokenError($e) && $this->token && $this->token->isRefreshable()) {
$this->initClient(true);
return $this->executeSdkMethod($method, $parameters);
}
}
}

/**
* @param $method
* @param $parameters
* @return mixed
*/
private function executeSdkMethod($method, $parameters)
{
return $this->client->$method(...$parameters);
}

/**
* @param ServiceException $e
* @return bool
*/
private function detectTokenError(ServiceException $e)
{
return $e->getCode() === 401 && strpos($e->getMessage(), 'Token expired') !== false;
}
}
7 changes: 7 additions & 0 deletions src/QuickBooksManagerServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Hotrush\QuickBooksManager;

use Hotrush\QuickBooksManager\Commands\RefreshTokensCommand;
use Illuminate\Support\ServiceProvider;

class QuickBooksManagerServiceProvider extends ServiceProvider
Expand All @@ -11,6 +12,7 @@ public function boot()
if ($this->app->runningInConsole()) {
$this->definePublishingGroups();
$this->defineMigrations();
$this->defineCommands();
}

$this->loadRoutesFrom(__DIR__.'/../routes/route.php');
Expand Down Expand Up @@ -38,4 +40,9 @@ private function defineMigrations()
{
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
}

private function defineCommands()
{
$this->commands(RefreshTokensCommand::class);
}
}
22 changes: 18 additions & 4 deletions src/QuickBooksToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,26 @@

class QuickBooksToken extends Model
{
const TOKEN_LIFETIME = 3600;
const TOKEN_REFRESH_PERIOD = 3000;
const REFRESH_TOKEN_LIFETIME = 8726400;

protected $table = 'quickbooks_tokens';

protected $fillable = [
'connection', 'access_token', 'refresh_token', 'realm_id', 'issued_at', 'expire_at', 'refresh_expire_at'
'connection', 'access_token', 'refresh_token', 'realm_id',
'issued_at', 'expire_at', 'refresh_at', 'refresh_expire_at',
];

public $timestamps = false;

protected $dates = [
'issued_at', 'expire_at', 'refresh_expire_at',
'issued_at', 'expire_at', 'refresh_at', 'refresh_expire_at',
];

/**
* @return bool
*/
public function isExpired()
{
return $this->expire_at < now();
Expand All @@ -40,8 +48,14 @@ public static function createFromToken($connection, OAuth2AccessToken $token)
'refresh_token' => $token->getRefreshToken(),
'realm_id' => $token->getRealmID(),
'issued_at' => now(),
'expire_at' => now()->addSeconds(3600),
'refresh_expire_at' => now()->addSeconds(8726400),
'expire_at' => now()->addSeconds(self::TOKEN_LIFETIME),
'refresh_at' => now()->addSeconds(self::TOKEN_REFRESH_PERIOD),
'refresh_expire_at' => now()->addSeconds(self::REFRESH_TOKEN_LIFETIME),
]);
}

public static function removeExpired($connection, $except = [])
{
return self::where('connection', $connection)->whereNotIn('id', $except)->delete();
}
}

0 comments on commit 1954004

Please sign in to comment.