mirror of
https://github.com/anikeen-com/id.git
synced 2026-10-31 20:59:32 +00:00
$user->appOrders() and app(AnikeenId::class)->appOrders() read v1/client/orders with the app's own token (scope orders:client): all(), pending() and find(), paginated transparently, user scoped when called on a billable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
531 lines
13 KiB
Markdown
531 lines
13 KiB
Markdown
# Anikeen ID
|
|
|
|
[](https://packagist.org/packages/anikeen/id)
|
|
[](https://packagist.org/packages/anikeen/id)
|
|
[](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->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
|
|
|
|
## AnikeenId
|
|
|
|
### Oauth
|
|
|
|
```php
|
|
public function retrievingToken(string $grantType, array $attributes): Result
|
|
```
|
|
|
|
### ManagesPricing
|
|
|
|
```php
|
|
public function createOrderPreview(array $attributes = []): Result
|
|
```
|
|
|
|
### ManagesProducts
|
|
|
|
```php
|
|
public function products(): Result
|
|
public function boxes(): Result
|
|
```
|
|
|
|
### ManagesShippingLabels
|
|
|
|
```php
|
|
public function shippingLabels(array $parameters = []): Result
|
|
public function shippingLabel(string $id): Result
|
|
public function shippingLabelPdf(string $id): ?string
|
|
public function issueShippingLabel(array $attributes): Result
|
|
public function shipShippingLabel(array $hex): Result
|
|
public function shippingPriceList(): Result
|
|
```
|
|
|
|
### ManagesUserBilling
|
|
|
|
```php
|
|
public function appOrders(): AppOrders
|
|
public function createOrderForUser(string $userId, array $attributes): Result
|
|
public function createTransactionForUser(string $userId, array $attributes): Result
|
|
```
|
|
|
|
### ManagesSocialiteUsers
|
|
|
|
```php
|
|
public function socialiteUsers(): SocialiteUsers
|
|
```
|
|
|
|
### ManagesSshKeys
|
|
|
|
```php
|
|
public function sshKeysByUserId(string $userId): SshKeys
|
|
```
|
|
|
|
### ManagesUsers
|
|
|
|
```php
|
|
public function getAuthedUser(): Result
|
|
public function createUser(array $attributes): Result
|
|
public function findOrCreateSocialiteUser(string $provider, string $providerId, ?string $email = null, ?string $name = null): Result
|
|
public function refreshToken(string $storedRefreshToken, string $scope = ''): Result
|
|
public function isEmailExisting(string $email): Result
|
|
public function setParent(mixed $parent): self
|
|
public function getParent()
|
|
```
|
|
|
|
|
|
## Billable
|
|
|
|
### ManagesAddresses
|
|
|
|
```php
|
|
public function addresses(): Addresses
|
|
public function hasDefaultBillingAddress(): bool
|
|
public function setBillable(Billable|Model $billable): self
|
|
public function getBillable(): Billable
|
|
```
|
|
|
|
### ManagesBalance
|
|
|
|
```php
|
|
public function balance(): float
|
|
public function charges(): float
|
|
public function billingOverview(): object
|
|
public function charge(string|float $amount, string $paymentMethodId, array $options = []): Transaction
|
|
public function setBillable(Billable|Model $billable): self
|
|
public function getBillable(): Billable
|
|
```
|
|
|
|
### ManagesCountries
|
|
|
|
```php
|
|
public function countries(): Countries
|
|
public function setBillable(Billable|Model $billable): self
|
|
public function getBillable(): Billable
|
|
```
|
|
|
|
### ManagesInvoices
|
|
|
|
```php
|
|
public function invoices(array $parameters = []): Invoices
|
|
public function setBillable(Billable|Model $billable): self
|
|
public function getBillable(): Billable
|
|
```
|
|
|
|
### ManagesOrders
|
|
|
|
```php
|
|
public function orders(array $parameters = []): Orders
|
|
public function setBillable(Billable|Model $billable): self
|
|
public function getBillable(): Billable
|
|
```
|
|
|
|
### ManagesPaymentMethods
|
|
|
|
```php
|
|
public function paymentMethods(): PaymentMethods
|
|
public function hasPaymentMethod(): ?PaymentMethod
|
|
public function defaultPaymentMethod(): ?PaymentMethod
|
|
public function hasDefaultPaymentMethod(): bool
|
|
public function setDefaultPaymentMethod(string $id): PaymentMethod
|
|
public function billingPortalUrl(?string $returnUrl = null, array $options = []): string
|
|
public function createSetupIntent(array $options = []): Result
|
|
public function setBillable(Billable|Model $billable): self
|
|
public function getBillable(): Billable
|
|
```
|
|
|
|
### ManagesProfile
|
|
|
|
```php
|
|
public function profilePortalUrl(?string $returnUrl = null, array $options = []): string
|
|
public function setBillable(Billable|Model $billable): self
|
|
public function getBillable(): Billable
|
|
```
|
|
|
|
### ManagesSubscriptions
|
|
|
|
```php
|
|
public function subscriptions(): Subscriptions
|
|
public function appSubscriptions(): AppSubscriptions
|
|
public function appOrders(): AppOrders
|
|
public function setBillable(Billable|Model $billable): self
|
|
public function getBillable(): Billable
|
|
```
|
|
|
|
### ManagesTaxation
|
|
|
|
```php
|
|
public function vatRate(): float
|
|
public function setBillable(Billable|Model $billable): self
|
|
public function getBillable(): Billable
|
|
```
|
|
|
|
### ManagesTransactions
|
|
|
|
```php
|
|
public function transactions(): Transactions
|
|
public function setBillable(Billable|Model $billable): self
|
|
public function getBillable(): Billable
|
|
```
|
|
|
|
### ManagesUserSshKeys
|
|
|
|
```php
|
|
public function sshKeys(): SshKeys
|
|
public function setBillable(Billable|Model $billable): self
|
|
public function getBillable(): Billable
|
|
```
|
|
|
|
|
|
[**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
|
|
```
|