Home / Developer Reference / Minimal Module Examples

Minimal Module Examples

Copy-paste gateway, server, registrar, SMS, addon starters

Start here for module development

Copy-paste these templates to scaffold real work. Each example is minimal but functional. Drop into the appropriate modules/ folder, enable in Admin, and extend.

1. Minimal gateway module

Place in modules/gateways/demogateway/. Add module.json and DemogatewayGateway.php.

// module.json
{
  "name": "demogateway",
  "display_name": "Demo Gateway",
  "description": "Minimal gateway example",
  "version": "1.0.0",
  "author": "Your Company"
}

// DemogatewayGateway.php
<?php
require_once ROOT_PATH . '/core/ModuleInterface.php';

class DemogatewayGateway implements ModuleInterface {
    private $config = [];
    private $enabled = false;

    public function getName(): string { return 'demogateway'; }
    public function getType(): string { return 'gateway'; }
    public function getDisplayName(): ?string { return 'Demo Gateway'; }
    public function getConfigurableOptionKeys(): array { return []; }
    public function getConfigurableOptionDefaults(string $optionKey): array { return []; }

    public function getConfigFields(): array {
        return [
            ['name' => 'api_key', 'label' => 'API Key', 'type' => 'password', 'required' => true],
            ['name' => 'test_mode', 'label' => 'Test Mode', 'type' => 'checkbox', 'required' => false]
        ];
    }

    public function initialize(array $config): bool {
        $this->config = $config;
        $this->enabled = !empty($config['api_key']);
        return $this->enabled;
    }

    public function isEnabled(): bool { return $this->enabled; }

    public function execute($action, $params = []) {
        switch ($action) {
            case 'process':
                return ['success' => true, 'type' => 'redirect', 'url' => 'https://example.com/pay?invoice=' . ($params['invoice']['id'] ?? 0)];
            default:
                return ['success' => false, 'error' => 'Unknown action'];
        }
    }
}

2. Minimal server module

Place in modules/servers/demoserver/. Implements create, suspend, unsuspend, terminate, test_connection, list_packages.

// DemoserverServer.php
<?php
require_once ROOT_PATH . '/core/ModuleInterface.php';

class DemoserverServer implements ModuleInterface {
    private $config = [];
    private $enabled = false;

    public function getName(): string { return 'demoserver'; }
    public function getType(): string { return 'server'; }
    public function getDisplayName(): ?string { return 'Demo Server'; }
    public function getConfigurableOptionKeys(): array { return []; }
    public function getConfigurableOptionDefaults(string $optionKey): array { return []; }

    public function getConfigFields(): array {
        return [
            ['name' => 'hostname', 'label' => 'Hostname', 'type' => 'text', 'required' => true],
            ['name' => 'api_key', 'label' => 'API Key', 'type' => 'password', 'required' => true]
        ];
    }

    public function initialize(array $config): bool {
        $this->config = $config;
        $this->enabled = !empty($config['hostname']) && !empty($config['api_key']);
        return $this->enabled;
    }

    public function isEnabled(): bool { return $this->enabled; }

    public function execute($action, $params = []) {
        switch ($action) {
            case 'create':
                return ['success' => true, 'username' => $params['username'] ?? 'user', 'domain' => $params['domain'] ?? ''];
            case 'suspend':
            case 'unsuspend':
                return ['success' => true, 'message' => 'Done'];
            case 'terminate':
                return ['success' => true, 'message' => 'Terminated'];
            case 'test_connection':
                return ['success' => true, 'message' => 'Connected'];
            case 'list_packages':
                return ['success' => true, 'packages' => [['id' => 1, 'name' => 'Basic']]];
            default:
                return ['success' => false, 'error' => 'Unknown action: ' . $action];
        }
    }
}

3. Minimal registrar module

Place in modules/registrars/demoregistrar/. Implements check_availability, register, renew, get_info, update_nameservers.

// DemoregistrarRegistrar.php
<?php
require_once ROOT_PATH . '/core/ModuleInterface.php';

class DemoregistrarRegistrar implements ModuleInterface {
    private $config = [];
    private $enabled = false;

    public function getName(): string { return 'demoregistrar'; }
    public function getType(): string { return 'registrar'; }
    public function getDisplayName(): ?string { return 'Demo Registrar'; }
    public function getConfigurableOptionKeys(): array { return []; }
    public function getConfigurableOptionDefaults(string $optionKey): array { return []; }

    public function getConfigFields(): array {
        return [
            ['name' => 'username', 'label' => 'Username', 'type' => 'text', 'required' => true],
            ['name' => 'api_token', 'label' => 'API Token', 'type' => 'password', 'required' => true]
        ];
    }

    public function initialize(array $config): bool {
        $this->config = $config;
        $this->enabled = !empty($config['username']) && !empty($config['api_token']);
        return $this->enabled;
    }

    public function isEnabled(): bool { return $this->enabled; }

    public function execute($action, $params = []) {
        switch ($action) {
            case 'check_availability':
                return ['success' => true, 'available' => true];
            case 'register':
                return ['success' => true, 'domain' => $params['domain'] ?? ''];
            case 'renew':
                return ['success' => true, 'expiry' => date('Y-m-d', strtotime('+1 year'))];
            case 'get_info':
                return ['success' => true, 'expiry' => date('Y-m-d'), 'nameservers' => []];
            case 'update_nameservers':
                return ['success' => true];
            default:
                return ['success' => false, 'error' => 'Unknown action: ' . $action];
        }
    }
}

4. Minimal SMS provider module

Place in modules/sms_providers/demosms/. Implements send action with to and message params.

// module.json
{
  "name": "demosms",
  "display_name": "Demo SMS",
  "description": "Minimal SMS provider example",
  "version": "1.0.0",
  "author": "Your Company"
}

// DemosmsSmsProvider.php
<?php
require_once ROOT_PATH . '/core/ModuleInterface.php';

class DemosmsSmsProvider implements ModuleInterface {
    private $config = [];
    private $enabled = false;

    public function getName(): string { return 'demosms'; }
    public function getType(): string { return 'sms_provider'; }
    public function getDisplayName(): ?string { return 'Demo SMS'; }
    public function getConfigurableOptionKeys(): array { return []; }
    public function getConfigurableOptionDefaults(string $optionKey): array { return []; }

    public function getConfigFields(): array {
        return [
            ['name' => 'api_key', 'label' => 'API Key', 'type' => 'password', 'required' => true],
            ['name' => 'from', 'label' => 'From (Sender)', 'type' => 'text', 'required' => true]
        ];
    }

    public function initialize(array $config): bool {
        $this->config = $config;
        $this->enabled = !empty($config['api_key']) && !empty($config['from']);
        return $this->enabled;
    }

    public function isEnabled(): bool { return $this->enabled; }

    public function execute($action, $params = []) {
        switch ($action) {
            case 'send':
                $to = $params['to'] ?? '';
                $message = $params['message'] ?? '';
                if (empty($to) || empty($message)) {
                    return ['success' => false, 'message_id' => null, 'error' => 'Missing to or message'];
                }
                // Call your SMS API here; return success with message_id
                return ['success' => true, 'message_id' => 'demo_' . time(), 'error' => null];
            default:
                return ['success' => false, 'error' => 'Unknown action'];
        }
    }
}

5. Minimal addon (client area – hooks only)

Place in modules/addons/demoaddon/. Uses hooks to run code in the client area and react to events. No admin page.

// module.json
{
  "name": "demoaddon",
  "display_name": "Demo Addon",
  "description": "Minimal addon – client area hooks",
  "version": "1.0.0",
  "author": "Your Company"
}

// hooks.php
<?php
require_once ROOT_PATH . '/core/HookManager.php';

HookManager::addAction('slack_invoice_created', function($data) {
    $invoiceId = $data['invoice_id'] ?? 0;
    $clientId = $data['client_id'] ?? 0;
    // Your logic: send to Slack, sync to CRM, etc.
}, 10);

HookManager::addAction('client_footer', function() {
    echo '<!-- Demo addon loaded -->';
}, 10);

Enable in Admin → Addons. See Hooks Reference for all available hooks.

6. Minimal addon (admin area – with admin page)

Place in modules/addons/demoadmin/. Adds a config page at /admin/demoadmin. Use admin_url in module.json and create admin/demoadmin.php.

// module.json
{
  "name": "demoadmin",
  "display_name": "Demo Admin Addon",
  "description": "Minimal addon with admin config page",
  "version": "1.0.0",
  "author": "Your Company",
  "admin_url": "/admin/demoadmin"
}

// admin/demoadmin.php (path: modules/addons/demoadmin/admin/demoadmin.php)
<?php
require_once ROOT_PATH . '/includes/bootstrap.php';

$auth = new Auth();
requireStaffAccess('settings');

$db = Database::getInstance();
$module = $db->query("SELECT * FROM modules WHERE name = 'demoadmin' AND type = 'addon'")->fetch_assoc();
$config = $module ? json_decode($module['config_json'] ?? '{}', true) : [];

if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save') {
    $newConfig = ['api_key' => trim($_POST['api_key'] ?? '')];
    $configJson = json_encode($newConfig);
    if ($module) {
        $stmt = $db->prepare("UPDATE modules SET config_json = ? WHERE name = 'demoadmin' AND type = 'addon'");
    } else {
        $stmt = $db->prepare("INSERT INTO modules (name, type, folder, enabled, config_json) VALUES ('demoadmin', 'addon', 'demoadmin', 0, ?)");
    }
    $stmt->bind_param('s', $configJson);
    $stmt->execute();
    setFlashMessage('success', 'Configuration saved.');
    redirect('/admin/demoadmin');
}

$pageTitle = 'Demo Admin Addon';
ob_start();
?>
<div class="card">
  <div class="card-header">Demo Admin Addon</div>
  <div class="card-body">
    <form method="POST">
      <input type="hidden" name="action" value="save">
      <div class="mb-3">
        <label class="form-label">API Key</label>
        <input type="password" name="api_key" class="form-control" value="<?= e($config['api_key'] ?? '') ?>">
      </div>
      <button type="submit" class="btn btn-primary">Save</button>
    </form>
  </div>
</div>
<?php
$content = ob_get_clean();
include ROOT_PATH . '/views/layouts/admin.php';
?>

Enable in Admin → Addons. The addon appears in the admin menu; clicking it loads /admin/demoadmin. File path: modules/addons/demoadmin/admin/demoadmin.php.

Config field types

TypeUse
textSingle-line input
passwordMasked input (API keys, secrets)
textareaMulti-line input
checkboxBoolean toggle
selectDropdown: add 'options' => ['a' => 'Label A', 'b' => 'Label B']

Logging and troubleshooting

Log errors or debug info to PHP error_log: error_log('MyModule: ' . json_encode($params));. Check system_logs, activity_log for provisioning activity. Store module-specific data in services.module_data (JSON) for use in suspend/unsuspend/terminate.

Was this helpful?

Tags: Developer