feat: complete pre-launch audit — frontend polish, churn prevention, login history, financial reports, configurable checkout
Includes all work from phases 6-9+ and frontend polish rounds 1 & 2: - Login history with device trust, new device notifications, session management - Churn prevention: cancellation surveys, winback campaigns with email sequences - Financial reports: revenue, P&L, tax, aging, refund, subscription reports with PDF/CSV/JSON export - Configurable checkout: plan config groups/options, build-your-own VPS - Frontend polish: fix broken legal links, add SEO meta tags, favicon, font display=swap, Head titles on all 14 marketing pages, mobile responsive fixes, AuthLayout legal footer, remove false 24/7 claims, hide empty stats, correct uptime SLA to 99.9%, GameServers notify buttons linked to /contact, 301 redirects for /terms and /privacy - WHMCS migration scripts - Update legal page effective dates to March 16, 2026 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
222
scripts/whmcs-migrate/migrate.php
Normal file
222
scripts/whmcs-migrate/migrate.php
Normal file
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* WHMCS to EZSCALE Migration Script
|
||||
*
|
||||
* Usage:
|
||||
* php migrate.php Run all phases (fetches from WHMCS API)
|
||||
* php migrate.php --phase=1 Run only phase 1
|
||||
* php migrate.php --dry-run Run without writing to the database
|
||||
* php migrate.php --export-only Export all WHMCS data to JSONL files (no DB writes)
|
||||
* php migrate.php --from-export Import from previously exported JSONL files (no API calls)
|
||||
* php migrate.php --status Show current migration progress
|
||||
* php migrate.php --export-status Show exported JSONL file summary
|
||||
* php migrate.php --validate-only Test connectivity without migrating
|
||||
* php migrate.php --reset Reset all migration state
|
||||
* php migrate.php --reset-exports Delete all exported JSONL files
|
||||
* php migrate.php --help Show this help message
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
use WhmcsMigrate\MigrationRunner;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Banner
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const VERSION = '2.0.0';
|
||||
|
||||
$banner = <<<BANNER
|
||||
|
||||
╔═══════════════════════════════════════════════════╗
|
||||
║ WHMCS → EZSCALE Migration Tool v%s ║
|
||||
║ %s ║
|
||||
╚═══════════════════════════════════════════════════╝
|
||||
|
||||
BANNER;
|
||||
|
||||
fprintf(STDOUT, $banner, VERSION, date('Y-m-d H:i:s'));
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CLI argument parsing
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
$options = getopt('', [
|
||||
'dry-run',
|
||||
'export-only',
|
||||
'from-export',
|
||||
'phase:',
|
||||
'reset',
|
||||
'reset-exports',
|
||||
'status',
|
||||
'export-status',
|
||||
'validate-only',
|
||||
'help',
|
||||
]);
|
||||
|
||||
if (isset($options['help'])) {
|
||||
printUsage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Resolve base path (directory containing this script)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
$basePath = __DIR__;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Apply CLI overrides before constructing the runner
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
if (isset($options['dry-run'])) {
|
||||
putenv('DRY_RUN=true');
|
||||
$_ENV['DRY_RUN'] = 'true';
|
||||
$_SERVER['DRY_RUN'] = 'true';
|
||||
}
|
||||
|
||||
if (isset($options['export-only'])) {
|
||||
putenv('EXPORT_ONLY=true');
|
||||
$_ENV['EXPORT_ONLY'] = 'true';
|
||||
$_SERVER['EXPORT_ONLY'] = 'true';
|
||||
}
|
||||
|
||||
if (isset($options['from-export'])) {
|
||||
putenv('FROM_EXPORT=true');
|
||||
$_ENV['FROM_EXPORT'] = 'true';
|
||||
$_SERVER['FROM_EXPORT'] = 'true';
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Execute
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
try {
|
||||
$runner = new MigrationRunner($basePath);
|
||||
|
||||
if (isset($options['reset'])) {
|
||||
$runner->reset();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (isset($options['reset-exports'])) {
|
||||
$runner->resetExports();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (isset($options['status'])) {
|
||||
$runner->showStatus();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (isset($options['export-status'])) {
|
||||
$runner->showExportSummary();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (isset($options['validate-only'])) {
|
||||
$runner->validateOnly();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$phaseNumber = isset($options['phase']) ? (int) $options['phase'] : null;
|
||||
|
||||
$runner->run($phaseNumber);
|
||||
|
||||
exit(0);
|
||||
} catch (Throwable $e) {
|
||||
fprintf(STDERR, "\nFATAL ERROR: %s\n", $e->getMessage());
|
||||
fprintf(STDERR, " File: %s:%d\n", $e->getFile(), $e->getLine());
|
||||
fprintf(STDERR, " Trace:\n%s\n", $e->getTraceAsString());
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function printUsage(): void
|
||||
{
|
||||
$usage = <<<USAGE
|
||||
Usage: php migrate.php [OPTIONS]
|
||||
|
||||
Options:
|
||||
--dry-run Run the migration without writing to the database.
|
||||
All operations are logged but no INSERT/UPDATE queries
|
||||
are executed.
|
||||
|
||||
--export-only Export all WHMCS data to JSONL files without touching
|
||||
the database. Each entity type gets its own .jsonl file
|
||||
in the exports/ directory. This is fast for re-imports.
|
||||
|
||||
--from-export Import from previously exported JSONL files instead of
|
||||
hitting the WHMCS API. Much faster for subsequent runs
|
||||
since there are no API calls or rate-limit waits.
|
||||
Requires --export-only to have been run first.
|
||||
|
||||
--phase=N Run only the specified phase number (1-7).
|
||||
Phases:
|
||||
1 Clients → Users + UserProfiles
|
||||
2 Products → Plans + PlanPrices
|
||||
3 Services → Subscriptions + Services
|
||||
4 Invoices → Invoices + InvoiceItems
|
||||
5 Transactions → PaymentTransactions
|
||||
6 Promotions → Coupons
|
||||
7 Orders → Orders
|
||||
|
||||
--status Display the current progress of all phases and exit.
|
||||
|
||||
--export-status Display a summary of all exported JSONL files.
|
||||
|
||||
--validate-only Test WHMCS API and EZSCALE database connectivity
|
||||
without performing any migration.
|
||||
|
||||
--reset Delete all migration state (ID mappings and progress)
|
||||
so the migration can be re-run from scratch.
|
||||
|
||||
--reset-exports Delete all exported JSONL files from the exports/
|
||||
directory.
|
||||
|
||||
--help Show this help message and exit.
|
||||
|
||||
Configuration:
|
||||
Copy .env.example to .env and fill in:
|
||||
- WHMCS API credentials (WHMCS_API_URL, WHMCS_API_IDENTIFIER, WHMCS_API_SECRET)
|
||||
- EZSCALE database credentials (EZSCALE_DB_*)
|
||||
- Laravel APP_KEY (LARAVEL_APP_KEY) for encrypting sensitive data
|
||||
- BATCH_SIZE (default: 250, WHMCS API supports up to 250)
|
||||
|
||||
Optional plan mapping:
|
||||
Edit plan_mapping.json to map WHMCS product IDs to existing EZSCALE
|
||||
plan slugs. Unmapped products will be auto-created as new plans.
|
||||
|
||||
Workflow:
|
||||
1. Export all data: php migrate.php --export-only
|
||||
2. Review exports: php migrate.php --export-status
|
||||
3. Test import: php migrate.php --from-export --dry-run
|
||||
4. Run import: php migrate.php --from-export
|
||||
5. Check status: php migrate.php --status
|
||||
|
||||
For a direct migration (no export step):
|
||||
php migrate.php
|
||||
|
||||
State:
|
||||
Migration progress and ID mappings are saved to the state/ directory.
|
||||
If the migration is interrupted, re-running will resume from the last
|
||||
checkpoint. Use --reset to clear all state.
|
||||
|
||||
Exports:
|
||||
JSONL files are saved to the exports/ directory. Each line is one JSON
|
||||
record. Use --reset-exports to delete all export files.
|
||||
|
||||
Logs:
|
||||
Detailed logs are written to logs/migration.log.
|
||||
|
||||
USAGE;
|
||||
|
||||
echo $usage;
|
||||
}
|
||||
Reference in New Issue
Block a user