bazdidfani / laravel-api-client
Laravel client for the Bazdidfani technical inspection and self-statement webservice API.
Requires
- php: ^8.1|^8.2|^8.3
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^9.6|^10.0|^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-09-20 12:09:24 UTC
README
کلاینت Laravel برای دریافت صفحهبندیشدهٔ بازدیدهای فنی و خوداظهاریها از وبسرویس بازدید فنی.
نصب از Packagist
بعد از انتشار پکیج در Packagist، مصرفکننده فقط این فرمان را اجرا میکند:
composer require bazdidfani/laravel-api-client:^1.1
Laravel سرویسپروایدر و Facade را با package discovery ثبت میکند.
نصب محلی با Path Repository
اگر این پوشه داخل پروژهٔ مصرفکننده قرار دارد، به composer.json آن پروژه اضافه کنید:
{
"repositories": [
{
"type": "path",
"url": "packages/bazdidfani-api-client",
"options": {
"symlink": true
}
}
]
}
سپس اجرا کنید:
composer require bazdidfani/laravel-api-client:@dev
تنظیمات
در .env پروژهٔ مصرفکننده:
BAZDIDFANI_API_BASE_URL=https://api.example.com BAZDIDFANI_API_TOKEN=bdf_replace-with-the-issued-site-token BAZDIDFANI_ORGANIZATION_HEADER=X-Organization-Code BAZDIDFANI_API_TIMEOUT=15 BAZDIDFANI_API_RETRY_TIMES=2 BAZDIDFANI_API_RETRY_SLEEP=200 BAZDIDFANI_RESUBMISSION_COOLDOWN_HOURS=24
برای هر سایت باید از سرور بازدید فنی یک توکن اختصاصی bdf_... دریافت کنید. این توکن به هیچ سازمان خاصی محدود نیست و برای هر کد سازمانی معتبر در سرور قابل استفاده است.
کد سازمان در .env نگهداری نمیشود و باید در هر فراخوانی متد بهصورت اجباری ارسال شود. تنظیم BAZDIDFANI_ORGANIZATION_HEADER فقط نام هدری است که پکیج کد سازمان را داخل آن میفرستد؛ اختیاری است و در صورت حذف، مقدار X-Organization-Code استفاده میشود. برای سناریوی عادی نیازی به تعریف این متغیر ندارید و فقط زمانی آن را تنظیم کنید که سرور API نام هدر دیگری تعیین کرده باشد.
انتشار فایل config اختیاری است:
php artisan vendor:publish --tag=bazdidfani-api-config
استفاده با Dependency Injection
use Bazdidfani\ApiClient\BazdidfaniApiClient; final class InspectionController { public function __construct( private readonly BazdidfaniApiClient $bazdidfaniApi, ) {} public function index(): array { return $this->bazdidfaniApi->technicalInspections('12345678', [ 'page' => 1, 'per_page' => 20, 'status' => 1, 'query' => 'راننده آزمایشی', 'smart_number' => 1234567, 'date_from' => '2026-01-01', 'date_to' => '2026-12-31', ]); } }
دریافت خوداظهاریها:
$result = $bazdidfaniApi->selfStatements('12345678', [ 'page' => 1, 'per_page' => 20, ]);
یا با Facade:
use Bazdidfani\ApiClient\Facades\BazdidfaniApi; $result = BazdidfaniApi::technicalInspections('12345678', ['per_page' => 50]);
پارامتر query در فیلدهای عمومی بازدید جستجو میکند و smart_number فقط شماره هوشمند هفترقمی دقیق را برمیگرداند. هر دو پارامتر برای متدهای technicalInspections و selfStatements قابل استفاده هستند.
خروجی هر متد آرایهٔ JSON پاسخ سرور، شامل data، links، meta، success و message است. پاسخهای ناموفق با Illuminate\Http\Client\RequestException گزارش میشوند.
ثبت بازدید فنی
برای ثبت یک بازدید فنی جدید برای شرکت متعلق به organizationCode:
$result = $bazdidfaniApi->submitTechnicalInspection('12345678', [ 'usage' => 'freighter', // یا 'passenger' 'company_usage' => 1, // 1=باربری، 2=مسافربری، 3=هر دو — مطابق companies.company_usage 'user_type' => 'company', 'smart_number' => '1234567', 'loader_code' => 100, 'technical_manager_national_code' => '0098765432', // اختیاری: 'branch_code' => 1, // پیشفرض ۱ (شرکت مادر)؛ برای ثبت روی یک شعبهٔ خاص ارسال شود 'driver_national_code' => '0012345678', 'driver_phone_number' => '09120000000', 'Insurance_validity' => '2027-01-01', 'validity_technical_examination' => '2027-01-01', 'driver_health_card_validity' => '2027-01-01', 'driver_certificate_validity' => '2027-01-01', 'driver_birthdate' => '1370-01-01', ]);
توکن به هیچ سازمان یا شعبهای محدود نیست؛ اگر شرکت مقصد چند شعبه (چند branch_code با یک organization_code) داشته باشد و branch_code ارسال نشود، بازدید برای شعبهٔ مادر (branch_code=1) ثبت میشود.
بررسی مجاز بودن ثبت بازدید جدید
اگر آخرین بازدید این ناوگان هنوز کد سباف نگرفته باشد، خودِ vehicle() هم بهجای دادهٔ ناوگان TechnicalInspectionNotAllowedException («کد سباف برای این ناوگان موجود نیست.») پرتاب میکند — چه هیچ بازدیدی تا کنون ثبت نشده باشد، چه بازدید فعلی هنوز در حال انجام باشد. برای دسترسی به دادهٔ خام در همین حالت (مثلاً external_technical_inspection_id)، exception را catch کنید و از vehicleData() استفاده کنید:
use Bazdidfani\ApiClient\Exceptions\TechnicalInspectionNotAllowedException; try { $vehicle = $bazdidfaniApi->vehicle('12345678', 1234567); } catch (TechnicalInspectionNotAllowedException $exception) { $vehicleData = $exception->vehicleData(); }
علاوه بر این، متد جداگانهٔ ensureNewTechnicalInspectionAllowed بررسی میکند که ثبت یک بازدید فنی جدید برای این ناوگان نزد همین شرکت مجاز است یا نه، و submitTechnicalInspection هم پیش از ارسال درخواست همین متد را صدا میزند:
- اگر آخرین بازدید کد سباف دارد، باید حداقل
BAZDIDFANI_RESUBMISSION_COOLDOWN_HOURSساعت (پیشفرض ۲۴ ساعت) ازsabaf_received_atگذشته باشد؛ در غیر این صورتTechnicalInspectionNotAllowedExceptionپرتاب میشود. - اگر آخرین بازدید هنوز کد سباف ندارد (یعنی بازدید فعال/در حال انجام است)، ثبت بازدید جدید مجاز نیست و
TechnicalInspectionNotAllowedExceptionپرتاب میشود. - اگر تاکنون هیچ بازدیدی برای این ناوگان نزد این شرکت ثبت نشده باشد، ثبت بدون مانع مجاز است.
use Bazdidfani\ApiClient\Exceptions\TechnicalInspectionNotAllowedException; try { $bazdidfaniApi->ensureNewTechnicalInspectionAllowed('12345678', 1234567); } catch (TechnicalInspectionNotAllowedException $exception) { // ثبت بازدید جدید برای این ناوگان مجاز نیست: $exception->getMessage() $vehicleData = $exception->vehicleData(); // همان $vehicle['data'] که باعث خطا شده (مثلاً external_technical_inspection_id) }
ابطال بازدید فعلی و ثبت اجباری بازدید جدید
اگر با وجود بازدید فعال (یا نگذشتن بازهٔ خنکسازی)، کاربر بخواهد مانند مکانیزم فعلی سیستم بازدید قبلی را ابطال و بازدید جدید را ثبت کند، آرگومان forceCreate را true ارسال کنید. در این حالت هیچ استعلامی از vehicle گرفته نمیشود، بررسی بهطور کامل رد میشود، و فیلد force_create: true هم به payload ارسالی اضافه میشود. پیام خطای TechnicalInspectionNotAllowedException مربوط به بازدید فعال هم کاربر را به همین راه راهنمایی میکند: «برای ابطال یا ادامهٔ بازدید در سامانهٔ بازدید فنی اقدام کنید.»
$result = $bazdidfaniApi->submitTechnicalInspection('12345678', [ 'usage' => 'freighter', // ... ], forceCreate: true);
پیشفرض این آرگومان false است.
مدیران فنی، ناوگان و دادههای مرجع
// مدیران فنی فعالِ شرکت $managers = $bazdidfaniApi->technicalManagers('12345678', ['per_page' => 20]); // ناوگان شرکت، با امکان جستوجو روی شمارهٔ هوشمند/پلاک $vehicles = $bazdidfaniApi->fleet('12345678', ['query' => '1234567']); // اطلاعات کامل یک ناوگان با کد هوشمند: مشخصات ناوگان + کد سباف و مدیر فنیِ آخرین بازدید + اطلاعات شرکت // توجه: اگر آخرین بازدید این ناوگان هنوز کد سباف نگرفته باشد، TechnicalInspectionNotAllowedException پرتاب میشود // (بخش «بررسی مجاز بودن ثبت بازدید جدید» را ببینید) $vehicle = $bazdidfaniApi->vehicle('12345678', 1234567); // دادههای مرجع — مستقل از شرکت، اما همچنان نیاز به هدر کد سازمان معتبر دارند $cities = $bazdidfaniApi->cities('12345678', ['query' => 'تهران']); $states = $bazdidfaniApi->states('12345678'); $loaderTypes = $bazdidfaniApi->loaderTypes('12345678');
استعلام بازدید فنی از سازمان
ابتدا اطلاعات ناوگان را با vehicle دریافت کنید. وقتی sabaf_code و
sabaf_received_at مقدار دارند، فیلد
external_technical_inspection_id شناسهٔ بازدید ثبتشده در سازمان است و باید
برای استعلام استفاده شود. اگر هنوز کد سباف صادر نشده باشد، vehicle() مقداری
برنمیگرداند و TechnicalInspectionNotAllowedException پرتاب میکند.
$vehicle = $bazdidfaniApi->vehicle('12345678', 1234567); $externalTechnicalInspectionId = $vehicle['data']['external_technical_inspection_id'];
برای بازدید باری از متد زیر استفاده کنید:
$result = $bazdidfaniApi->technicalInspectionInquiryCargo( '12345678', $externalTechnicalInspectionId, );
و برای بازدید مسافری:
$result = $bazdidfaniApi->technicalInspectionInquiryPassenger( '12345678', $externalTechnicalInspectionId, );
هر دو متد درخواست POST را با هدر کد سازمان ارسال میکنند. شناسه باید UUID
معتبر، متعلق به همان سازمان، و مطابق با نوع ناوگان انتخابشده باشد؛ در غیر این
صورت پاسخ ناموفق از طریق RequestException برگردانده میشود.
loaderTypes برای پر کردن loader_code هنگام فراخوانی submitTechnicalInspection استفاده میشود.
همهٔ متدهای بالا با Facade هم در دسترساند؛ از جمله
BazdidfaniApi::technicalInspectionInquiryCargo(...) و
BazdidfaniApi::technicalInspectionInquiryPassenger(...).
نمونه پاسخها
پاسخ technicalInspections
{
"data": [
{
"id": 100,
"code": "TECH-100",
"kind": "technical_inspection",
"self_statement": false,
"status": {
"code": 4,
"title": "در حال انجام بازدید فنی"
},
"type": 1,
"description": null,
"vehicle": {
"smart_number": "1234567",
"plate": {
"first_number": "12",
"second_number": "345",
"third_character": "ب",
"fourth_number": "67"
},
"loader_type": "بارگیر",
"insurance_validity": "2027-01-01 00:00:00"
},
"driver": {
"national_code": "0012345678",
"full_name": "راننده آزمایشی",
"father_name": "نام پدر",
"health_card_validity": "2027-01-01 00:00:00",
"smart_card_validity": "2027-01-01 00:00:00"
},
"technical_manager_national_code": "0098765432",
"technical_inspection": {
"id": 200,
"status": "in_progress",
"description": null,
"latitude": "35.68920000",
"longitude": "51.38900000",
"external_id": null,
"submitted_at": null,
"started_at": "2026-09-02T10:30:00.000000Z"
},
"organization": {
"code": "12345678",
"name": "شرکت نمونه"
},
"created_at": "2026-09-02T10:00:00+00:00",
"updated_at": "2026-09-02T10:30:00+00:00"
}
],
"links": {
"first": "https://api.example.com/api/v1/webservice/technical-inspections?page=1",
"last": "https://api.example.com/api/v1/webservice/technical-inspections?page=3",
"prev": null,
"next": "https://api.example.com/api/v1/webservice/technical-inspections?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 3,
"per_page": 20,
"to": 20,
"total": 42
},
"success": true,
"message": "فهرست بازدیدهای فنی با موفقیت دریافت شد."
}
پاسخ selfStatements
ساختار صفحهبندی و مشخصات خودرو و راننده مشابه پاسخ بالا است. تفاوت رکورد خوداظهاری به شکل زیر است:
{
"data": [
{
"id": 101,
"code": "SELF-101",
"kind": "self_statement",
"self_statement": true,
"status": {
"code": 10,
"title": "در حال انجام خوداظهاری"
},
"type": 1,
"description": null,
"vehicle": {
"smart_number": "1234567",
"plate": {
"first_number": "12",
"second_number": "345",
"third_character": "ب",
"fourth_number": "67"
},
"loader_type": "بارگیر",
"insurance_validity": "2027-01-01 00:00:00"
},
"driver": {
"national_code": "0012345678",
"full_name": "راننده آزمایشی",
"father_name": "نام پدر",
"health_card_validity": "2027-01-01 00:00:00",
"smart_card_validity": "2027-01-01 00:00:00"
},
"technical_manager_national_code": "0098765432",
"technical_inspection": null,
"organization": {
"code": "12345678",
"name": "شرکت نمونه"
},
"created_at": "2026-09-02T10:00:00+00:00",
"updated_at": "2026-09-02T10:30:00+00:00"
}
],
"links": {
"first": "https://api.example.com/api/v1/webservice/self-statements?page=1",
"last": "https://api.example.com/api/v1/webservice/self-statements?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"per_page": 20,
"to": 1,
"total": 1
},
"success": true,
"message": "فهرست خوداظهاریها با موفقیت دریافت شد."
}
پاسخ submitTechnicalInspection
{
"success": true,
"message": "بازدید فنی با موفقیت ثبت شد.",
"data": {
"bazdidfani": {
"id": 512,
"code": "TECH-512"
}
}
}
پاسخ technicalManagers
{
"success": true,
"message": "فهرست مدیران فنی شرکت با موفقیت دریافت شد.",
"data": [
{
"id": 7,
"national_code": "0098765432",
"full_name": "مدیر فنی نمونه",
"phone": "09120000000",
"capacity": 10,
"passenger_capacity": 0,
"freighter_capacity": 10,
"type": 1,
"start_cooperate": "2026-01-01",
"end_cooperate": "2027-01-01",
"status": 1,
"company": {
"code": "12345678",
"name": "شرکت نمونه"
}
}
],
"links": { "first": "...", "last": "...", "prev": null, "next": null },
"meta": { "current_page": 1, "per_page": 15, "total": 1 }
}
پاسخ fleet
{
"success": true,
"message": "فهرست ناوگان شرکت با موفقیت دریافت شد.",
"data": [
{
"id": 3,
"status": "1",
"vehicle": {
"smart_number": "1234567",
"plate": {
"first_number": "12",
"second_number": "345",
"third_character": "ب",
"fourth_number": "67"
},
"usage": "freighter",
"VIN": "VIN-1234567",
"date_made": "1400",
"validity_technical_examination": "2027-01-01T00:00:00.000000Z",
"loader": { "code": 100, "name": "بارگیر آزمایشی" }
},
"truck_info": {
"capacity": 20000,
"insurance_validity": "2027-01-01T00:00:00.000000Z",
"insurance_number": null,
"owner_phone_number": "09120000000",
"chassis_number": null,
"document_number": null,
"document_date": null
}
}
],
"links": { "first": "...", "last": "...", "prev": null, "next": null },
"meta": { "current_page": 1, "per_page": 15, "total": 1 }
}
پاسخ vehicle
اگر تا کنون بازدیدی برای این ناوگان ثبت نشده باشد یا آخرین بازدید هنوز کد سباف نگرفته باشد، این JSON برگردانده نمیشود و TechnicalInspectionNotAllowedException پرتاب میشود (بخش «بررسی مجاز بودن ثبت بازدید جدید» را ببینید). اگر ناوگان با این کد هوشمند برای کد سازمانی ارسالی یافت نشود، پاسخ 404 است (RequestException).
sabaf_received_at تاریخ دریافت کد سباف از سازمان است (از بازدید فنیِ مرتبط با آخرین بازدید). برای بازدیدهایی که از مسیر خوداظهاری کد سباف گرفتهاند و بازدید فنیِ رسمی ندارند، این مقدار null است، هرچند sabaf_code مقدار داشته باشد.
external_technical_inspection_id شناسهٔ UUID بازدید فنی در سازمان است. فقط
وقتی بازدید فنیِ مرتبط وجود داشته باشد مقدار دارد و برای فراخوانی
technicalInspectionInquiryCargo یا technicalInspectionInquiryPassenger استفاده
میشود.
{
"success": true,
"message": "اطلاعات ناوگان با موفقیت دریافت شد.",
"data": {
"vehicle": {
"smart_number": 1234567,
"plate": {
"first_number": "12",
"second_number": "345",
"third_character": "ب",
"fourth_number": "67"
},
"usage": "freighter",
"VIN": "VIN-1234567",
"date_made": "1400",
"validity_technical_examination": "2027-01-01T00:00:00.000000Z",
"loader": { "code": 100, "name": "بارگیر آزمایشی" },
"capacity": 20000,
"insurance_validity": "2027-01-01T00:00:00.000000Z",
"insurance_number": null,
"owner_phone_number": "09120000000",
"chassis_number": null,
"document_number": null,
"document_date": null
},
"sabaf_code": "SABAF-987654",
"sabaf_received_at": "2026-06-01 10:00:00",
"external_technical_inspection_id": "c60f5b5d-6390-4b0d-9d3c-4d484ecfa6e7",
"last_visit": {
"code": "TECH-512",
"status": 4,
"type": 1,
"created_at": "2026-09-02T10:00:00+00:00"
},
"technical_manager": {
"id": 7,
"national_code": "0098765432",
"full_name": "مدیر فنی نمونه",
"phone": "09120000000",
"capacity": 10,
"passenger_capacity": 0,
"freighter_capacity": 10,
"type": 1
},
"company": {
"organization_code": "12345678",
"branch_code": 1,
"name": "شرکت نمونه",
"company_usage": 1,
"ceo_name": "مدیرعامل نمونه",
"ceo_phone": "09120000001",
"coordinator_name": "هماهنگکننده نمونه",
"coordinator_phone": "09120000002",
"address": "آدرس نمونه",
"city": { "code": 1234, "name": "تهران" },
"company_national_code": "14001234567",
"postal_code": "1234567890",
"company_website": null,
"company_fax": null,
"company_phone": "02100000000"
}
}
}
پاسخ cities / states / loaderTypes
{
"success": true,
"message": "فهرست شهرها با موفقیت دریافت شد.",
"data": [
{
"id": 1,
"code": "1234",
"name": "تهران",
"state": { "code": "07", "name": "تهران" }
}
],
"links": { "first": "...", "last": "...", "prev": null, "next": null },
"meta": { "current_page": 1, "per_page": 15, "total": 1 }
}
پاسخ ناموفق
برای نمونه، اگر کد سازمان معتبر نباشد سرور پاسخ 403 برمیگرداند:
{
"success": false,
"message": "دسترسی برای کد سازمانی ارسالشده مجاز نیست."
}
جزئیات پاسخ ناموفق از exception قابل دریافت است:
use Illuminate\Http\Client\RequestException; try { $result = $bazdidfaniApi->technicalInspections('12345678'); } catch (RequestException $exception) { $status = $exception->response->status(); $error = $exception->response->json(); }
اجرای تست پکیج
composer install
composer test