}>
*/
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 '';
}
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');
}
}