Add feature tests for domain order management

- Implement AdminDomainOrderTest to cover admin functionalities for domain orders including listing, confirming registration, KYC updates, activation, rejection, and refunds.
- Implement ClientDomainOrderTest to cover client functionalities for placing domain orders, including validation for balance, duplicate orders, and domain normalization.
- Ensure proper handling of domain pricing and service statuses throughout the tests.
This commit is contained in:
baodev0
2026-07-10 14:16:35 +07:00
parent 61c0e026c8
commit dfb926055d
81 changed files with 35743 additions and 481 deletions
@@ -193,9 +193,30 @@ class ClientController extends Controller
$client = $result['client'];
$this->activityLogger->logAuth('register', 'customer', $client->id, $client->email);
return response()->json([
'error' => false,
'message' => 'Đăng ký thành công. Vui lòng kiểm tra email và nhập mã xác thực để kích hoạt tài khoản.',
]);
}
public function verifyEmail(Request $request)
{
$request->validate([
'email' => ['required', 'email', 'max:255'],
'code' => ['required', 'string', 'size:6'],
]);
$result = $this->authService->verifyEmail(
$request->input('email'),
$request->input('code'),
);
$client = $result['client'];
$this->activityLogger->logAuth('email_verified', 'customer', $client->id, $client->email);
return response()->json([
'error' => false,
'data' => [
'data' => [
'client' => new ClientResource($client->loadCount(['vps', 'hosting', 'domain'])),
'token' => $result['token'],
'refresh_token' => $result['refresh_token'],
@@ -204,6 +225,20 @@ class ClientController extends Controller
]);
}
public function resendVerifyCode(Request $request)
{
$request->validate([
'email' => ['required', 'email', 'max:255'],
]);
$this->authService->resendVerifyCode($request->input('email'));
return response()->json([
'error' => false,
'message' => 'Nếu email tồn tại và chưa xác thực, mã xác thực mới đã được gửi.',
]);
}
/**
* @OA\Post(
* path="/client/logout",
@@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Api\Client\Domain;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\Client\Domain\StoreDomainOrderRequest;
use App\Mail\Admin\DomainOrderNewNotification;
use App\Models\Notification;
use App\Models\Setting;
use App\Models\Template;
use App\Services\Api\Domain\DomainOrderService;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class DomainOrderController extends Controller
{
public function __construct(
private readonly DomainOrderService $domainOrderService,
) {}
public function store(StoreDomainOrderRequest $request): JsonResponse
{
try {
$result = $this->domainOrderService->placeOrder($request->validated(), $request->user());
$this->notifyAdminNewDomainOrder($request->user(), $result);
return response()->json([
'success' => true,
'message' => 'Đặt mua tên miền thành công.',
'notice' => 'Việc đặt mua không đồng nghĩa tên miền đã được đăng ký thành công. Đơn hàng sẽ được nhân viên kiểm tra và xử lý. Nếu tên miền không còn khả dụng, số dư đã thanh toán sẽ được hoàn lại vào tài khoản.',
'data' => [
'order_num' => $result['order']->order_num,
'domain_name' => $result['order']->orderItems->first()?->domain_name,
'status' => 'pending',
'total' => $result['invoice']->total,
'invoice_number' => $result['invoice']->invoice_number,
],
], 201);
} catch (\InvalidArgumentException $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 422);
} catch (\Throwable $e) {
Log::error('DomainOrderController@store', [
'error' => $e->getMessage(),
'customer_id' => $request->user()?->id,
'data' => $request->validated(),
]);
return response()->json([
'success' => false,
'message' => 'Đặt mua tên miền thất bại. Vui lòng thử lại.',
], 500);
}
}
private function notifyAdminNewDomainOrder($customer, array $result): void
{
$domainName = $result['order']->orderItems->first()?->domain_name ?? '';
$orderNum = $result['order']->order_num;
$total = number_format((int) $result['invoice']->total) . ' VNĐ';
Notification::notifyAdmins([
'type' => 'domain_order_new',
'title' => 'Đơn đăng ký tên miền mới',
'body' => "Khách hàng {$customer->fullname} vừa đặt mua tên miền {$domainName} (#{$orderNum})",
'link' => '/domain-orders',
]);
try {
$setting = Setting::where('key', 'setting')->first();
$settingValue = $setting ? json_decode((string) $setting->value, true) : [];
$adminEmail = $settingValue['admin_email'] ?? null;
if (empty($adminEmail)) {
return;
}
$template = Template::where('name', 'Domain Order New')->first();
if (!$template) {
return;
}
$adminUrl = rtrim(env('ADMIN_URL', config('app.url')), '/') . '/domain-orders';
Mail::to($adminEmail)->send(new DomainOrderNewNotification($template, [
'company_name' => $settingValue['company'] ?? config('app.name'),
'company_logo' => $settingValue['logo'] ?? '',
'domain_name' => $domainName,
'customer_name' => $customer->fullname,
'customer_email' => $customer->email,
'order_num' => $orderNum,
'order_total' => $total,
'created_at' => now()->format('d/m/Y H:i:s'),
'admin_url' => $adminUrl,
]));
} catch (\Throwable $e) {
Log::error('DomainOrderController: không thể gửi email admin: ' . $e->getMessage());
}
}
}
@@ -0,0 +1,77 @@
<?php
namespace App\Http\Controllers\Api\Client\Domain;
use App\Http\Controllers\Controller;
use App\Models\Setting;
use Illuminate\Http\JsonResponse;
class DomainPricingController extends Controller
{
/**
* Default domain pricing data (fallback when settings not configured)
*/
private function getDefaultDomainPricing(): array
{
return [
'domains' => [
['tld' => '.vn', 'setupFee' => 'Miễn phí', 'firstYear' => 400000, 'renewal' => 400000],
['tld' => '.com.vn', 'setupFee' => 'Miễn phí', 'firstYear' => 300000, 'renewal' => 300000],
['tld' => '.net.vn', 'setupFee' => 'Miễn phí', 'firstYear' => 300000, 'renewal' => 300000],
['tld' => '.org.vn', 'setupFee' => 'Miễn phí', 'firstYear' => 300000, 'renewal' => 300000],
['tld' => '.com', 'setupFee' => 'Miễn phí', 'firstYear' => 320000, 'renewal' => 340000],
['tld' => '.net', 'setupFee' => 'Miễn phí', 'firstYear' => 369000, 'renewal' => 409000],
['tld' => '.org', 'setupFee' => 'Miễn phí', 'firstYear' => 339000, 'renewal' => 369000],
['tld' => '.biz', 'setupFee' => 'Miễn phí', 'firstYear' => 399000, 'renewal' => 409000],
['tld' => '.info', 'setupFee' => 'Miễn phí', 'firstYear' => 490000, 'renewal' => 509000],
['tld' => '.pro', 'setupFee' => 'Miễn phí', 'firstYear' => 469000, 'renewal' => 489000],
['tld' => '.name', 'setupFee' => 'Miễn phí', 'firstYear' => 259000, 'renewal' => 259000],
['tld' => '.ws', 'setupFee' => 'Miễn phí', 'firstYear' => 909000, 'renewal' => 909000],
['tld' => '.tv', 'setupFee' => 'Miễn phí', 'firstYear' => 765000, 'renewal' => 819000],
['tld' => '.mobi', 'setupFee' => 'Miễn phí', 'firstYear' => 589000, 'renewal' => 589000],
['tld' => '.uk', 'setupFee' => 'Miễn phí', 'firstYear' => 537000, 'renewal' => 537000],
['tld' => '.asia', 'setupFee' => 'Miễn phí', 'firstYear' => 380000, 'renewal' => 452000],
['tld' => '.me', 'setupFee' => 'Miễn phí', 'firstYear' => 1029000, 'renewal' => 1029000],
['tld' => '.us', 'setupFee' => 'Miễn phí', 'firstYear' => 323000, 'renewal' => 323000],
],
'services' => [
['name' => 'Chứng thực Rapid SSL', 'setupFee' => 230000, 'maintenance' => 750000, 'note' => '01 Domain'],
['name' => 'Chứng thực Quick SSL', 'setupFee' => 230000, 'maintenance' => 2300000, 'note' => '01 Domain'],
['name' => 'Chứng thực Wildcard SSL', 'setupFee' => 230000, 'maintenance' => 3300000, 'note' => 'Unlimited Subdomain'],
['name' => 'Chứng thực True BusinessID', 'setupFee' => 540000, 'maintenance' => 3900000, 'note' => '01 Domain'],
['name' => 'Chứng thực True BusinessID WildCard', 'setupFee' => 900000, 'maintenance' => 15000000, 'note' => 'Unlimited Subdomain'],
['name' => 'Đăng ký DNS riêng', 'setupFee' => 200000, 'maintenance' => 'Miễn phí', 'note' => "dns1.tenmiencuaban.com\ndns2.tenmiencuaban.com"],
['name' => 'Bảo mật thông tin tên miền', 'setupFee' => 'Miễn phí', 'maintenance' => 90000, 'note' => 'Toàn bộ thông tin Whois tên miền của bạn sẽ được giấu kín hoàn toàn với mọi công cụ Whois'],
['name' => 'Khóa tên miền', 'setupFee' => 'Miễn phí', 'maintenance' => 'Miễn phí', 'note' => 'Tăng cường bảo mật cho tên miền'],
],
];
}
/**
* GET /v1/domain-pricing
* Returns domain pricing list and addon services.
* Public endpoint (no auth required).
*/
public function index(): JsonResponse
{
$setting = Setting::where('key', 'domain_pricing')->first();
if ($setting && !empty($setting->value)) {
$value = json_decode((string) $setting->value, true);
if (is_array($value) && !empty($value)) {
return response()->json([
'error' => false,
'data' => $value,
'message' => 'Lấy bảng giá tên miền thành công.',
]);
}
}
// Fallback to default data
return response()->json([
'error' => false,
'data' => $this->getDefaultDomainPricing(),
'message' => 'Lấy bảng giá tên miền thành công (mặc định).',
]);
}
}