Cách làm api phạt nguội bằng laravel
Trong những năm gần đây, vấn đề phạt nguội giao thông đang được rất nhiều tài xế quan tâm. Việc tra cứu thông tin vi phạm không chỉ giúp người dùng nắm rõ tình trạng phương tiện, mà còn tránh được những rắc rối khi đi đăng kiểm hoặc lưu thông trên đường. Thay vì phải truy cập thủ công vào website của CSGT, chúng ta hoàn toàn có thể xây dựng một API tra cứu phạt nguội để tích hợp trực tiếp vào hệ thống quản lý, ứng dụng di động hay website của mình.
Trong bài viết này, tôi sẽ hướng dẫn bạn cách xây dựng một API check phạt nguội đơn giản bằng PHP Laravel như trang "Check Phạt Nguội", bao gồm các bước từ cấu hình dự án, xử lý request/response đến hiển thị kết quả. Với cách làm này, bạn có thể dễ dàng tự động hóa việc tra cứu, tiết kiệm thời gian và tăng tính tiện lợi cho người dùng.
<?php namespace Dev\KOMA\Services;
class PhatNguoiService {
private string $url = "https://www.csgt.vn/tra-cuu-phuong-tien-vi-pham.html";
private string $captchaFile;
private string $search_url = "https://www.csgt.vn/?mod=contact&task=tracuu_post&ajax";
private ?string $sessionId = null; // giữ PHPSESSID
public function __construct(string $storageDir = __DIR__) {
$this->captchaFile = $storageDir . "/captcha_" . uniqid() . ".png";
}
/**
* @throws \RuntimeException
* @return array
*/
public function run($bienKS, $xe): array {
try {
// 1. Get main page HTML and sessionId
$html = $this->fetchMainPage($this->url);
// 2. Get captcha image URL
$imgUrl = $this->getCaptchaUrl($html);
// 3. Download captcha
$this->downloadCaptcha($imgUrl);
// 4. Send captcha to get ID
$responseCaptcha = $this->sendCaptcha();
$captcha_id = $this->cutResponse($responseCaptcha);
// 5. Get captcha result
$captchaText = $this->getCaptchaResult($captcha_id);
$captcha = $this->cutResponse($captchaText);
// 6. Submit form
$response = $this->submitForm($bienKS, $xe, $captcha);
if ($response === '404') {
throw new \RuntimeException('Không tìm thấy kết quả');
}
$result = json_decode($response, true);
if (!is_array($result)) {
throw new \RuntimeException('Kết quả không phải là JSON hợp lệ');
}
if (!isset($result['success'])) {
throw new \RuntimeException('Thiếu trường success trong kết quả');
}
if ($result['success'] !== "true") {
throw new \RuntimeException('Không có kết quả hợp lệ');
}
$html = $this->fetchMainPage($result['href'], false);
$data = $this->parseBodyPrint($html);
// Clean up captcha file after processing
if (file_exists($this->captchaFile)) {
unlink($this->captchaFile);
}
return $data;
} catch (\Exception $e) {
// Clean up file even if there's an error
if (file_exists($this->captchaFile)) {
unlink($this->captchaFile);
}
throw $e; // Re-throw the exception after cleanup
}
}
private function parseBodyPrint(string $html): array {
$doc = new \DOMDocument();
libxml_use_internal_errors(true);
$doc->loadHTML($html);
libxml_clear_errors();
$xpath = new \DOMXPath($doc);
// tìm div có id=bodyPrint123
$target = $xpath->query("//div[@id='bodyPrint123']")->item(0);
if (!$target) {
return ["error" => "Không tìm thấy bodyPrint123"];
}
$data = [];
$rows = $xpath->query(".//div[contains(concat(' ', normalize-space(@class), ' '), ' form-group ')]", $target);
foreach ($rows as $row) {
$labelNode = $xpath->query(".//label//span", $row)->item(0);
$valueNode = $xpath->query(".//div[contains(@class,'col-md-9')]", $row)->item(0);
if ($labelNode && $valueNode) {
$label = trim($labelNode->textContent);
$value = trim($valueNode->textContent);
$data[$label] = $value;
} else {
// những group không có label mà chỉ có text (ví dụ địa chỉ, đơn vị xử lý)
$text = trim($row->textContent);
if ($text !== '') {
$data[] = $text;
}
}
}
return $data;
}
private function fetchMainPage($url, $flag = true) {
$ch = curl_init($url);
$options = [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HEADER => true, // để tách header/body khi flag = true
CURLOPT_USERAGENT => "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
];
if (!$flag && $this->sessionId) {
// Khi flag = false thì gửi kèm sessionId
$options[CURLOPT_HTTPHEADER] = [
"Cookie: PHPSESSID={$this->sessionId}"
];
// Lúc này không cần tách header ra nữa
$options[CURLOPT_HEADER] = false;
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
curl_close($ch);
if ($flag) {
[$header, $body] = explode("\r\n\r\n", $response, 2);
if (preg_match('/PHPSESSID=([^;]+)/', $header, $m)) {
$this->sessionId = $m[1];
}
return $body;
}
return $response;
}
private function submitForm(string $bienSo, string $loaiXe, string $captchaText): string {
$postFields = [
'BienKS' => $bienSo,
'Xe' => $loaiXe,
'captcha' => $captchaText,
'ipClient' => '9.9.9.91',
'cUrl' => '1'
];
$data = http_build_query($postFields);
// Dùng PHPSESSID lấy từ fetchMainPage
$cookie = $this->sessionId ? "PHPSESSID={$this->sessionId}" : "";
$ch = curl_init($this->search_url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $data,
CURLOPT_HTTPHEADER => [
"Content-Type: application/x-www-form-urlencoded; charset=UTF-8",
"Referer: " . $this->url,
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"X-Requested-With: XMLHttpRequest",
"Accept: */*",
"Cookie: $cookie",
],
]);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
private function getCaptchaUrl(string $html): string {
$dom = new \DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML($html);
libxml_clear_errors();
$xpath = new \DOMXPath($dom);
$nodeList = $xpath->query("//img[@id='imgCaptcha']");
if ($nodeList === false || $nodeList->length === 0) {
throw new \RuntimeException("Không tìm thấy captcha trong HTML");
}
/** @var \DOMElement $imgNode */
$imgNode = $nodeList->item(0);
$imgSrc = $imgNode->getAttribute("src");
$parsedUrl = parse_url($this->url);
$base = $parsedUrl['scheme'] . "://" . $parsedUrl['host'];
return (strpos($imgSrc, "http") !== 0)
? rtrim($base, "/") . "/" . ltrim($imgSrc, "/")
: $imgSrc;
}
private function downloadCaptcha(string $imgUrl): void {
$ch = curl_init($imgUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_COOKIE => "PHPSESSID={$this->sessionId}" // tải captcha cũng phải kèm session
]);
$imgData = curl_exec($ch);
curl_close($ch);
file_put_contents($this->captchaFile, $imgData);
}
private function sendCaptcha(): string {
$ch = curl_init("https://captcha.lapro.vn/in.php");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
"file" => new \CURLFile($this->captchaFile),
],
]);
$responseCaptcha = curl_exec($ch);
curl_close($ch);
return $responseCaptcha;
}
private function cutResponse(string $responseCaptcha): string {
if (strpos($responseCaptcha, "OK|") === 0) {
return explode("|", $responseCaptcha)[1];
}
die("Lỗi gửi captcha: $responseCaptcha<br>");
}
private function getCaptchaResult(string $captcha_id): string {
for ($i = 0; $i < 10; $i++) {
sleep(3);
$ch = curl_init("https://captcha.lapro.vn/res.php?id=" . trim($captcha_id));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
if ($result && $result !== "NOT_READY") {
return $result;
}
}
return "NOT_READY";
}
}
>>>Xem kết quả tại: Kiểm tra phạt nguội
Chia sẻ bài viết
Bình luận
Chia sẻ cảm nghĩ của bạn về bài viết này.