Skip to main content

Start here

1

Scaffold it

--type takes service, gateway, provider or addon. Leave it off and the command asks.
2

Switch it on

DezerX runs on Laravel Octane, so the site keeps serving the old module list until the workers reload. Switching the module on from Admin, Modules instead runs the migrations and reloads the workers for you.
3

Check it

You now have a working module: an admin settings page, routes, permissions, a credentials table, and stubs for everything core will call. Search it for TODO to find what’s left to fill in.
The generator runs composer dump-autoload for you and leaves the module disabled, which is the safe default. Read Enabling a module before you use --enable. Octane workers hold the autoloader in memory, and there’s a timing trap.

Which type do I want?

Service modules

Something people buy that you have to create: a game server, VPS, hosting account, domain or licence key.

Payment gateways

Taking payments through a new provider. Core already knows how to turn a payment into an invoice or a service.

OAuth providers

Sign in with Discord, Google, GitHub and friends. The smallest module type.

Addon modules

Anything else: affiliates, blogs, forms, compliance tooling, status pages.
Want to change how the customer area looks rather than what it does? That’s a theme, not a module.

The other pages

Fundamentals

The parts every module shares: manifest, providers, routes, permissions, settings UI, credentials, events, and the rules for running under Octane.

Conventions reference

Exact class names, method signatures, what calls what. Keep it open while you work.

Installing a module

Got a module from the marketplace or a colleague? Where to put it and what to run.

Running under Octane

Why your change isn’t live yet, and the code patterns that leak between requests.

Six things that will bite you

Core finds your code by building a string like Modules\AcmePanel\Services\RenewService. Spell it wrong and nothing happens. No error, no warning, the feature just doesn’t exist.php artisan module:doctor is how you catch that.
Octane boots the app once per worker and reuses it for thousands of requests. Routes, providers, the module list and every PHP class stay in memory until the workers reload, so an edit you just saved isn’t running yet.
The same goes for anything that runs in a queue worker (provisioning, queued listeners): run php artisan queue:restart too.And because a worker outlives the request, state you put in a static property or a singleton leaks to the next customer. See Running under Octane.
Some modules autoload through a classmap rather than PSR-4, and classmaps are static. Your new file stays invisible until you dump, then reload Octane.Anything from make:module lives under app/, so PSR-4 covers it and you won’t hit this.
If the customer has paid and the server didn’t get created, throw an exception. Throwing gets the queued job retried. Returning ['success' => false] and logging a warning means nobody finds out until they complain.This is the most expensive class of bug in the product.
The existing credential models encrypt with Crypt::encryptString(). Laravel’s encrypted cast uses Crypt::encrypt(), which serialises. They are not compatible, so adding the cast makes every stored credential unreadable.New columns are fine. See Credentials.
Meaning: check the rest of the app does. Nothing in core should depend on your tables, listeners or routes.That’s the step people skip and the one that breaks production.

Commands

make:module also accepts --webhook for gateways and --enable to switch the module on and migrate it in one go. You still need php artisan octane:reload afterwards.