Skip to main content

Creating Subscriptions

Subscriptions are created using the CreateSubscription action. Inject it via the service container or resolve it with app().

Basic subscription

use Pixelworxio\Subscribd\Actions\CreateSubscription;
use Pixelworxio\Subscribd\Models\Plan;

$plan = Plan::where('key', 'pro')->firstOrFail();

app(CreateSubscription::class)->execute($user, $plan);
This creates the subscription record, creates or retrieves the customer at the gateway, stores the payment method, issues the first invoice, and dispatches the SubscriptionCreated event.

Subscription options

Pass an options array as the third argument:
app(CreateSubscription::class)->execute($user, $plan, [
    'name'     => 'default',    // subscription slot name, defaults to 'default'
    'gateway'  => 'stripe',     // override the default gateway
    'coupon'   => 'SAVE20',     // apply a coupon code at checkout
]);

PlanItem quantities

When a plan has PlanItems (composable add-ons), pass a plan_items map to set per-item quantities at subscription time:
app(CreateSubscription::class)->execute($user, $plan, [
    'plan_items' => [
        'extra_projects' => 3,
        'api_add_on'     => 1,
    ],
]);
Omitted keys default to the PlanItem’s included_quantity.

Trials

If the plan defines trial_days, the trial starts automatically. No payment is collected until the trial ends. To start a trial without collecting a payment method upfront, set trial_requires_payment_method to false on the plan:
// config/subscribd.php
'plans' => [
    'pro' => [
        'trial_days'                      => 14,
        'trial_requires_payment_method'   => false,
        // ...
    ],
],
With this setting, CreateSubscription skips the gateway entirely. A gateway_id is assigned later when ConvertTrialToActive runs. See Trial Management for the full trial lifecycle.

Gateway selection

Subscriptions use the default gateway from config unless overridden:
// Use PayPal instead of the default gateway
app(CreateSubscription::class)->execute($user, $plan, ['gateway' => 'paypal']);

Zero-cost plans

Plans with amount = 0 are automatically routed to the null gateway — no payment method is required and no API call is made:
$freePlan = Plan::where('key', 'free')->firstOrFail();
app(CreateSubscription::class)->execute($user, $freePlan);  // no gateway call

Named subscriptions (multi-slot)

A user can hold multiple concurrent subscriptions by using different slot names:
$individualPlan = Plan::where('key', 'pro')->firstOrFail();
$teamPlan       = Plan::where('key', 'team-pro')->firstOrFail();

app(CreateSubscription::class)->execute($user, $individualPlan, ['name' => 'individual']);
app(CreateSubscription::class)->execute($user, $teamPlan,       ['name' => 'team']);

// Access each slot
$user->subscription('individual');
$user->subscription('team');
See Multiple Subscriptions for the full multi-slot pattern.

Handling gateway exceptions

For gateways that require Strong Customer Authentication (SCA/3DS), CreateSubscription may throw RequiresActionException:
use Pixelworxio\Subscribd\Exceptions\RequiresActionException;
use Pixelworxio\Subscribd\Exceptions\SubscriptionException;

try {
    $subscription = app(CreateSubscription::class)->execute($user, $plan);
} catch (RequiresActionException $e) {
    // Redirect the user to complete 3DS confirmation
    return redirect($e->redirectUrl());
} catch (SubscriptionException $e) {
    // Gateway rejected the subscription (e.g. Braintree with interval_count > 1)
    report($e);
    return back()->withErrors(['billing' => $e->getMessage()]);
}

Webhook-activated gateways (Paddle)

Paddle returns an Incomplete subscription with a checkout_url in meta. Redirect the user to complete payment:
$subscription = app(CreateSubscription::class)->execute($user, $plan, ['gateway' => 'paddle']);

if ($subscription->status->value === 'incomplete') {
    return redirect($subscription->meta['checkout_url']);
}
SubscriptionCreated fires from the webhook handler once Paddle confirms payment, not from CreateSubscription.

Listening to the SubscriptionCreated event

use Pixelworxio\Subscribd\Events\SubscriptionCreated;

class SendWelcomeEmail
{
    public function handle(SubscriptionCreated $event): void
    {
        $billable = $event->subscription->billable;
        $plan     = $event->subscription->plan;

        Mail::to($billable->billingEmail())->send(new WelcomeMail($plan));
    }
}
Register in AppServiceProvider or a dedicated provider:
Event::listen(SubscriptionCreated::class, SendWelcomeEmail::class);

Testing

Use the fake gateway in tests. It processes subscriptions synchronously without any HTTP calls:
use Pixelworxio\Subscribd\Actions\CreateSubscription;
use Pixelworxio\Subscribd\Models\Plan;

it('subscribes a user to the pro plan', function () {
    $user = User::factory()->create();
    $plan = Plan::factory()->create(['key' => 'pro', 'amount' => 4900]);

    app(CreateSubscription::class)->execute($user, $plan);

    expect($user->subscribed())->toBeTrue();
    expect($user->subscription()->plan->key)->toBe('pro');
});
See Testing for the full FakeGateway setup.

Next steps