WHMCS Importer for LiBilling
  • PHP 91.8%
  • Blade 8.2%
Find a file
Troy Siedsma 091caeea6b Security: neutralize CSV formula injection in import report exports
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>
2026-06-28 18:28:38 +00:00
config Ticket mapping: Tier 1 DepartmentImporter; skip attachments entirely 2026-06-04 13:14:09 +00:00
resources/views Import report: full-detail CSV export 2026-06-11 18:57:42 +00:00
routes Re-platform onto LiBilling/libilling-importer-whmcs Forgejo origin 2026-05-22 13:14:19 +00:00
src Security: neutralize CSV formula injection in import report exports 2026-06-28 18:28:38 +00:00
tests/Feature Security M: strip credential columns from client conflict context 2026-06-28 16:41:48 +00:00
.gitattributes Re-platform onto LiBilling/libilling-importer-whmcs Forgejo origin 2026-05-22 13:14:19 +00:00
.gitignore Re-platform onto LiBilling/libilling-importer-whmcs Forgejo origin 2026-05-22 13:14:19 +00:00
composer.json Standardize composer.json + README + LICENSE 2026-05-22 15:03:57 +00:00
LICENSE.md Standardize composer.json + README + LICENSE 2026-05-22 15:03:57 +00:00
README.md Standardize composer.json + README + LICENSE 2026-05-22 15:03:57 +00:00

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:

  1. Discovery tab: pre-flight probe; resolve every blocker (Map / Ignore) before importing
  2. 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
  3. 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.".