Files
id/README.stub
Maurice PreußandClaude Opus 5.5 c4a42b4ddc Check out a running service from its start
AppSubscription::checkout() takes an optional $startsAt (needs id-core
with starts_at support on PUT v1/client/subscriptions/{id}/checkout).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:03:47 +01:00

366 lines
9.1 KiB
Plaintext

# Anikeen ID
[![Latest Stable Version](https://img.shields.io/packagist/v/anikeen/id.svg?style=flat-square)](https://packagist.org/packages/anikeen/id)
[![Total Downloads](https://img.shields.io/packagist/dt/anikeen/id.svg?style=flat-square)](https://packagist.org/packages/anikeen/id)
[![License](https://img.shields.io/packagist/l/anikeen/id.svg?style=flat-square)](https://packagist.org/packages/anikeen/id)
PHP Anikeen ID API Client for Laravel 11+
## Table of contents
1. [Installation](#installation)
2. [Configuration](#configuration)
3. [General](#general)
4. [Examples](#examples)
5. [Documentation](#documentation)
6. [Development](#Development)
## Installation
```
composer require anikeen/id
```
## Configuration
Add environmental variables to your `.env` file:
```
ANIKEEN_ID_KEY=
ANIKEEN_ID_SECRET=
ANIKEEN_ID_CALLBACK_URL=http://localhost/auth/callback
```
To switch from `production` to `staging` use following variable:
```
ANIKEEN_ID_MODE=staging
```
You will need to add an entry to the services configuration file so that after config files are cached for usage in production environment (Laravel command `artisan config:cache`) all config is still available.
Add to `config/services.php` file:
```php
'anikeen' => [
'mode' => env('ANIKEEN_ID_MODE'),
'client_id' => env('ANIKEEN_ID_KEY'),
'client_secret' => env('ANIKEEN_ID_SECRET'),
'redirect' => env('ANIKEEN_ID_CALLBACK_URL'),
'base_url' => env('ANIKEEN_ID_BASE_URL'),
],
```
### Event Listener
In Laravel 11, the default EventServiceProvider provider was removed. Instead, add the listener using the listen method on the Event facade, in your `AppServiceProvider` boot method:
```php
public function boot(): void
{
Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) {
$event->extendSocialite('anikeen', \Anikeen\Id\Socialite\Provider::class);
});
}
```
### Registering Middleware
Append it to the global middleware stack in your application's `bootstrap/app.php` file:
```php
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
\Anikeen\Id\Http\Middleware\CreateFreshApiToken::class,
]);
})
```
### Implementing Billable
To implement the `Billable` trait, you need to add the `Billable` trait to your user model.
```php
use Anikeen\Id\Billable;
class User extends Authenticatable
{
use Billable;
// Your model code...
}
```
then, you can use the `Billable` trait methods in your user model.
### App subscriptions (trusted apps, no user token)
`subscriptions()` acts with the user's own access token and stops working when the user's login
expired. Trusted apps can manage a user's subscriptions with the app's own token (client
credentials, scope `subscriptions:client`) instead:
```php
$subscriptions = $team->owner->appSubscriptions(); // nothing is requested yet
$subscription = $subscriptions->create([
'group' => $team->id,
'name' => 'Cloud Server S',
'unit' => 'hour',
'price' => 0.0137,
'idempotency_key' => "server:{$pendingServer->id}", // retries return the same subscription
]);
$subscription->checkout(); // bill the first period
$subscription->checkout(startsAt: $domain->registered_at); // already running: bill the period that started then
$subscription->pause(); // expires when the paid period ends
$subscription->resume(); // expired subscriptions start a new period
$subscription->revoke(refund: false); // cancel
$subscriptions->find($id); // null if unknown or not this user's
$subscriptions->all(['group' => $team->id, 'active' => true]);
```
The user's Anikeen ID is read from the billable's `anikeen_id` attribute
(`AnikeenId::useUserIdField('…')` to change it). The app must be trusted by Anikeen ID; it sees the
subscriptions it created (plus those of clients delegated to it).
Failures throw `Anikeen\Id\Exceptions\ApiException` with `status` (0 = not reachable), a stable
`error` code (`already_active`, `already_cancelled`, `not_active`, `already_paused`,
`already_resumed`, `billing_address_missing`, `not_changeable`, `subscription_not_found`,
`idempotency_key_conflict`) and `isNotFound()`, `isConflict()`, `isTemporary()`:
```php
try {
$subscription->revoke();
} catch (ApiException $e) {
if ($e->isNotFound() || $e->error === 'already_cancelled') {
// already gone
} elseif ($e->isTemporary()) {
// retry later
} else {
throw $e;
}
}
```
### App orders (trusted apps, no user token)
Orders that bill the app's subscriptions, read with the app's token (scope `orders:client`):
```php
$team->owner->appOrders()->pending(); // unpaid orders of one user, oldest first
app(AnikeenId::class)->appOrders()->pending(); // of all users
$team->owner->appOrders()->find($orderId); // null if unknown or not this user's
```
Each order has `status` (`processing` = current month, still collecting; `pending` = closed and not
paid; `completed`; `cancelled`), the totals, `payment_attempts`, `next_payment_attempt_at` (null =
no more automatic retries) and `last_payment_error`.
### Change the default access token / refresh token field name
If you access / refresh token fields differs from the default `anikeen_id_access_token` / `anikeen_id_refresh_token`, you can specify the field name in the `AppServiceProvider` boot method:
```php
use Anikeen\Id\AnikeenId;
public function boot(): void
{
AnikeenId::useAccessTokenField('anikeen_id_access_token');
AnikeenId::useRefreshTokenField('anikeen_id_refresh_token');
}
```
### Implementing Auth
This method should typically be called in the `boot` method of your `AppServiceProvider` class:
```php
use Anikeen\Id\AnikeenId;
use Anikeen\Id\Providers\AnikeenIdUserProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
public function boot(): void
{
Auth::provider('anikeen', function ($app, array $config) {
return new AnikeenIdUserProvider(
$app->make(AnikeenId::class),
$app->make(Request::class),
$config['model'],
$config['fields'] ?? [],
);
});
}
```
reference the guard in the `guards` configuration of your `auth.php` configuration file:
```php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'anikeen',
'provider' => 'anikeen',
],
],
```
reference the provider in the `providers` configuration of your `auth.php` configuration file:
```php
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'anikeen' => [
'driver' => 'anikeen',
'model' => App\Models\User::class,
'fields' => ['first_name', 'last_name', 'email'],
],
],
```
## General
#### Setters and Getters
```php
$anikeenId = new Anikeen\Id\AnikeenId();
$anikeenId->setClientId('abc123');
$anikeenId->setClientSecret('abc456');
$anikeenId->setToken('abcdef123456');
$anikeenId = $anikeenId->withClientId('abc123');
$anikeenId = $anikeenId->withClientSecret('abc123');
$anikeenId = $anikeenId->withToken('abcdef123456');
```
#### Error handling for an unsuccessful query:
```php
$result = $anikeenId->sshKeysByUserId('someInvalidId');
// Check, if the query was successfully
if (!$result->success()) {
die('Ooops: ' . $result->error());
}
```
#### Shift result to get single key data:
```php
$result = $anikeenId->sshKeysByUserId('someValidId');
$sshKey = $result->shift();
echo $sshKey->name;
```
## Examples
#### Get User SSH Key
```php
$anikeenId = new Anikeen\IdAnikeenId();
$anikeenId->setClientId('abc123');
// Get SSH Key by User ID
$result = $anikeenId->sshKeysByUserId('someValidId');
// Check, if the query was successfully
if (!$result->success()) {
die('Ooops: ' . $result->error());
}
// Shift result to get single key data
$sshKey = $result->shift();
echo $sshKey->name;
```
#### Create Order Preview
```php
$anikeenId = new \Anikeen\Id\AnikeenId();
// Create new Order Preview
$result = $anikeenId->createOrderPreview([
'country_iso' => 'de',
'items' => [
[
'type' => 'physical',
'name' => 'Test',
'price' => 2.99,
'unit' => 'onetime',
'units' => 1,
]
]
])->shift();
echo $preview->gross_total;
```
#### OAuth Tokens
```php
$anikeenId = new Anikeen\Id\AnikeenId();
$anikeenId->setClientId('abc123');
$anikeenId->setToken('abcdef123456');
$result = $anikeenId->getAuthedUser();
$user = $userResult->shift();
```
```php
$anikeenId->setToken('uvwxyz456789');
$result = $anikeenId->getAuthedUser();
```
```php
$result = $anikeenId->withToken('uvwxyz456789')->getAuthedUser();
```
#### Facade
```php
use Anikeen\Id\Facades\AnikeenId;
AnikeenId::withClientId('abc123')->withToken('abcdef123456')->getAuthedUser();
```
## Documentation
<!-- GENERATED-DOCS -->
[**OAuth Scopes Enums**](https://github.com/anikeen-com/id/blob/main/src/Enums/Scope.php)
## Development
#### Run Tests
```shell
composer test
```
```shell
BASE_URL=xxxx CLIENT_ID=xxxx CLIENT_KEY=yyyy CLIENT_ACCESS_TOKEN=zzzz composer test
```
#### Generate Documentation
```shell
composer docs
```