Files
portal-api/app/Services/Plesk/PleskHostingService.php
baodev0andClaude Sonnet 5.5 b44a88bb52 feat(service): huỷ dịch vụ, SSL, đổi mật khẩu hosting, lịch sử thanh toán theo hình thức
- 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>
2026-10-28 16:40:24 +07:00

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,
])));
}
}