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

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:

PlanItem quantities

When a plan has PlanItems (composable add-ons), pass a plan_items map to set per-item quantities at subscription time:
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:
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:

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:

Named subscriptions (multi-slot)

A user can hold multiple concurrent subscriptions by using different slot names:
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:

Webhook-activated gateways (Paddle)

Paddle returns an Incomplete subscription with a checkout_url in meta. Redirect the user to complete payment:
SubscriptionCreated fires from the webhook handler once Paddle confirms payment, not from CreateSubscription.

Listening to the SubscriptionCreated event

Register in AppServiceProvider or a dedicated provider:

Testing

Use the fake gateway in tests. It processes subscriptions synchronously without any HTTP calls:
See Testing for the full FakeGateway setup.

Next steps