Subscriptions
Subscriptions bill something that recurs – a pass, a plan, a server by the hour. They work like orders: create a draft, let the user confirm it, read it back.
Endpoints
| Method | Path | |
|---|---|---|
GET | /api/v1/subscriptions | the user's subscriptions, newest first |
POST | /api/v1/subscriptions | create a subscription |
GET | /api/v1/subscriptions/{id} | one subscription |
PUT | /api/v1/subscriptions/{id} | change it before checkout, afterwards only the description (trusted apps) |
PUT | /api/v1/subscriptions/{id}/checkout | start it (trusted apps; third-party apps let the user confirm) |
PUT | /api/v1/subscriptions/{id}/pause | pause: runs until the paid period ends, then expires |
PUT | /api/v1/subscriptions/{id}/resume | resume a paused or expired subscription |
PUT | /api/v1/subscriptions/{id}/revoke | cancel; {"refund": true} credits usage not yet charged |
User token; scopes subscriptions (or subscriptions:read to read). Third-party apps can only pause, resume or revoke subscriptions they created. Trusted apps can manage subscriptions without the user's token: see App subscriptions.
Creating a subscription
curl -X POST https://id.anikeen.com/api/v1/subscriptions \
-H 'Authorization: Bearer USER_TOKEN' \
-H 'Accept: application/json' -H 'Content-Type: application/json' \
-d '{"name": "Pixel Stream Pass", "description": "All streams, every month", "unit": "month", "price": 6.714286}'$subscription = $user->subscriptions()->create([
'name' => 'Pixel Stream Pass',
'description' => 'All streams, every month',
'unit' => 'month',
'price' => 6.714286, // net per unit, 7.99 with 19 % VAT
]);
return redirect()->away($subscription->confirmationUrl(route('pass.return')));
// later: https://shop.example.com/pass/return?status=completed&subscription=01J…Fields
| Field | |
|---|---|
name | required |
description | optional; shown on the user's bills |
unit | required: hour, day, week, month or year |
price | required: net per unit, up to 6 decimals |
group | your app's group, e.g. a team ID |
payload | your own data (an object) |
ends_at | optional end date |
monthly_cap | hour and day units only: net, at most this per calendar month (not below price) |
trial_days / trial_ends_at, trial_once, trial_payment_method | a free trial |
discount | a discount per billed period |
withdrawal_waiver_required | ask for the early start |
Subscriptions of third-party apps are drafts with a confirmation_url. The user confirms on Anikeen ID (a payment method is required, unless the trial allows none) and returns to return_url?status=completed|cancelled&subscription=<id>. Read the subscription afterwards:
$subscription = $user->subscriptions()->find($id);
if ($subscription->isActive()) {
// grant access
}Billing
Subscriptions are billed in arrears: each started period is added to the user's open monthly bill of your app and group, which is charged on the 1st of the following month. With a monthly_cap, an hourly or daily subscription costs at most the cap per calendar month – the units beyond it are listed at price 0.
expected_monthly_price on a subscription is a gross estimate for a month (hour × 720, day × 30, week × 30/7, year ÷ 12; with a monthly cap at most the cap).
Status
status (in the SDK status(), constants on Anikeen\Id\Enums\SubscriptionStatus):
status | Meaning | Active |
|---|---|---|
draft | Created, not confirmed by the user yet | no |
trial | Running, in its trial period | yes |
active | Running and billed | yes |
paused | Runs until the paid period ends, then expires (resume to continue) | yes |
cancelling | Runs until a scheduled cancellation takes effect | yes |
expired | Paused and ran out; resume starts a new period | no |
cancelled | Cancelled for good | no |
The SDK has a shortcut for each: isActive(), isDraft(), isTrial(), isPaused(), isCancelling(), isExpired(), isCancelled().
A subscription also carries cancels_at (when a scheduled cancellation takes effect), trial_ends_at, discount, cancellation_reason (why the billing cancelled it, e.g. trial_ended_without_payment_method) and checkout_session_id (the checkout session it waits in, if any).
Pause, resume, cancel
$subscription->pause(); // expires when the paid period ends
$subscription->resume(); // an expired subscription starts a new, billed period
$subscription->revoke(refund: false);Resuming a subscription after its paid period ran out starts a new period; the paused time isn't billed.
Webhooks
Follow your subscriptions through subscription.created, subscription.activated, subscription.paused, subscription.resumed, subscription.cancelled, subscription.expired and more; see the event reference.