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>
This commit is contained in:
baodev0
2026-10-28 16:40:24 +07:00
co-authored by Claude Sonnet 5.5
parent a6b641015e
commit b44a88bb52
72 changed files with 1701 additions and 231 deletions
-56
View File
@@ -1,56 +0,0 @@
<?php
namespace App\Http\Middleware;
use App\Support\ResponseCipher;
use Closure;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class SealResponse
{
public const KEY_HEADER = 'x-seal-key';
public const OPT_IN_HEADER = 'x-seal';
private const SEALED_ATTRIBUTE = 'response_sealed';
public function handle(Request $request, Closure $next, ?string $mode = null)
{
$key = ResponseCipher::keyFrom($request->headers->get(self::KEY_HEADER));
if ($this->openRequest($request, $key) === false) {
return new JsonResponse(['message' => 'Khoá phiên không khớp, vui lòng thử lại.'], 428);
}
$response = $next($request);
$wanted = $mode === 'always' || $request->headers->get(self::OPT_IN_HEADER) === '1';
if (! $key || ! $wanted || $request->attributes->has(self::SEALED_ATTRIBUTE) || ! $response instanceof JsonResponse) {
return $response;
}
$request->attributes->set(self::SEALED_ATTRIBUTE, true);
$response->setData(['enc' => ResponseCipher::seal($response->getContent(), $key)]);
return $response;
}
private function openRequest(Request $request, ?string $key): ?bool
{
$sealed = $request->isJson() ? $request->json('enc') : null;
if (! is_string($sealed)) {
return null;
}
$data = $key ? json_decode((string) ResponseCipher::open($sealed, $key), true) : null;
if (! is_array($data)) {
return false;
}
$request->json()->replace($data);
$request->request->replace($data);
return true;
}
}