Files
id/src/Id/Resources/AppSubscription.php
T
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

135 lines
3.5 KiB
PHP

<?php
namespace Anikeen\Id\Resources;
use Anikeen\Id\Exceptions\ApiException;
use Anikeen\Id\Support\AppRequest;
use DateTimeInterface;
use JsonSerializable;
/**
* A subscription managed by the app itself (client credentials), see AppSubscriptions.
*
* @property string $id
* @property string $user_id
* @property ?string $group
* @property string $name
* @property ?string $description
* @property string $unit
* @property float $price
* @property ?float $vat_rate
* @property bool $active
* @property bool $autorenew
* @property bool $cancelled
* @property ?string $status "active" or "inactive"
* @property ?string $starts_at
* @property ?string $ends_at
* @property ?string $accounting_at
* @property bool $is_past_due
* @property int $failed_renewal_retries
* @property ?array $payload
* @property ?string $idempotency_key
*/
class AppSubscription implements JsonSerializable
{
public function __construct(protected object $data)
{
}
public function __get(string $name): mixed
{
return $this->data->{$name} ?? null;
}
public function __isset(string $name): bool
{
return isset($this->data->{$name});
}
/**
* Change a subscription that wasn't checked out yet.
*
* @throws ApiException
*/
public function update(array $attributes): self
{
return $this->send('PUT', '', $attributes);
}
/**
* Activate the subscription and bill its first period.
*
* $startsAt (in the past) books a service that is already running, e.g. a domain registered
* earlier: the first period runs from $startsAt, is billed now and renews when it ends.
*
* @throws ApiException 409 "already_active", "billing_address_missing", "period_already_ended", …
*/
public function checkout(DateTimeInterface|string|null $startsAt = null): self
{
if ($startsAt instanceof DateTimeInterface) {
$startsAt = $startsAt->format(DateTimeInterface::ATOM);
}
return $this->send('PUT', '/checkout', $startsAt === null ? null : ['starts_at' => $startsAt]);
}
/**
* Cancel the subscription. With $refund (only within 14 days of its start), its not yet
* charged usage is credited.
*
* @throws ApiException 409 "already_cancelled", 404 "subscription_not_found", …
*/
public function revoke(bool $refund = false): self
{
return $this->send('PUT', '/revoke', ['refund' => $refund]);
}
/**
* Stop renewing; the subscription expires when its paid period ends.
*
* @throws ApiException
*/
public function pause(): self
{
return $this->send('PUT', '/pause');
}
/**
* Renew again (an expired subscription starts a new period).
*
* @throws ApiException
*/
public function resume(): self
{
return $this->send('PUT', '/resume');
}
/**
* @throws ApiException
*/
public function refresh(): self
{
return $this->send('GET', '');
}
public function toArray(): array
{
return (array) $this->data;
}
public function jsonSerialize(): array
{
return $this->toArray();
}
/**
* @throws ApiException
*/
private function send(string $method, string $action, ?array $payload = null): self
{
$this->data = AppRequest::send($method, sprintf('v1/client/subscriptions/%s%s', $this->id, $action), $payload)->data;
return $this;
}
}