> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dezerx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Building Modules

> Everything in DezerX that touches money, servers or logins is a module. This is how you write one.

## Start here

<Steps>
  <Step title="Scaffold it">
    ```bash theme={null}
    php artisan make:module AcmePanel --type=service
    ```

    `--type` takes `service`, `gateway`, `provider` or `addon`. Leave it off and the command asks.
  </Step>

  <Step title="Switch it on">
    ```bash theme={null}
    php artisan module:enable AcmePanelService
    php artisan module:migrate AcmePanelService
    ```
  </Step>

  <Step title="Check it">
    ```bash theme={null}
    php artisan module:doctor AcmePanelService
    ```
  </Step>
</Steps>

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.

<Note>
  The generator runs `composer dump-autoload` for you and leaves the module **disabled**, which is
  the safe default. Read [Enabling a module](/extensions/tutorials/fundamentals#enabling-a-module) before you use
  `--enable`, because php-fpm caches the autoloader and there is a timing trap.
</Note>

## Which type do I want?

<CardGroup cols={2}>
  <Card title="Service modules" icon="server" href="/modules/creating-a-service-module">
    Something people buy that you have to create: a game server, VPS, hosting account, domain or
    licence key.
  </Card>

  <Card title="Payment gateways" icon="credit-card" href="/modules/creating-a-gateway-module">
    Taking payments through a new provider. Core already knows how to turn a payment into an invoice
    or a service.
  </Card>

  <Card title="OAuth providers" icon="fingerprint" href="/modules/creating-an-oauth-provider-module">
    Sign in with Discord, Google, GitHub and friends. The smallest module type.
  </Card>

  <Card title="Addon modules" icon="sparkles" href="/modules/creating-an-addon-module">
    Anything else: affiliates, blogs, forms, compliance tooling, status pages.
  </Card>
</CardGroup>

## The other pages

<CardGroup cols={2}>
  <Card title="Fundamentals" icon="book-open" href="/extensions/tutorials/fundamentals">
    The parts every module shares: manifest, providers, routes, permissions, settings UI,
    credentials, events. Read it once and you can read any module in the tree.
  </Card>

  <Card title="Conventions reference" icon="table-list" href="/modules/reference">
    Exact class names, method signatures, what calls what. Keep it open while you work.
  </Card>
</CardGroup>

## Five things that will bite you

<AccordionGroup>
  <Accordion title="Class names are the contract" icon="signature">
    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.
  </Accordion>

  <Accordion title="Run composer dump-autoload after adding a class" icon="arrows-rotate">
    Some modules autoload through a classmap rather than PSR-4, and classmaps are static. Your new
    file stays invisible until you dump.

    Anything from `make:module` lives under `app/`, so PSR-4 covers it and you won't hit this.
  </Accordion>

  <Accordion title="Never swallow a provisioning failure" icon="triangle-exclamation">
    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.
  </Accordion>

  <Accordion title="Don't add the encrypted cast to a column that already has data" icon="lock">
    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](/extensions/tutorials/fundamentals#credentials).
  </Accordion>

  <Accordion title="Check the app still works when your module is disabled" icon="toggle-off">
    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.
  </Accordion>
</AccordionGroup>

## Commands

| Command                                        | What it does                                |
| ---------------------------------------------- | ------------------------------------------- |
| `php artisan make:module {Name} --type={type}` | Scaffold a new module                       |
| `php artisan module:doctor [{Name}]`           | Validate manifests, packaging and contracts |
| `php artisan module:list`                      | What exists, and what's enabled             |
| `php artisan module:enable {Name}`             | Turn a module on                            |
| `php artisan module:migrate {Name}`            | Run its migrations                          |
| `vendor/bin/pest`                              | The test suite                              |

<Tip>
  `make:module` also accepts `--webhook` for gateways and `--enable` to switch the module on and
  migrate it in one go.
</Tip>
