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
- Building a payment gateway plugin
- Other plugin types
- Plugin development (the full list of extension points)
- Developer guide