- PHP 91.8%
- Blade 8.2%
Both WHMCS import-report CSV exports wrote ImportSessionEvent source_id and message (interpolated WHMCS source data: server/domain/contact names) straight to fputcsv. A source record leading with = + - @ executed as a formula in the operator's spreadsheet on export. Route every data row through App\Support\CsvFormulaGuard::neutralizeRow(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| config | ||
| resources/views | ||
| routes | ||
| src | ||
| tests/Feature | ||
| .gitattributes | ||
| .gitignore | ||
| composer.json | ||
| LICENSE.md | ||
| README.md | ||
LiBilling WHMCS Importer
Data migration tool for importing clients, products, pricing, and more from a WHMCS installation into LiBilling. Reads the WHMCS source over a read-only MySQL connection, never writes back.
Features
- Client Import: WHMCS clients to LiBilling Users + Teams (match by email, flag for password reset)
- Product Import: WHMCS products and product groups to LiBilling ProductGroups + Products
- Pricing Import: WHMCS billing cycle pricing to LiBilling Pricings (term/period mapping)
- Dry Run Mode: Validate and preview what would be imported without writing to the database
- Chunked Processing: Configurable chunk size for large datasets
- Admin Sidebar: Automatically registers a "WHMCS Import" link in the admin Tools section via AdminMenuRegistry
- Read-Only by Construction: connected MySQL user must hold SELECT only; the importer refuses to start otherwise
Installation
This package is loaded as a local Composer path repository. No separate installation is needed when developing within the LiBilling monorepo.
For standalone installation:
composer require libilling/libilling-importer-whmcs
Configuration
Publish the config file:
php artisan vendor:publish --tag=libilling-importer-whmcs
Set your WHMCS database credentials in .env:
WHMCS_IMPORT_DB_HOST=127.0.0.1
WHMCS_IMPORT_DB_PORT=3306
WHMCS_IMPORT_DB_DATABASE=whmcs
WHMCS_IMPORT_DB_USERNAME=libilling_import
WHMCS_IMPORT_DB_PASSWORD=secret
WHMCS_IMPORT_DECRYPT_KEY=value-of-cc_encryption_hash-from-whmcs-configuration.php
If the WHMCS database isn't directly reachable from LiBilling, use an SSH tunnel:
ssh -L 3307:127.0.0.1:3306 user@whmcs-host
…and point WHMCS_IMPORT_DB_HOST=127.0.0.1, WHMCS_IMPORT_DB_PORT=3307.
Config Options
| Key | Env | Default | Description |
|---|---|---|---|
database.host |
WHMCS_IMPORT_DB_HOST |
-- | WHMCS database host |
database.port |
WHMCS_IMPORT_DB_PORT |
3306 |
WHMCS database port |
database.database |
WHMCS_IMPORT_DB_DATABASE |
-- | WHMCS database name |
database.username |
WHMCS_IMPORT_DB_USERNAME |
-- | SELECT-only MySQL user |
database.password |
WHMCS_IMPORT_DB_PASSWORD |
-- | Database password |
decrypt_key |
WHMCS_IMPORT_DECRYPT_KEY |
-- | $cc_encryption_hash from source configuration.php |
chunk_size |
WHMCS_IMPORT_CHUNK_SIZE |
100 |
Records processed per batch |
queue |
WHMCS_IMPORT_QUEUE |
whmcs-import |
Horizon queue for import jobs |
Usage
Admin Dashboard (standard interface)
Navigate to Admin → Tools → WHMCS Import to access the import workflow. The dashboard is the supported entry point for every operator-driven action:
- Discovery tab: pre-flight probe; resolve every blocker (Map / Ignore) before importing
- Dashboard tab: tier-grouped table; per-importer Run + per-tier Run-all buttons; dry-run toggle; live event tail under the running importer's row with Pause / Resume / Cancel
- Counters and event log update every 1.5 seconds while a session is active
The dashboard is what operators use day-to-day. Imports are GUI-driven by design, the CLI commands below are power-user fallbacks for scripted migrations or terminal-tee debugging.
Artisan Commands (power-user only)
# Verify the configured WHMCS connection is reachable and read-only
php artisan libilling:whmcs-test-connection
# Probe which WHMCS modules are in use and whether LiBilling has matching packages installed
php artisan libilling:whmcs-discover
# Manage operator-recorded module mapping decisions (parity with the discovery dashboard)
php artisan libilling:whmcs-module-map list
php artisan libilling:whmcs-module-map set --type=registrar --source=netearthone --target=logicboxes
# Run a single importer synchronously in the terminal, same events table the dashboard reads,
# so the GUI mirrors the run live. Concurrency-capped to one CLI run by default; --force overrides.
php artisan libilling:whmcs-import server [--dry-run] [--force]
# Cutover-mode helpers
php artisan libilling:whmcs-cutover-status
php artisan libilling:whmcs-cutover-end
Import Order
Importers run in tier order so foreign-key dependencies are satisfied. The dashboard's "Run all in tier" button respects this; CLI runs honor dependsOn() validation.
| Tier | Importer | Source | Target | Notes |
|---|---|---|---|---|
| 0 | Server | tblservers |
Server |
Decrypts WHMCS credentials; alias-aware module mapping |
| 0 | ProductGroup | tblproductgroups |
ProductGroup |
|
| 0 | TLD | tbldomainpricing |
Tld |
Per-row registrar override on selection page |
| 0 | KbCategory | tblknowledgebasecats |
KbCategory |
|
| 1 | Product | tblproducts |
Product |
Depends on ProductGroup; type + module mapping |
| 1 | ProductPricing | tblpricing (type=product) |
Pricing |
Fan-out: 1 source row → up to 6 LiBilling rows (one per cycle column) |
| 1 | Promotion | tblpromotions |
Promotion |
Coupon-only types |
| 1 | EmailTemplate | tblemailtemplates |
EmailTemplate |
Imported inactive (Smarty doesn't translate) |
| 1 | ConfigurableOption | tblproductconfigoptions |
ConfigurableOptionGroup + Options + Pricing |
Multi-product fan-out |
| 2 | Client | tblclients |
User + personal Team |
Email match; password not imported |
| 3 | Contact | tblcontacts (subaccount=1) |
User + personal Team + parent team_user |
Notification-only contacts skipped |
| 3 | Service | tblhosting |
Service + synthetic Order |
Synthesizes a minimal Order to satisfy the FK; invoices/transactions NOT imported |
| 3 | Domain | tbldomains |
Domain + 3 domain_addons rows (id_protection, dns_management, email_forwarding) |
Longest-suffix TLD match; registrar via alias chain |
| 4 | Ticket | tbltickets + tblticketreplies + tblticketnotes |
Ticket + N+M+1 ticket_events |
Best-effort staff actor matching; attachment filenames mirrored as markdown links |
Out of scope per operator direction: Orders, Invoices, Transactions. ServiceImporter creates synthetic minimal Orders only to satisfy the FK contract.
Testing
vendor/bin/sail artisan test --testsuite=WhmcsImporter
License
LiBilling is (C) Lithium Holdings, LLC. All components except for third-party modules and select packages with their own license are licensed under a Commercial License. Contact licensing@lithiumholdings.com for licensing enquiries. Any dissemination of material herein is prohibited without expressed written consent of Lithium Holdings.
This package, libilling-importer-whmcs is licensed under The MIT License (MIT). Please see License File for more information.
Is it any good?
Yes.
When people first hear about a new product, they frequently ask if it is any good. A Hacker News user remarked:
Note to self: Starting immediately, all raganwald projects will have a "Is it any good?" section in the readme, and the answer shall be "yes.".