WHBMP

Building a plugin

Oct 25, 2026

Start in one command

php artisan whbmp:make-plugin my-addon --name="My Addon"
php artisan whbmp:make-plugin acme-pay --type=gateway --name="Acme Pay"

This creates plugins/<slug>/ with plugin.json, the main class, a README, a CHANGELOG and a test. Activate an addon on the Plugins page. Before a release run:

php artisan whbmp:plugin-check my-addon

It checks that the manifest loads, that every class the plugin provides exists and has the right interface, and that the version is in CHANGELOG.md.

The manifest

plugin.json is the one place the name, description and version are written. Raise version there for a release and add the same version to CHANGELOG.md. requires.whbmp is the oldest WHBMP the plugin works with. provides maps a plugin type to the class that implements it (addon, payment_gateway, domain_registrar, server, compute, ai_provider, fraud_check, captcha). Every class must live in the plugin's namespace (WhbmpPlugins\YourName).

An addon in a few lines

Extend App\Plugins\AbstractAddon and return the slug:

class MyAddon extends AbstractAddon
{
    public static function slug(): string { return 'my-addon'; }
}

Name, description and version come from plugin.json. Put tables in database/migrations/; they run when the addon is installed. Override install(), activate() or deactivate() only if you need to act at those moments.

Settings. MyAddon::setting('key') and setSetting('key', $value) keep plain values; secret('key') and setSecret('key', $value) keep encrypted ones. They are stored under the plugin's own prefix, and saving a secret field left blank keeps what was stored.

Texts. Wrap every text in __('…') and ship lang/es.json, lang/de.json, lang/fr.json and lang/ar.json in the plugin folder.

Where a plugin can add to WHBMP

Register these from a service provider (named in plugin.json as provider) with App\Plugins\Extensions. Every one is active only while the plugin is.

Method What it adds
adminMenu() A row in the admin sidebar (under Plugins by default)
clientMenu() A row in the client area menu
slot() A view shown at a named place on an existing page
adminClientTab() A tab on the admin client profile
productTab(), productSettings() A tab or settings on the product editor
productType() A new kind of product, optionally with its own service module
serviceObserver() Code that runs when a service is created, changed or ended
emailTemplates() Email templates, placeholders and sample data
dashboardTile(), settingsTile() A tile on the admin dashboard or the settings page
apiDetail() An extra block in an API resource
messageChannel() A way to send messages (SMS, chat)
knowledgebase() The knowledgebase source
loginChallenge() An extra step at sign-in (two-factor)
legacyPasswordVerifier() Accept the old password hashes of a platform you migrate from

The Projects plugin (plugins/projects) uses most of these and is a good example to read.

Test and release it

See Testing and releasing a plugin: the ready-made test setup, whbmp:plugin-check, and how to give your plugin to others.

Related

Powered by WHBMP