Create Hosting Subscription -> * Assign Plan -> persist module_hosting_accounts. No database/SSL/mail/git * deploy — that's later phases. */ class PleskHostingService { public function __construct( private PleskClientService $clients, private PleskDomainService $domains, private HostingAccountRepositoryInterface $accounts, private DnsDomainRepositoryInterface $dnsDomains, ) { } public function provision(array $data): array { $server = DnsServer::find($data['dns_manager_server_id'] ?? null); if (!$server) { return ['error' => true, 'message' => 'Hosting server not found']; } if (!$server->servesHosting()) { return ['error' => true, 'message' => 'This server does not support hosting']; } $customer = Customer::find($data['customer_id'] ?? null); if (!$customer) { return ['error' => true, 'message' => 'Customer not found']; } $client = $this->clients->findOrCreateCustomer($server, $customer); if ($client['error']) { return $client; } $pleskClientId = $client['data']['id']; $pleskLogin = $client['data']['login']; $pleskPassword = $client['data']['password']; // Suffix avoids a collision if the same domain is re-provisioned (e.g. after cancellation). $ftpLogin = str_replace('.', '_', $data['domain_name']) . '_' . Str::lower(Str::random(4)); // Not persisted anywhere — the only record of it is this response, once. $ftpPassword = $data['ftp_password'] ?? Helper::general_password(); $subscription = $this->domains->createDomain($server, [ 'domain_name' => $data['domain_name'], 'ftp_login' => $ftpLogin, 'ftp_password' => $ftpPassword, 'plan' => $data['hosting_plan'] ?? null, 'owner_client_id' => $pleskClientId, ]); if (!$subscription) { return [ 'error' => true, 'message' => $this->domains->getLastError() ?? 'Failed to create Plesk hosting subscription', ]; } $domainInfo = $this->domains->getDomainInfo($server, $data['domain_name']) ?: []; $dnsManagerDomainId = $data['dns_manager_domain_id'] ?? null; if (!$dnsManagerDomainId) { // Plesk's /domains object is the same entity the DNS module tracks — // creating a hosting subscription there already created the DNS zone // on Plesk, so mirror it locally instead of leaving it unlinked. $dnsDomain = $this->dnsDomains->model->firstOrCreate( [ 'domain_name' => $data['domain_name'], 'dns_manager_server_id' => $server->id, ], [ 'plesk_id' => $subscription['id'], 'is_created' => 1, ] ); $dnsManagerDomainId = $dnsDomain->id; } $account = $this->accounts->create([ 'customer_id' => $customer->id, 'service_id' => $data['service_id'] ?? null, 'dns_manager_server_id' => $server->id, 'dns_manager_domain_id' => $dnsManagerDomainId, 'plesk_client_id' => $pleskClientId, 'plesk_subscription_id' => $subscription['id'], 'plesk_guid' => $subscription['guid'], 'hosting_plan' => $data['hosting_plan'] ?? null, 'ftp_username' => $subscription['hosting_settings']['ftp_login'] ?? $ftpLogin, 'system_username' => $subscription['hosting_settings']['ftp_login'] ?? $ftpLogin, 'document_root' => $domainInfo['www_root'] ?? null, 'status' => 'active', ]); $this->notify($customer, $server, $account, $data['domain_name'], $ftpPassword, $pleskLogin, $pleskPassword, $data['invoice_id'] ?? null); return ['error' => false, 'data' => $account, 'ftp_password' => $ftpPassword]; } private function notify( Customer $customer, DnsServer $server, ModuleHostingAccount $account, string $domainName, string $ftpPassword, string $pleskLogin, ?string $pleskPassword, ?int $invoiceId = null, ): void { $shared = [ 'customer_name' => $customer->fullname, 'customer_email' => $customer->email, 'customer_company' => $customer->company, 'domain_name' => $domainName, 'hosting_plan' => $account->hosting_plan, 'server_name' => $server->name, 'ftp_username' => $account->ftp_username, 'ftp_password' => $ftpPassword, 'plesk_login' => $pleskLogin, 'plesk_password' => $pleskPassword, 'plesk_panel_url' => $server->panelUrl(), 'created_at' => now()->format('d/m/Y H:i:s'), ]; dispatch(new HostingAccountCreatedJob(array_merge($shared, [ 'server_host' => $server->publicHost(), 'invoice_id' => $invoiceId, ]))); dispatch(new HostingAccountCreatedAdminNotificationJob(array_merge($shared, [ 'server_ip' => $server->ip_address, 'plesk_client_id' => $account->plesk_client_id, 'plesk_subscription_id' => $account->plesk_subscription_id, ]))); } }