mirror of
https://github.com/anikeen-com/id.git
synced 2026-10-31 20:59:32 +00:00
App subscriptions: manage a user's subscriptions with the app's own token
- $user->appSubscriptions(): create (idempotency_key), find, all, checkout, revoke, pause, resume, update against /v1/client/subscriptions with a client credentials token, so apps don't depend on the user's login - user id from the billable's anikeen_id (AnikeenId::useUserIdField()) - ApiException with status, stable error code, isNotFound/isConflict/isTemporary; network errors no longer escape as raw Guzzle exceptions - rejected app tokens are renewed once; asApp() never changes the shared client's token - tests (testbench + Guzzle mock) and CI Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9c3f9344a0
commit
8de737641e
13 files changed
+749
-57
No files matched your search
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Anikeen\Id\Tests;
|
||||
|
||||
use Anikeen\Id\AnikeenId;
|
||||
use Anikeen\Id\Contracts\AppTokenRepository;
|
||||
use Anikeen\Id\Providers\AnikeenIdServiceProvider;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Middleware;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Orchestra\Testbench\TestCase as Orchestra;
|
||||
|
||||
abstract class TestCase extends Orchestra
|
||||
{
|
||||
protected MockHandler $http;
|
||||
|
||||
/** @var list<array{request: \Psr\Http\Message\RequestInterface}> */
|
||||
protected array $sent = [];
|
||||
|
||||
protected function getPackageProviders($app): array
|
||||
{
|
||||
return [AnikeenIdServiceProvider::class];
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
config(['services.anikeen.client_id' => 'app-client', 'services.anikeen.client_secret' => 'secret']);
|
||||
|
||||
$this->http = new MockHandler();
|
||||
$stack = HandlerStack::create($this->http);
|
||||
$stack->push(Middleware::history($this->sent));
|
||||
|
||||
$this->app->instance(AnikeenId::class, (new AnikeenId())->setHttpClient(new Client(['handler' => $stack, 'base_uri' => 'https://id.test/api/'])));
|
||||
$this->app->instance(AppTokenRepository::class, new class implements AppTokenRepository {
|
||||
public int $forgotten = 0;
|
||||
|
||||
public function getAccessToken(): string
|
||||
{
|
||||
return $this->forgotten ? 'fresh-app-token' : 'app-token';
|
||||
}
|
||||
|
||||
public function forgetAccessToken(): void
|
||||
{
|
||||
$this->forgotten++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected function respond(int $status, array $body = []): void
|
||||
{
|
||||
$this->http->append(new Response($status, ['Content-Type' => 'application/json'], json_encode($body)));
|
||||
}
|
||||
|
||||
protected function sentJson(int $index): array
|
||||
{
|
||||
return json_decode((string) $this->sent[$index]['request']->getBody(), true) ?? [];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user