anikeen / id
PHP Anikeen ID API client for Laravel 11+
Requires
- php: ^8.2
- ext-json: *
- firebase/php-jwt: ^6.10|^7.0
- guzzlehttp/guzzle: ^7.8
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- socialiteproviders/manager: ^4.6
Requires (Dev)
- laravel/framework: ^11.0|^12.0|^13.0
- orchestra/testbench: ^9.0|^10.0|^11.0
- phpunit/phpunit: ^11.0|^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main
- 2.3.0
- 2.2.0
- 2.1.0
- 2.0.0
- 1.2.0
- 1.1.0
- 1.0.0
- 1.0.0-rc.10
- 1.0.0-rc.9
- 1.0.0-rc.8
- 1.0.0-rc.7
- 1.0.0-rc.6
- 1.0.0-rc.5
- 1.0.0-rc.4
- 1.0.0-rc.3
- 1.0.0-rc.2
- 1.0.0-rc.1
- 1.0.0-rc
- 1.0.0-alpha.11
- 1.0.0-alpha.10
- 1.0.0-alpha.9
- 1.0.0-alpha.8
- 1.0.0-alpha.7
- 1.0.0-alpha.6
- 1.0.0-alpha.5
- 1.0.0-alpha.4
- 1.0.0-alpha.3
- 1.0.0-alpha.2
- 1.0.0-alpha.1
- 1.0.0-alpha
- dev-feature/webhooks
- dev-feature/checkout-confirmation
- dev-feature/backdated-checkout
- dev-app-subscriptions
- dev-laravel-10
This package is auto-updated.
Last update: 2026-11-01 10:49:15 UTC
README
PHP Anikeen ID API Client for Laravel 11+
Table of contents
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:
'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:
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:
->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.
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:
$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():
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):
$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.
Selling as a third-party app (user confirms on Anikeen ID)
Apps that are not trusted can create orders and subscriptions for the signed-in user (user token),
but can't charge them. The user confirms on Anikeen ID and comes back to your return_url:
use Anikeen\Id\Exceptions\ConfirmationRequiredException; $order = $user->orders()->create([ 'billing_address_id' => $addressId, 'items' => [['type' => 'digital', 'name' => 'Sticker pack', 'unit' => 'piece', 'units' => 1, 'gross_price' => '9.99']], ]); return redirect($order->confirmationUrl(route('shop.done'))); // later: https://your-app.test/shop/done?status=completed&order=01J…
return_url must have the origin of one of your app's redirect URIs. status is completed,
pending (payment still processing) or cancelled; check it with $user->orders()->find($id)
instead of trusting the query string. Subscriptions work the same way
($user->subscriptions()->create([...])->confirmationUrl(...), &subscription=…).
Unconfirmed bookings are cancelled after 24 hours.
checkout() on such a draft throws ConfirmationRequiredException; its confirmationUrl
points to the same page. Trusted apps keep checking out directly.
Webhooks
Anikeen ID sends order.* and subscription.* events to the webhook endpoints of your app (set up
in the Anikeen ID admin panel, which also shows the signing secret whsec_…). Verify each request
with the raw body and answer 2xx within 10 seconds; failed deliveries are retried for about three days.
use Anikeen\Id\Exceptions\SignatureVerificationException; use Anikeen\Id\Webhooks\Webhook; Route::post('webhooks/anikeen', function (Request $request) { try { $event = Webhook::constructEvent($request->getContent(), $request->header('Anikeen-Signature'), config('services.anikeen.webhook_secret')); } catch (SignatureVerificationException) { abort(400); } match ($event['type']) { 'order.completed' => Shop::fulfil($event['data']['object']['id']), 'subscription.cancelled' => Panel::suspend($event['data']['object']['id']), default => null, }; return response()->noContent(); })->withoutMiddleware(VerifyCsrfToken::class);
Events can arrive more than once or out of order: use $event['id'] to skip duplicates, and
reload the object through the API when the current state matters.
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:
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:
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:
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'anikeen', 'provider' => 'anikeen', ], ],
reference the provider in the providers configuration of your auth.php configuration file:
'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
$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:
$result = $anikeenId->sshKeysByUserId('someInvalidId'); // Check, if the query was successfully if (!$result->success()) { die('Ooops: ' . $result->error()); }
Shift result to get single key data:
$result = $anikeenId->sshKeysByUserId('someValidId'); $sshKey = $result->shift(); echo $sshKey->name;
Examples
Get User SSH Key
$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
$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
$anikeenId = new Anikeen\Id\AnikeenId(); $anikeenId->setClientId('abc123'); $anikeenId->setToken('abcdef123456'); $result = $anikeenId->getAuthedUser(); $user = $userResult->shift();
$anikeenId->setToken('uvwxyz456789'); $result = $anikeenId->getAuthedUser();
$result = $anikeenId->withToken('uvwxyz456789')->getAuthedUser();
Facade
use Anikeen\Id\Facades\AnikeenId; AnikeenId::withClientId('abc123')->withToken('abcdef123456')->getAuthedUser();
Documentation
AnikeenId
Oauth
public function retrievingToken(string $grantType, array $attributes): Result
ManagesPricing
public function createOrderPreview(array $attributes = []): Result
ManagesProducts
public function products(): Result public function boxes(): Result
ManagesShippingLabels
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
public function appOrders(): AppOrders public function createOrderForUser(string $userId, array $attributes): Result public function createTransactionForUser(string $userId, array $attributes): Result
ManagesSocialiteUsers
public function socialiteUsers(): SocialiteUsers
ManagesSshKeys
public function sshKeysByUserId(string $userId): SshKeys
ManagesUsers
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
public function addresses(): Addresses public function hasDefaultBillingAddress(): bool public function setBillable(Billable|Model $billable): self public function getBillable(): Billable
ManagesBalance
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
public function countries(): Countries public function setBillable(Billable|Model $billable): self public function getBillable(): Billable
ManagesInvoices
public function invoices(array $parameters = []): Invoices public function setBillable(Billable|Model $billable): self public function getBillable(): Billable
ManagesOrders
public function orders(array $parameters = []): Orders public function setBillable(Billable|Model $billable): self public function getBillable(): Billable
ManagesPaymentMethods
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
public function profilePortalUrl(?string $returnUrl = null, array $options = []): string public function setBillable(Billable|Model $billable): self public function getBillable(): Billable
ManagesSubscriptions
public function subscriptions(): Subscriptions public function appSubscriptions(): AppSubscriptions public function appOrders(): AppOrders public function setBillable(Billable|Model $billable): self public function getBillable(): Billable
ManagesTaxation
public function vatRate(): float public function setBillable(Billable|Model $billable): self public function getBillable(): Billable
ManagesTransactions
public function transactions(): Transactions public function setBillable(Billable|Model $billable): self public function getBillable(): Billable
ManagesUserSshKeys
public function sshKeys(): SshKeys public function setBillable(Billable|Model $billable): self public function getBillable(): Billable
Development
Run Tests
composer test
BASE_URL=xxxx CLIENT_ID=xxxx CLIENT_KEY=yyyy CLIENT_ACCESS_TOKEN=zzzz composer test
Generate Documentation
composer docs