diff --git a/src/Id/Billable.php b/src/Id/Billable.php index 3c946e7..7e5618b 100644 --- a/src/Id/Billable.php +++ b/src/Id/Billable.php @@ -13,10 +13,9 @@ use Anikeen\Id\Concerns\ManagesProfile; use Anikeen\Id\Concerns\ManagesSubscriptions; use Anikeen\Id\Concerns\ManagesTaxation; use Anikeen\Id\Concerns\ManagesTransactions; -use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Helpers\Paginator; -use GuzzleHttp\Exception\GuzzleException; use stdClass; +use Throwable; trait Billable { @@ -32,27 +31,23 @@ trait Billable use ManagesTransactions; use Request; - protected stdClass|null $userData = null; - /** * Get the currently authenticated user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function getUserData(): stdClass { - if (!$this->userData) { - $this->userData = $this->request('GET', 'v1/user')->data; + if (!isset($this->userDataCache)) { + $this->userDataCache = $this->request('GET', 'v1/user')->data; } - return $this->userData; + return $this->userDataCache; } /** * Make a request to the Anikeen API. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function request(string $method, string $path, null|array $payload = null, array $parameters = [], ?Paginator $paginator = null): Result { diff --git a/src/Id/Concerns/MagicProperties.php b/src/Id/Concerns/MagicProperties.php deleted file mode 100644 index 0e6ba26..0000000 --- a/src/Id/Concerns/MagicProperties.php +++ /dev/null @@ -1,39 +0,0 @@ - $value) { - if (!property_exists($this, $key)) { - $this->{$key} = $value; - } - } - } - - /** - * Magic getter: return null for undefined properties - * - * @param string $name - * @return mixed|null - */ - public function __get(string $name) - { - return null; - } - - /** - * Magic isset: return false for undefined properties - * - * @param string $name - * @return bool - */ - public function __isset(string $name): bool - { - return false; - } -} \ No newline at end of file diff --git a/src/Id/Concerns/ManagesAddresses.php b/src/Id/Concerns/ManagesAddresses.php index ef7596c..63a1f05 100644 --- a/src/Id/Concerns/ManagesAddresses.php +++ b/src/Id/Concerns/ManagesAddresses.php @@ -5,7 +5,7 @@ namespace Anikeen\Id\Concerns; use Anikeen\Id\ApiOperations\Request; use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Resources\Addresses; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; trait ManagesAddresses { @@ -14,21 +14,23 @@ trait ManagesAddresses /** * Get addresses from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function addresses(): Addresses { - return (new Addresses($this->request('GET', 'v1/addresses'))) - ->setBillable($this); + if (!isset($this->addressesCache)) { + $this->addressesCache = Addresses::builder(fn() => $this->request('GET', 'v1/addresses')) + ->setBillable($this); + } + + return $this->addressesCache; } /** * Check if the current user has a default billing address. * * @see \Anikeen\Id\Resources\Addresses::hasDefaultBillingAddress() - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function hasDefaultBillingAddress(): bool { diff --git a/src/Id/Concerns/ManagesBalance.php b/src/Id/Concerns/ManagesBalance.php index 0c2150e..b80028c 100644 --- a/src/Id/Concerns/ManagesBalance.php +++ b/src/Id/Concerns/ManagesBalance.php @@ -3,10 +3,8 @@ namespace Anikeen\Id\Concerns; use Anikeen\Id\ApiOperations\Request; -use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Resources\Transaction; -use Anikeen\Id\Result; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; trait ManagesBalance { @@ -15,8 +13,7 @@ trait ManagesBalance /** * Get balance from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function balance(): float { @@ -26,8 +23,7 @@ trait ManagesBalance /** * Get charges from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function charges(): float { @@ -40,12 +36,11 @@ trait ManagesBalance * @param float $amount Amount to charge in euros. * @param string $paymentMethodId Payment method ID. * @param array $options Additional options for the charge. - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function charge(float $amount, string $paymentMethodId, array $options = []): Transaction { - return new Transaction($this->request('POST', 'billing/charge', [ + return new Transaction(fn() => $this->request('POST', 'billing/charge', [ 'amount' => $amount, 'payment_method_id' => $paymentMethodId, 'options' => $options, diff --git a/src/Id/Concerns/ManagesCountries.php b/src/Id/Concerns/ManagesCountries.php index 5792980..1fe32f8 100644 --- a/src/Id/Concerns/ManagesCountries.php +++ b/src/Id/Concerns/ManagesCountries.php @@ -5,7 +5,7 @@ namespace Anikeen\Id\Concerns; use Anikeen\Id\ApiOperations\Request; use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Resources\Countries; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; trait ManagesCountries { @@ -14,12 +14,15 @@ trait ManagesCountries /** * Get available countries for the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function countries(): Countries { - return (new Countries($this->request('GET', 'v1/countries'))) - ->setBillable($this); + if (!isset($this->countriesCache)) { + $this->countriesCache = Countries::builder(fn() => $this->request('GET', 'v1/countries')) + ->setBillable($this); + } + + return $this->countriesCache; } } \ No newline at end of file diff --git a/src/Id/Concerns/ManagesInvoices.php b/src/Id/Concerns/ManagesInvoices.php index 212fc85..0664c87 100644 --- a/src/Id/Concerns/ManagesInvoices.php +++ b/src/Id/Concerns/ManagesInvoices.php @@ -5,7 +5,7 @@ namespace Anikeen\Id\Concerns; use Anikeen\Id\ApiOperations\Request; use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Resources\Invoices; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; trait ManagesInvoices { @@ -14,12 +14,15 @@ trait ManagesInvoices /** * Get invoices from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function invoices(array $parameters = []): Invoices { - return (new Invoices($this->request('GET', 'v1/invoices', [], $parameters))) - ->setBillable($this); + if (!isset($this->invoicesCache)) { + $this->invoicesCache = Invoices::builder(fn() => $this->request('GET', 'v1/invoices', [], $parameters)) + ->setBillable($this); + } + + return $this->invoicesCache; } } \ No newline at end of file diff --git a/src/Id/Concerns/ManagesOrders.php b/src/Id/Concerns/ManagesOrders.php index c86548a..37fdc6e 100644 --- a/src/Id/Concerns/ManagesOrders.php +++ b/src/Id/Concerns/ManagesOrders.php @@ -6,7 +6,7 @@ use Anikeen\Id\ApiOperations\Request; use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Resources\Orders; use Anikeen\Id\Result; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; trait ManagesOrders { @@ -15,12 +15,15 @@ trait ManagesOrders /** * Get orders from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function orders(array $parameters = []): Orders { - return (new Orders($this->request('GET', 'v1/orders', [], $parameters))) - ->setBillable($this); + if (!isset($this->ordersCache)) { + $this->ordersCache = Orders::builder(fn() => $this->request('GET', 'v1/orders', [], $parameters)) + ->setBillable($this); + } + + return $this->ordersCache; } } \ No newline at end of file diff --git a/src/Id/Concerns/ManagesPaymentMethods.php b/src/Id/Concerns/ManagesPaymentMethods.php index 7710d86..847f2cf 100644 --- a/src/Id/Concerns/ManagesPaymentMethods.php +++ b/src/Id/Concerns/ManagesPaymentMethods.php @@ -7,7 +7,7 @@ use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Resources\PaymentMethod; use Anikeen\Id\Resources\PaymentMethods; use Anikeen\Id\Result; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; trait ManagesPaymentMethods { @@ -16,21 +16,24 @@ trait ManagesPaymentMethods /** * Get payment methods from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function paymentMethods(): PaymentMethods { - return (new PaymentMethods($this->request('GET', 'v1/payment-methods'))) - ->setBillable($this); + if (!isset($this->paymentMethodsCache)) {; + $this->paymentMethodsCache = PaymentMethods::builder( + fn() => $this->request('GET', 'v1/payment-methods') + )->setBillable($this); + } + + return $this->paymentMethodsCache; } /** * Check if current user has at least one payment method. * * @see \Anikeen\Id\Resources\PaymentMethods::hasPaymentMethod() - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function hasPaymentMethod(): ?PaymentMethod { @@ -41,8 +44,7 @@ trait ManagesPaymentMethods * Get default payment method from the current user. * * @see \Anikeen\Id\Resources\PaymentMethods::defaultPaymentMethod() - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function defaultPaymentMethod(): ?PaymentMethod { @@ -53,8 +55,7 @@ trait ManagesPaymentMethods * Check if the current user has a default payment method. * * @see \Anikeen\Id\Resources\PaymentMethods::hasDefaultPaymentMethod() - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function hasDefaultPaymentMethod(): bool { @@ -66,8 +67,7 @@ trait ManagesPaymentMethods * * @param string|null $returnUrl The URL to redirect to after the user has finished in the billing portal. * @param array $options Additional options for the billing portal. - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function billingPortalUrl(?string $returnUrl = null, array $options = []): string { @@ -81,8 +81,7 @@ trait ManagesPaymentMethods * Create a new setup intent. * * @param array $options Additional options for the setup intent. - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function createSetupIntent(array $options = []): Result { diff --git a/src/Id/Concerns/ManagesPricing.php b/src/Id/Concerns/ManagesPricing.php index 4890d49..6e92097 100644 --- a/src/Id/Concerns/ManagesPricing.php +++ b/src/Id/Concerns/ManagesPricing.php @@ -5,7 +5,7 @@ namespace Anikeen\Id\Concerns; use Anikeen\Id\ApiOperations\Post; use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Result; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; trait ManagesPricing { @@ -29,8 +29,7 @@ trait ManagesPricing * } $attributes The order data: * - country_iso: ISO 3166-1 alpha-2 country code * - items: Array of order items (each with type, name, price, unit, units, and quantity) - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function createOrderPreview(array $attributes = []): Result { diff --git a/src/Id/Concerns/ManagesProfile.php b/src/Id/Concerns/ManagesProfile.php index 4dd174a..f331ab8 100644 --- a/src/Id/Concerns/ManagesProfile.php +++ b/src/Id/Concerns/ManagesProfile.php @@ -5,7 +5,7 @@ namespace Anikeen\Id\Concerns; use Anikeen\Id\ApiOperations\Request; use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Result; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; trait ManagesProfile { @@ -17,8 +17,7 @@ trait ManagesProfile * @param string|null $returnUrl The URL to redirect to after the user has completed their profile. * @param array $options Additional options for the profile URL. * @return string - * @throws GuzzleException - * @throws RequestRequiresClientIdException + * @throws Throwable */ public function profilePortalUrl(?string $returnUrl = null, array $options = []): string { diff --git a/src/Id/Concerns/ManagesSshKeys.php b/src/Id/Concerns/ManagesSshKeys.php index 0435e2c..17994cb 100644 --- a/src/Id/Concerns/ManagesSshKeys.php +++ b/src/Id/Concerns/ManagesSshKeys.php @@ -6,7 +6,7 @@ namespace Anikeen\Id\Concerns; use Anikeen\Id\ApiOperations\Get; use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Resources\SshKeys; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; trait ManagesSshKeys { @@ -15,12 +15,15 @@ trait ManagesSshKeys /** * Get currently authed user with Bearer Token. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function sshKeysByUserId(string $sskKeyId): SshKeys { - return (new SshKeys($this->get(sprintf('v1/users/%s/ssh-keys/json', $sskKeyId)))) - ->setParent($this); + if (!isset($this->sshKeysCache)) { + $this->sshKeysCache = SshKeys::builder(fn() => $this->get(sprintf('v1/users/%s/ssh-keys/json', $sskKeyId))) + ->setParent($this); + } + + return $this->sshKeysCache; } } \ No newline at end of file diff --git a/src/Id/Concerns/ManagesSubscriptions.php b/src/Id/Concerns/ManagesSubscriptions.php index b86d2bf..b6dc837 100644 --- a/src/Id/Concerns/ManagesSubscriptions.php +++ b/src/Id/Concerns/ManagesSubscriptions.php @@ -5,7 +5,7 @@ namespace Anikeen\Id\Concerns; use Anikeen\Id\ApiOperations\Request; use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Resources\Subscriptions; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; trait ManagesSubscriptions { @@ -14,12 +14,15 @@ trait ManagesSubscriptions /** * Get subscriptions from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function subscriptions(): Subscriptions { - return (new Subscriptions($this->request('GET', 'v1/subscriptions'))) - ->setBillable($this); + if (!isset($this->subscriptionsCache)) { + $this->subscriptionsCache = Subscriptions::builder(fn() => $this->request('GET', 'v1/subscriptions')) + ->setBillable($this); + } + + return $this->subscriptionsCache; } } \ No newline at end of file diff --git a/src/Id/Concerns/ManagesTaxation.php b/src/Id/Concerns/ManagesTaxation.php index 4abde3b..191fcda 100644 --- a/src/Id/Concerns/ManagesTaxation.php +++ b/src/Id/Concerns/ManagesTaxation.php @@ -4,7 +4,7 @@ namespace Anikeen\Id\Concerns; use Anikeen\Id\ApiOperations\Request; use Anikeen\Id\Exceptions\RequestRequiresClientIdException; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; trait ManagesTaxation { @@ -13,11 +13,10 @@ trait ManagesTaxation /** * Get VAT for the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ - public function vat(): float + public function vatRate(): float { - return $this->getUserData()->vat; + return $this->getUserData()->vat_rate; } } \ No newline at end of file diff --git a/src/Id/Concerns/ManagesTransactions.php b/src/Id/Concerns/ManagesTransactions.php index d52f64d..d83a312 100644 --- a/src/Id/Concerns/ManagesTransactions.php +++ b/src/Id/Concerns/ManagesTransactions.php @@ -6,7 +6,7 @@ use Anikeen\Id\ApiOperations\Request; use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Resources\Transactions; use Anikeen\Id\Result; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; trait ManagesTransactions { @@ -15,12 +15,15 @@ trait ManagesTransactions /** * Get transactions from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function transactions(): Transactions { - return (new Transactions($this->request('GET', 'v1/transactions'))) - ->setBillable($this);; + if (!isset($this->transactionsCache)) { + $this->transactionsCache = Transactions::builder(fn() => $this->request('GET', 'v1/transactions')) + ->setBillable($this); + } + + return $this->transactionsCache; } } \ No newline at end of file diff --git a/src/Id/Concerns/ManagesUsers.php b/src/Id/Concerns/ManagesUsers.php index c99b6fe..3e00ee4 100644 --- a/src/Id/Concerns/ManagesUsers.php +++ b/src/Id/Concerns/ManagesUsers.php @@ -7,7 +7,7 @@ use Anikeen\Id\ApiOperations\Get; use Anikeen\Id\ApiOperations\Post; use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Result; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; trait ManagesUsers { @@ -17,8 +17,7 @@ trait ManagesUsers /** * Get currently authed user with Bearer Token * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function getAuthedUser(): Result { @@ -40,8 +39,7 @@ trait ManagesUsers * - username: The username (optional) * - email: The email (required) * - password: The password (optional, can be reset by the user if not provided) - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function createUser(array $attributes): Result { @@ -52,8 +50,7 @@ trait ManagesUsers * Checks if the given email exists. * * @param string $email The email to check. - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function isEmailExisting(string $email): Result { diff --git a/src/Id/Exceptions/CollectionException.php b/src/Id/Exceptions/CollectionException.php new file mode 100644 index 0000000..637070c --- /dev/null +++ b/src/Id/Exceptions/CollectionException.php @@ -0,0 +1,11 @@ +billable->request('PUT', sprintf('v1/addresses/%s', $this->id), $attributes))) + return (new self(fn() => $this->billable->request('PUT', sprintf('v1/addresses/%s', $this->id), $attributes))) ->setBillable($this->billable); } /** * Delete given address from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function delete(): bool { diff --git a/src/Id/Resources/Addresses.php b/src/Id/Resources/Addresses.php index dd3b5f8..2232a05 100644 --- a/src/Id/Resources/Addresses.php +++ b/src/Id/Resources/Addresses.php @@ -3,9 +3,7 @@ namespace Anikeen\Id\Resources; use Anikeen\Id\Concerns\HasBillable; -use Anikeen\Id\Exceptions\RequestRequiresClientIdException; -use Anikeen\Id\Result; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; class Addresses extends BaseCollection { @@ -44,12 +42,11 @@ class Addresses extends BaseCollection *   - email: Email address (optional, e.g. for delivery notifications) *   - primary: Whether this address should be the primary address (optional) *   - primary_billing_address: Whether this address should be the primary billing address (optional) - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function create(array $attributes = []): Address { - return (new Address($this->billable->request('POST', 'v1/addresses', $attributes))) + return (new Address(fn() => $this->billable->request('POST', 'v1/addresses', $attributes))) ->setBillable($this->billable); } @@ -58,31 +55,25 @@ class Addresses extends BaseCollection */ public function find(string $id): ?Address { - $result = $this->billable->request('GET', sprintf('v1/addresses/%s', $id)); - - return $result->success() ? - (new Address($result)) - ->setBillable($this->billable) - : null; + return (new Address(fn() => $this->billable->request('GET', sprintf('v1/addresses/%s', $id)))) + ->setBillable($this->billable); } /** * Get default address from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function defaultBillingAddress(): Address { - return (new Address($this->billable->request('GET', sprintf('v1/addresses/%s', $this->billable->getUserData()->billing_address_id)))) + return (new Address(fn() => $this->billable->request('GET', sprintf('v1/addresses/%s', $this->billable->getUserData()->billing_address_id)))) ->setBillable($this->billable); } /** * Check if the current user has a default billing address. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function hasDefaultBillingAddress(): bool { diff --git a/src/Id/Resources/BaseCollection.php b/src/Id/Resources/BaseCollection.php index 64ad11f..ce982bf 100644 --- a/src/Id/Resources/BaseCollection.php +++ b/src/Id/Resources/BaseCollection.php @@ -2,17 +2,51 @@ namespace Anikeen\Id\Resources; -use Anikeen\Id\Concerns\MagicProperties; +use Anikeen\Id\AnikeenId; +use Anikeen\Id\Exceptions\CollectionException; +use Anikeen\Id\Exceptions\ResourceException; +use Anikeen\Id\Helpers\Paginator; use Anikeen\Id\Result; +use Closure; +use GuzzleHttp\Psr7\Response; use JsonSerializable; +use Throwable; +/** + * @property bool $success + * @property mixed $data + * @property int $total + * @property int $status + * @property null|array $links + * @property null|array $meta + * @property null|Paginator $paginator + * @property AnikeenId $anikeenId + * @property Response $response + * @property null|Throwable $exception + */ abstract class BaseCollection implements JsonSerializable { - use MagicProperties; + private Closure $callable; + public ?Result $result = null; - public function __construct(protected Result $result) + /** + * @throws CollectionException + */ + protected function __construct(callable $callable) { - // + $this->result = $callable(); + + if (!$this->result->success()) { + throw new CollectionException(sprintf('%s for collection [%s]', rtrim($this->result->data->message, '.'), get_called_class()), $this->result->response->getStatusCode()); + } + } + + /** + * @throws CollectionException + */ + public static function builder(callable $callable): static + { + return new static($callable, false); } /** @@ -43,4 +77,14 @@ abstract class BaseCollection implements JsonSerializable * Returns the Resource based on the ID. */ abstract public function find(string $id): ?BaseResource; + + public function __get(string $name) + { + return $this->result->{$name} ?? null; + } + + public function __isset(string $name): bool + { + return isset($this->result->{$name}); + } } \ No newline at end of file diff --git a/src/Id/Resources/BaseResource.php b/src/Id/Resources/BaseResource.php index 1f0cdfb..d6de860 100644 --- a/src/Id/Resources/BaseResource.php +++ b/src/Id/Resources/BaseResource.php @@ -2,17 +2,38 @@ namespace Anikeen\Id\Resources; -use Anikeen\Id\Concerns\MagicProperties; +use Anikeen\Id\Exceptions\ResourceException; use Anikeen\Id\Result; use JsonSerializable; abstract class BaseResource implements JsonSerializable { - use MagicProperties; + public Result $result; - public function __construct(protected Result $result) + /** + * @throws ResourceException + */ + public function __construct(callable $callable) { - $this->setMagicProperties($this->result->data); + $this->result = $callable(); + + if (!$this->result->success()) { + throw new ResourceException(sprintf('%s for resource [%s]', rtrim($this->result->data->message, '.'), get_called_class()), $this->result->response->getStatusCode()); + } + + foreach ($this->result->data as $key => $value) { + if (!property_exists($this, $key)) { + $this->{$key} = $value; + } + } + } + + /** + * Returns the collection of resources as a JSON string. + */ + public function jsonSerialize(): array + { + return $this->toArray(); } /** @@ -23,11 +44,13 @@ abstract class BaseResource implements JsonSerializable return (array)$this->result->data; } - /** - * Returns the collection of resources as a JSON string. - */ - public function jsonSerialize(): array + public function __get(string $name) { - return $this->toArray(); + return null; + } + + public function __isset(string $name): bool + { + return false; } } \ No newline at end of file diff --git a/src/Id/Resources/Invoice.php b/src/Id/Resources/Invoice.php index 323af56..6b82baf 100644 --- a/src/Id/Resources/Invoice.php +++ b/src/Id/Resources/Invoice.php @@ -3,8 +3,7 @@ namespace Anikeen\Id\Resources; use Anikeen\Id\Concerns\HasBillable; -use Anikeen\Id\Exceptions\RequestRequiresClientIdException; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; /** * @property string $id @@ -16,8 +15,7 @@ class Invoice extends BaseResource /** * Get temporary download url from given invoice. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function getInvoiceTemporaryUrl(): string { diff --git a/src/Id/Resources/Invoices.php b/src/Id/Resources/Invoices.php index 24606c7..3d9dc8f 100644 --- a/src/Id/Resources/Invoices.php +++ b/src/Id/Resources/Invoices.php @@ -3,9 +3,6 @@ namespace Anikeen\Id\Resources; use Anikeen\Id\Concerns\HasBillable; -use Anikeen\Id\Exceptions\RequestRequiresClientIdException; -use Anikeen\Id\Result; -use GuzzleHttp\Exception\GuzzleException; class Invoices extends BaseCollection { @@ -16,11 +13,7 @@ class Invoices extends BaseCollection */ public function find(string $id): ?Invoice { - $result = $this->billable->request('GET', sprintf('v1/invoices/%s', $id)); - - return $result->success() - ? (new Invoice($result)) - ->setBillable($this->billable) - : null; + return (new Invoice(fn() => $this->billable->request('GET', sprintf('v1/invoices/%s', $id)))) + ->setBillable($this->billable); } } \ No newline at end of file diff --git a/src/Id/Resources/Order.php b/src/Id/Resources/Order.php index 3189d9a..9a799a0 100644 --- a/src/Id/Resources/Order.php +++ b/src/Id/Resources/Order.php @@ -3,8 +3,7 @@ namespace Anikeen\Id\Resources; use Anikeen\Id\Concerns\HasBillable; -use Anikeen\Id\Exceptions\RequestRequiresClientIdException; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; /** * @property string $id @@ -55,44 +54,40 @@ class Order extends BaseResource * } $attributes The order data: * - billing_address: Billing address (ISO 3166-1 alpha-2 country code) * - shipping_address: Shipping address (first name, last name, ISO 3166-1 alpha-2 country code) - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function update(array $attributes = []): self { - return (new self($this->billable->request('PUT', sprintf('v1/orders/%s', $this->id), $attributes))) + return (new self(fn() => $this->billable->request('PUT', sprintf('v1/orders/%s', $this->id), $attributes))) ->setBillable($this->billable); } /** * Checkout given order from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ - public function checkout(string $orderId): self + public function checkout(): self { - return (new self($this->billable->request('PUT', sprintf('v1/orders/%s/checkout', $this->id)))) + return (new self(fn() => $this->billable->request('PUT', sprintf('v1/orders/%s/checkout', $this->id)))) ->setBillable($this->billable); } /** * Revoke given order from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ - public function revoke(string $orderId): self + public function revoke(): self { - return (new self($this->billable->request('PUT', sprintf('v1/orders/%s/revoke', $this->id)))) + return (new self(fn() => $this->billable->request('PUT', sprintf('v1/orders/%s/revoke', $this->id)))) ->setBillable($this->billable); } /** * Delete given order from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function delete(): bool { @@ -102,12 +97,11 @@ class Order extends BaseResource /** * Get order items from given order. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function orderItems(array $parameters = []): OrderItems { - return (new OrderItems($this->billable->request('GET', sprintf('v1/orders/%s/items', $this->id), [], $parameters))) + return OrderItems::builder(fn() => $this->billable->request('GET', sprintf('v1/orders/%s/items', $this->id), [], $parameters)) ->setBillable($this->billable) ->setParent($this); } diff --git a/src/Id/Resources/OrderItem.php b/src/Id/Resources/OrderItem.php index 2cf0e1b..36308d5 100644 --- a/src/Id/Resources/OrderItem.php +++ b/src/Id/Resources/OrderItem.php @@ -4,8 +4,7 @@ namespace Anikeen\Id\Resources; use Anikeen\Id\Concerns\HasBillable; use Anikeen\Id\Concerns\HasParent; -use Anikeen\Id\Exceptions\RequestRequiresClientIdException; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; /** * @property string $id @@ -31,12 +30,11 @@ class OrderItem extends BaseResource * }> * } $attributes The order data: * - items: Array of order items, each with type, name, description, price, unit, and quantity - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function update(array $attributes = []): self { - return (new self($this->billable->request('PUT', sprintf('v1/orders/%s/items/%s', $this->parent->id, $this->id), $attributes))) + return (new self(fn() => $this->billable->request('PUT', sprintf('v1/orders/%s/items/%s', $this->parent->id, $this->id), $attributes))) ->setBillable($this->billable) ->setParent($this->parent); } @@ -44,8 +42,7 @@ class OrderItem extends BaseResource /** * Delete given order item from given order. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function delete(): bool { diff --git a/src/Id/Resources/OrderItems.php b/src/Id/Resources/OrderItems.php index 49ffbfc..481aa0d 100644 --- a/src/Id/Resources/OrderItems.php +++ b/src/Id/Resources/OrderItems.php @@ -4,9 +4,8 @@ namespace Anikeen\Id\Resources; use Anikeen\Id\Concerns\HasBillable; use Anikeen\Id\Concerns\HasParent; -use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Result; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; class OrderItems extends BaseCollection { @@ -30,12 +29,11 @@ class OrderItems extends BaseCollection * }> * } $attributes The order data: * - items: Array of order items, each with type, name, description, price, unit, and quantity - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function create(string $orderId, array $attributes = []): OrderItem { - return (new OrderItem($this->billable->request('POST', sprintf('v1/orders/%s', $orderId), $attributes))) + return (new OrderItem(fn() => $this->billable->request('POST', sprintf('v1/orders/%s', $orderId), $attributes))) ->setBillable($this->billable) ->setParent($this->parent); } @@ -45,13 +43,8 @@ class OrderItems extends BaseCollection */ public function find(string $id): ?OrderItem { - /** @var Result $result */ - $result = $this->parent->request('GET', sprintf('v1/orders/%s/items/%s', $this->parent->id, $id)); - - return $result->success() ? - (new OrderItem($result)) - ->setBillable($this->billable) - ->setParent($this->parent) - : null; + return (new OrderItem(fn() => $this->parent->request('GET', sprintf('v1/orders/%s/items/%s', $this->parent->id, $id)))) + ->setBillable($this->billable) + ->setParent($this->parent); } } \ No newline at end of file diff --git a/src/Id/Resources/Orders.php b/src/Id/Resources/Orders.php index 24a1440..49e3302 100644 --- a/src/Id/Resources/Orders.php +++ b/src/Id/Resources/Orders.php @@ -3,9 +3,8 @@ namespace Anikeen\Id\Resources; use Anikeen\Id\Concerns\HasBillable; -use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Result; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; class Orders extends BaseCollection { @@ -62,29 +61,22 @@ class Orders extends BaseCollection * - billing_address: Billing address (ISO 3166-1 alpha-2 country code) * - shipping_address: Shipping address (first name, last name, ISO 3166-1 alpha-2 country code) * - items: Array of order items (each with type, name, price, unit, units, and quantity) - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function create(array $attributes = []): Order { - return (new Order($this->billable->request('POST', 'v1/orders', $attributes))) + return (new Order(fn() => $this->billable->request('POST', 'v1/orders', $attributes))) ->setBillable($this->billable); } /** * Get given order from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function find(string $id): ?Order { - /** @var Result $result */ - $result = $this->billable->request('GET', sprintf('v1/orders/%s', $id)); - - return $result->success() - ? (new Order($result)) - ->setBillable($this->billable) - : null; + return (new Order(fn() => $this->billable->request('GET', sprintf('v1/orders/%s', $id)))) + ->setBillable($this->billable); } } \ No newline at end of file diff --git a/src/Id/Resources/PaymentMethods.php b/src/Id/Resources/PaymentMethods.php index ca6f9e0..d894456 100644 --- a/src/Id/Resources/PaymentMethods.php +++ b/src/Id/Resources/PaymentMethods.php @@ -3,65 +3,53 @@ namespace Anikeen\Id\Resources; use Anikeen\Id\Concerns\HasBillable; -use Anikeen\Id\Exceptions\RequestRequiresClientIdException; -use Anikeen\Id\Result; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; class PaymentMethods extends BaseCollection { use HasBillable; - private ?PaymentMethod $cachedDefaultPaymentMethod = null; - /** * Check if current user has at least one payment method. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function hasPaymentMethod(): bool { return $this->result->count() > 0; } - /** - * Get default payment method from the current user. - * - * @throws RequestRequiresClientIdException - * @throws GuzzleException - */ - public function defaultPaymentMethod(): PaymentMethod - { - if ($this->cachedDefaultPaymentMethod === null) { - $this->cachedDefaultPaymentMethod = (new PaymentMethod( - $this->billable->request('GET', 'v1/payment-methods/default') - ))->setBillable($this->billable); - } - - return $this->cachedDefaultPaymentMethod; - } - /** * Check if the current user has a default payment method. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function hasDefaultPaymentMethod(): bool { return $this->defaultPaymentMethod()?->id !== null; } + /** + * Get default payment method from the current user. + * + * @throws Throwable + */ + public function defaultPaymentMethod(): PaymentMethod + { + if (!isset($this->defaultPaymentMethodCache)) { + $this->defaultPaymentMethodCache = (new PaymentMethod(fn() => $this->billable->request('GET', 'v1/payment-methods/default'))) + ->setBillable($this->billable); + } + + return $this->defaultPaymentMethodCache; + } + /** * {@inheritDoc} */ public function find(string $id): ?PaymentMethod { - $result = $this->billable->request('GET', sprintf('v1/payment-methods/%s', $id)); - - return $result->success() - ? (new PaymentMethod($result)) - ->setBillable($this->billable) - : null; + return (new PaymentMethod(fn() => $this->billable->request('GET', sprintf('v1/payment-methods/%s', $id)))) + ->setBillable($this->billable); } } \ No newline at end of file diff --git a/src/Id/Resources/SshKey.php b/src/Id/Resources/SshKey.php index 80b0a6b..52b726a 100644 --- a/src/Id/Resources/SshKey.php +++ b/src/Id/Resources/SshKey.php @@ -3,8 +3,7 @@ namespace Anikeen\Id\Resources; use Anikeen\Id\Concerns\HasParent; -use Anikeen\Id\Exceptions\RequestRequiresClientIdException; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; /** * @property string $id @@ -16,8 +15,7 @@ class SshKey extends BaseResource /** * Deletes a given ssh key for the currently authed user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function delete(): bool { diff --git a/src/Id/Resources/SshKeys.php b/src/Id/Resources/SshKeys.php index 293895d..1934231 100644 --- a/src/Id/Resources/SshKeys.php +++ b/src/Id/Resources/SshKeys.php @@ -3,9 +3,8 @@ namespace Anikeen\Id\Resources; use Anikeen\Id\Concerns\HasParent; -use Anikeen\Id\Exceptions\RequestRequiresClientIdException; use Anikeen\Id\Result; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; class SshKeys extends BaseCollection { @@ -16,12 +15,11 @@ class SshKeys extends BaseCollection * * @param string $publicKey The public key to be added * @param string|null $name The name of the key (optional) - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function create(string $publicKey, ?string $name = null): SshKey { - return (new SshKey($this->parent->post('v1/ssh-keys', [ + return (new SshKey(fn() => $this->parent->post('v1/ssh-keys', [ 'public_key' => $publicKey, 'name' => $name, ])))->setParent($this->parent); @@ -32,12 +30,6 @@ class SshKeys extends BaseCollection */ public function find(string $id): ?SshKey { - /** @var Result $result */ - $result = $this->parent->get(sprintf('v1/ssh-keys/%s', $id)); - - return $result->success() - ? (new SshKey($result)) - ->setParent($this->parent) - : null; + return (new SshKey(fn() => $this->parent->get(sprintf('v1/ssh-keys/%s', $id)))); } } \ No newline at end of file diff --git a/src/Id/Resources/Subscription.php b/src/Id/Resources/Subscription.php index 45ebd75..fa43b5b 100644 --- a/src/Id/Resources/Subscription.php +++ b/src/Id/Resources/Subscription.php @@ -3,8 +3,7 @@ namespace Anikeen\Id\Resources; use Anikeen\Id\Concerns\HasBillable; -use Anikeen\Id\Exceptions\RequestRequiresClientIdException; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; /** * @property string $id @@ -26,6 +25,7 @@ class Subscription extends BaseResource * Update a given subscription from the current user. * * @param array{ + * group: string, * name: string, * description: null|string, * unit: string, @@ -36,6 +36,7 @@ class Subscription extends BaseResource * webhook_url: null|string, * webhook_secret: null|string * } $attributes The subscription data: + * - group: The group (optional) * - name: The name (required when set) * - description: The description (optional) * - unit: The unit (required when set, e.g. "hour", "day", "week", "month", "year") @@ -45,56 +46,62 @@ class Subscription extends BaseResource * - ends_at: The end date (optional) * - webhook_url: The webhook URL (optional) * - webhook_secret: The webhook secret (optional) - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function update(array $attributes): self { - return (new self($this->billable->request('PUT', sprintf('v1/subscriptions/%s', $this->id), $attributes))) + return (new self(fn() => $this->billable->request('PUT', sprintf('v1/subscriptions/%s', $this->id), $attributes))) ->setBillable($this->billable); } /** * Force given subscription to check out (trusted apps only). * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function checkout(): self { - return (new self($this->billable->request('PUT', sprintf('v1/subscriptions/%s/checkout', $this->id)))) + return (new self(fn() => $this->billable->request('PUT', sprintf('v1/subscriptions/%s/checkout', $this->id)))) ->setBillable($this->billable); } /** * Revoke a given running subscription from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function revoke(): self { - return (new self($this->billable->request('PUT', sprintf('v1/subscriptions/%s/revoke', $this->id)))) + return (new self(fn() => $this->billable->request('PUT', sprintf('v1/subscriptions/%s/revoke', $this->id)))) + ->setBillable($this->billable); + } + + /** + * Pause a given running subscription from the current user. + * + * @throws Throwable + */ + public function pause(): self + { + return (new self(fn() => $this->billable->request('PUT', sprintf('v1/subscriptions/%s/pause', $this->id)))) ->setBillable($this->billable); } /** * Resume a given running subscription from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function resume(): self { - return (new self($this->billable->request('PUT', sprintf('v1/subscriptions/%s/resume', $this->id)))) + return (new self(fn() => $this->billable->request('PUT', sprintf('v1/subscriptions/%s/resume', $this->id)))) ->setBillable($this->billable); } /** * Delete a given subscription from the current user. * - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function delete(): bool { diff --git a/src/Id/Resources/Subscriptions.php b/src/Id/Resources/Subscriptions.php index 8933707..f3ba0fc 100644 --- a/src/Id/Resources/Subscriptions.php +++ b/src/Id/Resources/Subscriptions.php @@ -3,8 +3,7 @@ namespace Anikeen\Id\Resources; use Anikeen\Id\Concerns\HasBillable; -use Anikeen\Id\Exceptions\RequestRequiresClientIdException; -use GuzzleHttp\Exception\GuzzleException; +use Throwable; class Subscriptions extends BaseCollection { @@ -14,6 +13,7 @@ class Subscriptions extends BaseCollection * Create a new subscription for the current user. * * @param array{ + * group: null|string, * name: string, * description: null|string, * unit: string, @@ -24,6 +24,7 @@ class Subscriptions extends BaseCollection * webhook_url: null|string, * webhook_secret: null|string * } $attributes The subscription data: + * - group: The group (optional) * - name: The name * - description: The description (optional) * - unit: The unit (e.g. "hour", "day", "week", "month", "year") @@ -33,12 +34,11 @@ class Subscriptions extends BaseCollection * - ends_at: The end date (optional) * - webhook_url: The webhook URL (optional) * - webhook_secret: The webhook secret (optional) - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable */ public function create(array $attributes): Subscription { - return (new Subscription($this->billable->request('POST', 'v1/subscriptions', $attributes))) + return (new Subscription(fn() => $this->billable->request('POST', 'v1/subscriptions', $attributes))) ->setBillable($this->billable); } @@ -47,11 +47,7 @@ class Subscriptions extends BaseCollection */ public function find(string $id): ?Subscription { - $result = $this->billable->request('GET', sprintf('v1/subscriptions/%s', $id)); - - return $result->success() - ? (new Subscription($result)) - ->setBillable($this->billable) - : null; + return (new Subscription(fn() => $this->billable->request('GET', sprintf('v1/subscriptions/%s', $id)))) + ->setBillable($this->billable); } } \ No newline at end of file diff --git a/src/Id/Resources/Transactions.php b/src/Id/Resources/Transactions.php index 6846fa1..6a2e285 100644 --- a/src/Id/Resources/Transactions.php +++ b/src/Id/Resources/Transactions.php @@ -3,9 +3,8 @@ namespace Anikeen\Id\Resources; use Anikeen\Id\Concerns\HasBillable; -use Anikeen\Id\Exceptions\RequestRequiresClientIdException; -use Anikeen\Id\Result; -use GuzzleHttp\Exception\GuzzleException; +use Anikeen\Id\Exceptions\ResourceException; +use Throwable; class Transactions extends BaseCollection { @@ -15,13 +14,13 @@ class Transactions extends BaseCollection * Create a new transaction for the current user. * * @param array $attributes The attributes for the transaction. - * @throws RequestRequiresClientIdException - * @throws GuzzleException + * @throws Throwable * @todo Add type hinting for the attributes array. */ public function create(array $attributes = []): Transaction { - return new Transaction($this->billable->request('POST', 'v1/transactions', $attributes)); + return (new Transaction($this->billable->request('POST', 'v1/transactions', $attributes))) + ->setBillable($this->billable); } /** @@ -29,11 +28,7 @@ class Transactions extends BaseCollection */ public function find(string $id): ?Transaction { - $result = $this->billable->request('GET', sprintf('v1/transactions/%s', $id)); - - return $result->success() - ? (new Transaction($result)) - ->setBillable($this->billable) - : null; + return (new Transaction(fn() => $this->billable->request('GET', sprintf('v1/transactions/%s', $id)))) + ->setBillable($this->billable); } } \ No newline at end of file