---
title: "DI and actions — Purchases for Laravel | Roundly"
description: "Inject PurchasesManager instead of calling the facade, or run the action behind handle(), sync() or replay() — the same code either way."
url: https://roundly-consulting.com/open-source/docs/purchases-for-laravel/without-facade
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)

# 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:

```php
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 method | Action | Takes |
| --- | --- | --- |
| `handle()` | `HandleProviderResultAction` | The verified ProviderResult — call result() first. |
| `sync()` | `SyncProviderResultAction` | `ProviderResult` |
| `replay()` | `ReplayProviderNotificationAction` | A 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](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)
