---
title: "Enums & exceptions — Purchases for Laravel | Roundly"
description: "The Status and ResultType enums with select-option and validation helpers, and the package’s exception types."
url: https://roundly-consulting.com/open-source/docs/purchases-for-laravel/enums-exceptions
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Purchases for Laravel](https://roundly-consulting.com/open-source/docs/purchases-for-laravel.md)

# Enums & exceptions

Status is the normalized status stored on purchases and subscriptions:

| Case | Value | Keeps access |
| --- | --- | --- |
| `New` | `new` | No |
| `Pending` | `pending` | No |
| `Processing` | `processing` | No |
| `Completed` | `completed` | Yes |
| `Failed` | `failed` | No |
| `Canceled` | `canceled` | No |
| `InGracePeriod` | `in_grace` | Yes |
| `OnHold` | `on_hold` | No |
| `Refunded` | `refunded` | No |

Status::isActive() is true only for Completed and InGracePeriod — the states that keep an entitlement. It drives Subscription::isActive() and the active() scope.

## ResultType

| Case | Value | Meaning |
| --- | --- | --- |
| `Purchase` | `purchase` | A one-off purchase — recorded as a Purchase. |
| `Subscription` | `subscription` | A subscription — recorded as a Subscription. |
| `Refund` | `refund` | A refund or chargeback — recorded as a PurchaseRefund. |
| `Notification` | `notification` | A store notification without a transaction. |
| `Unknown` | `unknown` | An unrecognised payload. |

## Select options & validation

Both enums use the Helpers trait from enums-for-laravel, so admin filters and forms need no bespoke arrays or language files:

```php
use RoundlyConsulting\Purchases\Enum\Status;

Status::options();          // list of {value, label, name} option DTOs for select inputs
Status::toOptions();        // ['completed' => 'Completed', 'in_grace' => 'In Grace', …]
Status::labels();           // human strings incl. "In Grace" / "On Hold" (no lang files)
Status::validationRule();   // "in:new,pending,processing,completed,failed,canceled,in_grace,on_hold,refunded"

$status = Status::tryFromLabel('On Hold');   // Status::OnHold
$status?->isActive();                        // false — an account hold keeps no access
```

## Exceptions

| Exception | Thrown when |
| --- | --- |
| `VerificationException` | A signature or credential check fails — including an Apple certificate outside its validity window — or a client sends bad input to a provider’s callback(): a malformed id, or one the store rejects with a 4xx (getPrevious() keeps the store’s RequestException). The webhook route answers 400. |
| `InvalidConfigurationException` | A config value is unusable — certificate\_clock\_skew outside 0–3600, a duration that isn’t a whole number, a URL or queue name that isn’t a string, a provider that doesn’t implement Provider. Deliberately not a VerificationException — a misconfigured host is not a forged notification. |
| `UnknownProviderException` | Resolving a provider id that isn’t registered. |
| `InvalidProviderNotificationException` | A notification payload can’t be decoded — or replay() refuses a notification (unsaved or soft-deleted, a provider notification that failed verification, or no longer rebuildable). |

All extend RoundlyConsulting\\Purchases\\Exceptions\\Exception, which adds a because(string $message, int $code = 0) named constructor:

```php
use RoundlyConsulting\Purchases\Exceptions\UnknownProviderException;
use RoundlyConsulting\Purchases\Exceptions\VerificationException;
use RoundlyConsulting\Purchases\Facades\Purchases;

try {
    $result = Purchases::result('stripe', $request);
} catch (VerificationException $e) {
    abort(400, $e->getMessage());
} catch (UnknownProviderException) {
    abort(404);
}
```

Config typos caught by the package toolkit are the exception: an unparseable on/off switch (push authentication aside), a key\_type outside bigint, uuid and ulid, and a models.\* class that isn’t the packaged model or a subclass of it throw RoundlyConsulting\\PackageToolkit\\Exceptions\\InvalidConfigurationException, which doesn’t extend the package’s base exception. Like the package’s own InvalidConfigurationException, it names the key.

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/purchases-for-laravel.md)
