- Lịch sử thanh toán hoá đơn trả thêm `method` (tự động gia hạn / số dư / admin / chuyển khoản / hoàn tiền), bỏ đuôi `(method)` khỏi ghi chú - `DnsServer::panelUrl()` = https://<domain>/ dùng cho email cấp hosting và nút Plesk của khách - Order item trả thêm `uuid`/`type` của dịch vụ để FE dẫn tới trang quản lý - Yêu cầu huỷ dịch vụ, chứng chỉ SSL, lịch sử gia hạn, đổi mật khẩu hosting cho khách Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
157 lines
6.0 KiB
PHP
157 lines
6.0 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Plesk;
|
|
|
|
use App\Http\Facade\HelperFacade as Helper;
|
|
use App\Jobs\Mail\Admin\HostingAccountCreatedAdminNotificationJob;
|
|
use App\Jobs\Mail\Customer\HostingAccountCreatedJob;
|
|
use App\Models\Customer;
|
|
use App\Models\DnsServer;
|
|
use App\Models\ModuleHostingAccount;
|
|
use App\Repositories\DnsManager\Domain\DnsDomainRepositoryInterface;
|
|
use App\Repositories\Module\HostingAccount\HostingAccountRepositoryInterface;
|
|
use Illuminate\Support\Str;
|
|
|
|
/**
|
|
* Phase 1 orchestration: Create Customer -> 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,
|
|
])));
|
|
}
|
|
}
|