NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Purchases for Laravel

DI and actions

There are three equivalent entry points, and all three run the same code. The facade is the shortest and the recommended default. RoundlyConsulting\Purchases\PurchasesManager — the facade’s root, a container singleton — gives you the identical API as an explicit constructor dependency, with no static calls. And every persisting method is a thin call into a single-purpose action with an execute() method, for composing into your own jobs, commands or actions:

use RoundlyConsulting\Purchases\Actions\HandleProviderResultAction;
use RoundlyConsulting\Purchases\Actions\ReplayProviderNotificationAction;
use RoundlyConsulting\Purchases\Actions\SyncProviderResultAction;
use RoundlyConsulting\Purchases\PurchasesManager;

final class GrantEntitlement
{
    public function __construct(private PurchasesManager $purchases) {}

    public function __invoke(ProviderResult $result, User $user): bool
    {
        $this->purchases->sync($result);

        return $this->purchases->for($user)->subscribedTo('pro');
    }
}

// The raw actions — the same code the manager calls:
app(SyncProviderResultAction::class)->execute($result);                // = Purchases::sync()
app(ReplayProviderNotificationAction::class)->execute($notification);  // = Purchases::replay()
app(HandleProviderResultAction::class)->execute(                       // = Purchases::handle()
    app(PurchasesManager::class)->result('stripe', $request),
);

Facade method → action

Facade methodActionTakes
handle()HandleProviderResultActionThe verified ProviderResult — call result() first.
sync()SyncProviderResultActionProviderResult
replay()ReplayProviderNotificationActionA PurchaseNotification or its id.
result(), for(), provider(), providers(), has(), ids()—No action — provider verification, lookups and read-only queries.

Purchases::fake() swaps PurchasesManager in the container as well as behind the facade. The webhook controller and the purchases:providers, purchases:verify and purchases:replay commands inject the manager, so the fake reaches them — and your own injected services — too.

Internal building blocks

RecordProviderResultAction, RecordPurchaseAction, RecordSubscriptionAction, RecordRefundAction and RecordProviderNotificationAction are @internal building blocks of handle() and sync(). Don’t call them from application code — go through the facade, the manager or the three public actions above, which also own the audit row and the events.

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

By donating, you agree to our donation terms.

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.