['Tên dịch vụ', 'Cloud VPS Business 2'], 'domain_name' => ['Tên miền', 'example.vn'], 'ssl_type' => ['Loại chứng chỉ', 'Positive SSL Wildcard'], 'issued_date' => ['Ngày cấp', '21/10/2026'], 'expires_date' => ['Ngày hết hạn', '21/10/2027'], 'nameservers' => ['Nameserver', 'ns1.tgs.com.vn, ns2.tgs.com.vn'], 'hosting_plan' => ['Gói hosting', 'Premium Hosting P2'], 'service_host' => ['IP / Host', '203.0.113.10'], 'service_user' => ['Tài khoản', 'administrator'], 'service_pass' => ['Mật khẩu', 'Xk9#mP2vLq7'], 'os_name' => ['Hệ điều hành', 'Ubuntu 22.04'], 'auth_code' => ['Mã xác thực chuyển tên miền', 'A1b2C3d4E5'], 'cancel_date' => ['Ngày huỷ / thu hồi', '21/10/2026 14:30'], 'cancel_reason' => ['Lý do', 'Hết hạn sử dụng, chưa gia hạn'], 'reset_date' => ['Thời điểm thực hiện', '21/10/2026 14:30'], 'trial_days' => ['Số ngày dùng thử', '7'], 'trial_expires' => ['Hạn dùng thử', '28/10/2026'], 'service_login_url' => ['URL đăng nhập trang dịch vụ', 'https://portal.tgs.com.vn/dang-nhap'], 'account_email' => ['Email đăng ký tài khoản', 'khach@example.com'], 'account_pass' => ['Mật khẩu đăng ký tài khoản', 'Tr1al#Pass2026'], 'name_server_1' => ['Nameserver 1 (NS1)', 'ns9.tgs.com.vn'], 'name_server_2' => ['Nameserver 2 (NS2)', 'ns10.tgs.com.vn'], 'kyc_url' => ['Link xác thực eKYC (để trống nếu không cần)', ''], 'old_expires_date' => ['Hạn cũ (trước gia hạn)', '25/10/2026'], 'billing_cycle' => ['Chu kỳ gia hạn', '1 năm'], 'trial_plan' => ['Gói dùng thử', 'Cloud VPS Trial 2 vCPU / 4GB'], ]; private const SERVICES = [ 'ssl' => [ 'label' => 'SSL', 'noun' => 'chứng chỉ SSL', 'target' => 'domain_name', 'new' => ['Tên miền' => 'domain_name', 'Loại chứng chỉ' => 'ssl_type', 'Ngày cấp' => 'issued_date', 'Ngày hết hạn' => 'expires_date'], 'reset' => ['Tên miền' => 'domain_name', 'Loại chứng chỉ' => 'ssl_type', 'Thời điểm cấp lại' => 'reset_date', 'Ngày hết hạn' => 'expires_date'], 'trial' => ['Tên miền' => 'domain_name', 'Loại chứng chỉ' => 'ssl_type', 'Thời gian dùng thử' => 'trial_days', 'Hạn dùng thử' => 'trial_expires', 'URL đăng nhập' => 'service_login_url'], ], 'domain' => [ 'label' => 'Tên miền', 'noun' => 'tên miền', 'target' => 'domain_name', 'new' => ['Tên miền' => 'domain_name', 'Ngày đăng ký' => 'issued_date', 'Ngày hết hạn' => 'expires_date', 'Nameserver' => 'nameservers'], 'reset' => ['Tên miền' => 'domain_name', 'Mã xác thực (Auth/EPP)' => 'auth_code', 'Thời điểm thực hiện' => 'reset_date'], 'trial' => ['Tên miền' => 'domain_name', 'Nameserver' => 'nameservers', 'Hạn dùng thử' => 'trial_expires', 'URL đăng nhập' => 'service_login_url'], ], 'hosting' => [ 'label' => 'Hosting', 'noun' => 'Hosting', 'target' => 'service_name', 'new' => ['Gói dịch vụ' => 'hosting_plan', 'Tên miền' => 'domain_name', 'IP / Host' => 'service_host', 'Tài khoản' => 'service_user', 'Mật khẩu' => 'service_pass', 'Ngày hết hạn' => 'expires_date'], 'reset' => ['Tên miền' => 'domain_name', 'Tài khoản' => 'service_user', 'Mật khẩu mới' => 'service_pass', 'Thời điểm thực hiện' => 'reset_date'], 'trial' => ['Gói dùng thử' => 'trial_plan', 'Tên miền' => 'domain_name', 'IP / Host' => 'service_host', 'Tài khoản' => 'service_user', 'Mật khẩu' => 'service_pass', 'Hạn dùng thử' => 'trial_expires', 'URL đăng nhập' => 'service_login_url'], ], 'cloud_vps' => [ 'label' => 'Cloud VPS', 'noun' => 'Cloud VPS', 'target' => 'service_name', 'new' => ['Dịch vụ' => 'service_name', 'Hệ điều hành' => 'os_name', 'IP / Host' => 'service_host', 'Tài khoản' => 'service_user', 'Mật khẩu' => 'service_pass', 'Ngày hết hạn' => 'expires_date'], 'reset' => ['Dịch vụ' => 'service_name', 'IP / Host' => 'service_host', 'Tài khoản' => 'service_user', 'Mật khẩu mới' => 'service_pass', 'Thời điểm thực hiện' => 'reset_date'], 'trial' => ['Gói dùng thử' => 'trial_plan', 'Hệ điều hành' => 'os_name', 'IP / Host' => 'service_host', 'Tài khoản' => 'service_user', 'Mật khẩu' => 'service_pass', 'Hạn dùng thử' => 'trial_expires', 'URL đăng nhập' => 'service_login_url'], ], ]; private const NEW_WITH_ACCOUNT_ROWS = [ 'ssl' => ['Tên miền' => 'domain_name', 'Loại chứng chỉ' => 'ssl_type', 'Ngày cấp' => 'issued_date', 'Ngày hết hạn' => 'expires_date'], 'hosting' => ['Gói dịch vụ' => 'hosting_plan', 'Tên miền' => 'domain_name', 'IP / Host' => 'service_host', 'Tài khoản Hosting' => 'service_user', 'Mật khẩu Hosting' => 'service_pass', 'Ngày cấp' => 'issued_date', 'Ngày hết hạn' => 'expires_date'], 'cloud_vps' => ['Dịch vụ' => 'service_name', 'Hệ điều hành' => 'os_name', 'IP / Host' => 'service_host', 'Tài khoản VPS' => 'service_user', 'Mật khẩu VPS' => 'service_pass', 'Ngày cấp' => 'issued_date', 'Ngày hết hạn' => 'expires_date'], ]; private const RENEWALS = [ 'vps' => ['label' => 'VPS', 'noun' => 'VPS', 'target' => 'service_name', 'column' => 'Dịch vụ'], 'hosting' => ['label' => 'Hosting', 'noun' => 'Hosting', 'target' => 'service_name', 'column' => 'Dịch vụ'], 'cloud_vps' => ['label' => 'Cloud VPS', 'noun' => 'Cloud VPS', 'target' => 'service_name', 'column' => 'Dịch vụ'], 'dedicated' => ['label' => 'Dedicated', 'noun' => 'máy chủ Dedicated', 'target' => 'service_name', 'column' => 'Dịch vụ'], 'colo' => ['label' => 'Colocation', 'noun' => 'dịch vụ Colocation', 'target' => 'service_name', 'column' => 'Dịch vụ'], 'ssl' => ['label' => 'SSL', 'noun' => 'chứng chỉ SSL', 'target' => 'domain_name', 'column' => 'Tên miền'], 'domain' => ['label' => 'Tên miền', 'noun' => 'tên miền', 'target' => 'domain_name', 'column' => 'Tên miền'], ]; private const RESET_COPY = [ 'ssl' => ['Cấp lại chứng chỉ SSL', 'Chứng chỉ SSL đã được cấp lại (reissue) theo yêu cầu. Quý khách vui lòng cài đặt lại chứng chỉ mới lên máy chủ; chứng chỉ cũ sẽ không còn hiệu lực.'], 'domain' => ['Đặt lại mã xác thực tên miền', 'Mã xác thực (Auth/EPP code) của tên miền đã được đặt lại theo yêu cầu. Mã này dùng để chuyển tên miền sang nhà đăng ký khác, vui lòng không chia sẻ cho người không liên quan.'], 'hosting' => ['Đặt lại mật khẩu Hosting', 'Mật khẩu Hosting của Quý khách đã được đặt lại theo yêu cầu. Thông tin đăng nhập mới như sau:'], 'cloud_vps' => ['Đặt lại mật khẩu Cloud VPS', 'Mật khẩu Cloud VPS của Quý khách đã được đặt lại theo yêu cầu. Thông tin đăng nhập mới như sau:'], ]; private const CANCEL_COPY = [ 'ssl' => 'thu hồi (revoke)', 'domain' => 'huỷ', 'hosting' => 'huỷ', 'cloud_vps' => 'huỷ', ]; private const CANCEL_NOTE = [ 'ssl' => 'Sau khi thu hồi, chứng chỉ không còn hiệu lực và website có thể hiển thị cảnh báo không an toàn. Vui lòng đăng ký chứng chỉ mới nếu còn nhu cầu.', 'domain' => 'Sau khi huỷ, tên miền có thể được đăng ký bởi người khác. Vui lòng liên hệ ngay nếu Quý khách muốn giữ lại tên miền.', 'hosting' => 'Toàn bộ dữ liệu website, cơ sở dữ liệu và email trên Hosting sẽ bị xoá và không thể khôi phục. Vui lòng sao lưu dữ liệu cần thiết.', 'cloud_vps' => 'Toàn bộ dữ liệu trên máy chủ ảo sẽ bị xoá và không thể khôi phục. Vui lòng sao lưu dữ liệu cần thiết.', ]; /** * @return array}> */ public static function all(): array { $items = []; foreach (self::SERVICES as $code => $service) { $items[] = self::newService($service); $items[] = self::cancelService($code, $service); $items[] = self::resetService($code, $service); foreach ([true, false] as $hasAccount) { $items[] = self::trialService($service, $hasAccount); $items[] = self::newServiceWithAccount($code, $service, $hasAccount); } } foreach (self::RENEWALS as $service) { $items[] = self::renewService($service); } foreach ([true, false] as $hasAccount) { $items[] = self::genericNewService($hasAccount); } foreach ([true, false] as $withPassword) { $items[] = self::resendAccount($withPassword); } return $items; } private static function renewService(array $service): array { $head = T::HEAD_CELL_STYLE . ' text-align: center;'; $cell = 'box-sizing: border-box; padding: 11px 14px; font-size: 13px; text-align: center;'; $headers = ''; foreach ([$service['column'], 'Chu kỳ', 'Hạn cũ', 'Hạn mới'] as $label) { $headers .= '' . $label . ''; } $values = '{{' . $service['target'] . '}}' . '{{billing_cycle}}' . '{{old_expires_date}}' . '{{expires_date}}'; $badge = '' . '' . '
✅ GIA HẠN THÀNH CÔNG
'; return self::template( "{$service['label']} — Gia hạn dịch vụ", "V/v: Thế Giới Số gia hạn thành công {$service['noun']} - {{{$service['target']}}} | {{client_name}}", $badge . T::greeting('{{client_name}}') . T::paragraph("Thế Giới Số trân trọng cảm ơn Quý khách đã hoàn tất thanh toán. {$service['noun']} dưới đây đã được gia hạn thành công và tiếp tục hoạt động bình thường.") . self::section('📋 THÔNG TIN GIA HẠN') . '' . $headers . '' . $values . '
' . T::paragraph('Thời hạn mới được tính từ ngày kết thúc cũ, cộng thêm theo chu kỳ thanh toán Quý khách đã chọn.') . self::section('📞 HỖ TRỢ KỸ THUẬT 24/7') . T::paragraph('Hotline: (028) 7309 7379
Email: support@tgs.com.vn') . T::paragraph('Một lần nữa, Thế Giới Số xin chân thành cảm ơn sự tin tưởng và đồng hành của Quý khách.') . T::paragraph('Trân trọng,'), ); } private static function newService(array $service): array { if ($service['target'] === 'domain_name' && $service['label'] === 'Tên miền') { return self::template( "{$service['label']} — Cấp mới dịch vụ", self::subject("cấp mới {$service['noun']}", $service), self::domainRegisteredBody(), ); } return self::template( "{$service['label']} — Cấp mới dịch vụ", self::subject("cấp mới {$service['noun']}", $service), T::title("{$service['label']} đã sẵn sàng") . T::greeting('{{client_name}}') . T::paragraph("Cảm ơn Quý khách đã tin tưởng sử dụng dịch vụ của {{app_name}}. {$service['noun']} {{{$service['target']}}} đã được khởi tạo thành công.") . T::heading('Thông tin dịch vụ') . T::infoTable(self::rows($service['new'])) . T::button('{{login_url}}', 'QUẢN LÝ DỊCH VỤ') . T::note('Vui lòng lưu thông tin ở nơi an toàn và không chia sẻ cho người khác.') . T::supportLine(), ); } private static function resendAccount(bool $withPassword): array { $loginLink = '{{login_url}}'; $rows = ['Trang đăng nhập' => $loginLink, 'Email đăng nhập' => '{{account_email}}']; $rows['Mật khẩu'] = $withPassword ? '{{account_pass}}' : 'Mật khẩu hiện tại của Quý khách'; return self::template( 'Tài khoản — Gửi lại thông tin tài khoản (' . ($withPassword ? 'kèm mật khẩu' : 'không kèm mật khẩu') . ')', 'V/v: Thế Giới Số gửi lại thông tin tài khoản | {{client_name}}', T::greeting('{{client_name}}') . T::paragraph('Theo yêu cầu của Quý khách, {{app_name}} xin gửi lại thông tin tài khoản đăng nhập như sau:') . T::infoTable($rows) . ($withPassword ? T::note('Quý khách vui lòng đổi mật khẩu ngay sau khi đăng nhập và không chia sẻ thông tin tài khoản cho người khác.', 'Bảo mật:') : T::note('Nếu không nhớ mật khẩu, Quý khách chọn "Quên mật khẩu" tại trang đăng nhập để đặt lại.')) . T::button('{{login_url}}', 'ĐĂNG NHẬP') . T::supportLine() . T::paragraph('Trân trọng,'), ); } private static function genericNewService(bool $hasAccount): array { $accessRows = ''; foreach (['IP / Host' => 'service_host', 'Tài khoản dịch vụ' => 'service_user', 'Mật khẩu dịch vụ' => 'service_pass'] as $label => $key) { $accessRows .= '{{#if ' . $key . '}}' . T::row($label, '{{' . $key . '}}') . '{{/if}}'; } return self::template( 'Dịch vụ — Cấp mới dịch vụ (' . ($hasAccount ? 'đã có tài khoản' : 'chưa có tài khoản') . ')', 'V/v: Thế Giới Số cấp mới dịch vụ - {{service_name}} | {{client_name}}', self::newServiceWithAccountBody( 'Dịch vụ {{service_name}}', T::infoTable(['Dịch vụ' => '{{service_name}}', 'Ngày cấp' => '{{issued_date}}', 'Ngày hết hạn' => '{{expires_date}}'], $accessRows), $hasAccount, ), ['service_host' => '', 'service_user' => '', 'service_pass' => ''], ); } private static function newServiceWithAccount(string $code, array $service, bool $hasAccount): array { $isDomain = $code === 'domain'; return self::template( "{$service['label']} — Cấp mới dịch vụ (khách hàng " . ($hasAccount ? 'đã có tài khoản' : 'chưa có tài khoản') . ')', self::subject("cấp mới {$service['noun']}", $service), self::newServiceWithAccountBody( "{$service['label']} {{{$service['target']}}}", $isDomain ? self::domainTable() : T::infoTable(self::rows(self::NEW_WITH_ACCOUNT_ROWS[$code])), $hasAccount, $isDomain ? self::kycSection('III') : '', ), ); } private static function newServiceWithAccountBody(string $serviceHtml, string $serviceTable, bool $hasAccount, string $afterAccount = ''): string { $loginLink = '{{login_url}}'; $account = $hasAccount ? T::paragraph('Dịch vụ đã được gắn vào tài khoản {{account_email}} Quý khách đang sử dụng tại {{app_name}}. Vui lòng đăng nhập bằng tài khoản này để quản lý dịch vụ.') . T::infoTable([ 'Trang đăng nhập' => $loginLink, 'Email đăng nhập' => '{{account_email}}', 'Mật khẩu' => 'Mật khẩu hiện tại của Quý khách', ]) . T::note('Nếu không nhớ mật khẩu, Quý khách chọn "Quên mật khẩu" tại trang đăng nhập để đặt lại.') : T::paragraph('Hệ thống đã tạo tài khoản để Quý khách đăng nhập và quản lý dịch vụ:') . T::infoTable([ 'Trang đăng nhập' => $loginLink, 'Email đăng nhập' => '{{account_email}}', 'Mật khẩu' => '{{account_pass}}', ]) . T::note('Quý khách vui lòng đổi mật khẩu ngay sau lần đăng nhập đầu tiên và không chia sẻ thông tin tài khoản cho người khác.', 'Bảo mật:'); return T::greeting('{{client_name}}') . T::paragraph("Cảm ơn Quý khách đã tin tưởng sử dụng dịch vụ của {{app_name}}. {$serviceHtml} đã được khởi tạo thành công.") . self::section('📋 I. THÔNG TIN DỊCH VỤ') . $serviceTable . self::section('🔐 II. TÀI KHOẢN QUẢN LÝ DỊCH VỤ') . $account . T::button('{{login_url}}', 'ĐĂNG NHẬP QUẢN LÝ DỊCH VỤ') . $afterAccount . T::supportLine() . T::paragraph('Trân trọng,'); } private static function domainTable(): string { $cell = 'box-sizing: border-box; padding: 10px; font-size: 13px; text-align: center;'; $head = T::HEAD_CELL_STYLE . ' text-align: center;'; $headers = ''; foreach (['Tên miền', 'Ngày đăng ký', 'Ngày hết hạn', 'NS1', 'NS2'] as $label) { $headers .= '' . $label . ''; } $values = ''; foreach (['{{domain_name}}', '{{issued_date}}', '{{expires_date}}', '{{name_server_1}}', '{{name_server_2}}'] as $value) { $values .= '' . $value . ''; } return '' . $headers . '' . $values . '
'; } private static function kycSection(string $number): string { return '{{#if kyc_url}}' . self::section("📋 {$number}. LƯU Ý QUAN TRỌNG") . T::paragraph('Căn cứ Khoản 2 Điều 9 và Khoản 6 Điều 21 Thông tư số 48/2025/TT-BKHCN ngày 25/12/2025 của Bộ Khoa học và Công nghệ về Quản lý và sử dụng tài nguyên Internet, chủ thể đăng ký tên miền là cá nhân có trách nhiệm hoàn thiện hồ sơ đăng ký thông qua hình thức xác thực điện tử (eKYC) trong vòng 03 ngày kể từ thời điểm nhận được email này.') . T::button('{{kyc_url}}', '✅ XÁC THỰC eKYC NGAY') . T::note('Việc không hoàn thành xác thực đúng hạn có thể ảnh hưởng đến quyền sử dụng tên miền của Quý khách.', '⏰ Lưu ý:') . '{{/if}}'; } private static function domainRegisteredBody(): string { return T::greeting('{{client_name}}') . T::paragraph('Công ty Cổ phần Giải pháp Công nghệ Thế Giới Số xin chân thành cảm ơn Quý khách đã tin tưởng lựa chọn dịch vụ đăng ký và quản lý tên miền của chúng tôi.') . self::section('🌐 I. THÔNG TIN TÊN MIỀN') . self::domainTable() . self::section('🔐 II. THÔNG TIN TÀI KHOẢN QUẢN LÝ') . T::infoTable([ 'Trang đăng nhập' => '{{service_login_url}}', 'Tên đăng nhập' => '{{account_email}}', 'Mật khẩu' => '{{account_pass}}', ]) . T::note('Quý khách vui lòng đổi mật khẩu ngay sau lần đăng nhập đầu tiên và bảo mật thông tin tài khoản.', 'Bảo mật:') . self::kycSection('III') . self::section('📞 {{#if kyc_url}}IV{{else}}III{{/if}}. HỖ TRỢ KỸ THUẬT 24/7') . T::paragraph('Hotline: (028) 7309 7379
Email: support@tgs.com.vn') . T::paragraph('Một lần nữa, Thế Giới Số xin chân thành cảm ơn sự tin tưởng và đồng hành của Quý khách. Chúng tôi luôn sẵn sàng hỗ trợ để đảm bảo trải nghiệm dịch vụ tốt nhất.') . T::paragraph('Trân trọng,'); } private static function section(string $title): string { return '' . '' . '
' . $title . '
'; } private static function cancelService(string $code, array $service): array { $verb = self::CANCEL_COPY[$code]; return self::template( "{$service['label']} — Thông báo huỷ / thu hồi", self::subject("thông báo {$verb} {$service['noun']}", $service), T::title("Thông báo {$verb} {$service['noun']}") . T::greeting('{{client_name}}') . T::paragraph("{{app_name}} xin thông báo {$service['noun']} {{{$service['target']}}} của Quý khách đã được {$verb}.") . T::infoTable([ $service['label'] => '{{' . $service['target'] . '}}', 'Ngày ' . $verb => '{{cancel_date}}', 'Lý do' => '{{cancel_reason}}', ]) . T::note(self::CANCEL_NOTE[$code], 'Quan trọng:') . T::supportLine(), ); } private static function resetService(string $code, array $service): array { [$title, $intro] = self::RESET_COPY[$code]; return self::template( "{$service['label']} — Đặt lại (Reset)", self::subject(mb_strtolower(mb_substr($title, 0, 1)) . mb_substr($title, 1), $service), T::title($title) . T::greeting('{{client_name}}') . T::paragraph($intro) . T::infoTable(self::rows($service['reset'])) . T::button('{{login_url}}', 'QUẢN LÝ DỊCH VỤ') . T::note('Nếu Quý khách không yêu cầu thao tác này, vui lòng liên hệ ngay bộ phận hỗ trợ để được bảo vệ tài khoản.') . T::supportLine(), ); } private static function trialService(array $service, bool $hasAccount): array { $suffix = $hasAccount ? 'đã có tài khoản' : 'chưa có tài khoản'; $account = $hasAccount ? T::paragraph('Quý khách vui lòng đăng nhập bằng tài khoản {{account_email}} đã đăng ký trước đó để quản lý dịch vụ dùng thử.') : T::heading('Tài khoản đăng ký') . T::paragraph('Hệ thống đã tự động tạo tài khoản để Quý khách quản lý dịch vụ dùng thử:') . T::infoTable(['Email đăng nhập' => '{{account_email}}', 'Mật khẩu' => '{{account_pass}}']); $note = $hasAccount ? 'Dịch vụ dùng thử sẽ tự động ngừng khi hết hạn và dữ liệu có thể bị xoá. Quý khách vui lòng đăng ký chính thức trước thời hạn để tiếp tục sử dụng.' : 'Vui lòng đổi mật khẩu ngay sau lần đăng nhập đầu tiên. Dịch vụ dùng thử sẽ tự động ngừng khi hết hạn và dữ liệu có thể bị xoá.'; return self::template( "{$service['label']} — Dùng thử ({$suffix})", self::subject("kích hoạt dùng thử {$service['noun']}", $service), T::title("Dùng thử {$service['label']}") . T::greeting('{{client_name}}') . T::paragraph("Cảm ơn Quý khách đã đăng ký dùng thử {$service['noun']} tại {{app_name}}. Dịch vụ dùng thử đã được kích hoạt và có hiệu lực đến {{trial_expires}}.") . T::heading('Thông tin dùng thử') . T::infoTable(self::rows($service['trial'])) . $account . T::button('{{service_login_url}}', 'ĐĂNG NHẬP QUẢN LÝ DỊCH VỤ') . T::note($note) . T::supportLine(), ); } /** * @param array $map label => variable key * @return array */ private static function rows(array $map): array { return array_map(fn (string $key) => '{{' . $key . '}}', $map); } private static function subject(string $topic, array $service): string { return "V/v: Thế Giới Số {$topic} - {{{$service['target']}}} | {{client_name}}"; } /** * @param array $examples */ private static function template(string $name, string $subject, string $body, array $examples = []): array { $variables = []; foreach (MailPlaceholders::extract($subject, $body) as $key) { if (isset(self::VARIABLES[$key])) { $variables[] = ['key' => $key, 'label' => self::VARIABLES[$key][0], 'example' => $examples[$key] ?? self::VARIABLES[$key][1]]; } } return compact('name', 'subject', 'body', 'variables'); } }