Express Elwadi API v1.0 Stable
واجهة برمجية تتيح لك ربط موقعك أو تطبيقك بنظام حجز إكسبريس الوادى. يمكنك البحث عن الرحلات، اختيار المقاعد، الحجز، الاسترجاع، ونقل التذاكر.
Base URL
https://clinic.khobraelmal.com/api/v1
كيف تبدأ؟
نظام الحسابات
حسابك يعمل بنظام دائن ومدين:
| العملية | التأثير |
|---|---|
| شحن الرصيد | ➕ يزيد رصيدك (دائن) |
| حجز تذكرة | ➖ ينقص رصيدك (مدين) |
| استرجاع تذكرة | ➕ يرجع المبلغ - الغرامة (دائن) |
| نقل تذكرة | ➖ غرامة النقل + فرق السعر (مدين) |
مهم
لا يمكن الحجز إذا كان رصيدك غير كافي. تواصل مع الإدارة لشحن رصيدك.
المصادقة مطلوب
جميع طلبات API تحتاج مصادقة. أرسل API Key و Secret في الـ Headers:
الطريقة 1: Headers منفصلة
X-API-KEY: your_api_key
X-API-SECRET: your_api_secret
الطريقة 2: Bearer Token
Authorization: Bearer your_api_key:your_api_secret
مثال cURL
curl -X GET "https://clinic.khobraelmal.com/api/v1/stations" \
-H "X-API-KEY: abc123" \
-H "X-API-SECRET: xyz789"
مثال JavaScript
const response = await fetch('https://clinic.khobraelmal.com/api/v1/stations', {
headers: {
'X-API-KEY': 'your_api_key',
'X-API-SECRET': 'your_api_secret',
}
});
أمان
لا تشارك API Key في كود الـ Frontend. استخدمه من السيرفر فقط (Backend).
الأخطاء
كل الاستجابات ترجع بصيغة JSON موحدة:
{
"success": false,
"message": "وصف الخطأ بالعربي"
}
| الكود | المعنى |
|---|---|
200 | نجاح |
400 | بيانات ناقصة أو غير صحيحة |
401 | API Key غير صحيح |
404 | غير موجود |
500 | خطأ في السيرفر |
المحطات
الاستجابة
{
"success": true,
"data": [
{
"id": 1,
"name": "الترجمان",
"code": "CS2",
"address": "السبتية - ميناء القاهرة البرى",
"phone": "0123456789",
"latitude": 30.0626,
"longitude": 31.2497,
"city_name": "القاهرة",
"governorate_name": "القاهرة"
}
]
}
الخطوط
{
"data": [
{ "id": 1, "name": "القاهرة - الخارجة", "code": "CAI-KHG" }
]
}
الرحلات
المعاملات (Query Parameters)
| المعامل | النوع | مطلوب | الوصف |
|---|---|---|---|
from_station_id | integer | ✅ | معرف محطة القيام |
to_station_id | integer | ✅ | معرف محطة الوصول |
date | string | ✅ | التاريخ (YYYY-MM-DD) |
الاستجابة
{
"data": [
{
"instance_id": 15,
"trip_date": "2026-03-15",
"departure_time": "06:00:00",
"route_name": "القاهرة - الخارجة",
"service_class_name": "VIP",
"price": 140,
"available_seats": 22,
"total_seats": 33
}
]
}
المقاعد
الاستجابة
{
"data": [
{
"id": 42,
"seat_number": 5,
"row_number": 2,
"column_number": 1,
"cell_type": "seat",
"status": "available" // available | confirmed | pending | blocked
}
]
}
أنواع الخلايا (cell_type)
seat مقعد عادي — door باب — driver سائق — aisle ممر — stairs سلم — bathroom حمام — empty فارغ
حجز تذكرة
الطلب (Body JSON)
| الحقل | النوع | مطلوب | الوصف |
|---|---|---|---|
instance_id | integer | ✅ | معرف الرحلة |
from_station_id | integer | ✅ | محطة القيام |
to_station_id | integer | ✅ | محطة الوصول |
seat_ids | array | ✅ | معرفات المقاعد [42, 43] |
passenger_name | string | — | اسم الراكب |
passenger_phone | string | — | تليفون الراكب |
ticket_category_id | integer | — | فئة التذكرة (لو فيه خصم) |
مثال
curl -X POST "https://clinic.khobraelmal.com/api/v1/bookings" \
-H "X-API-KEY: abc123" \
-H "X-API-SECRET: xyz789" \
-H "Content-Type: application/json" \
-d '{
"instance_id": 15,
"from_station_id": 1,
"to_station_id": 4,
"seat_ids": [42, 43],
"passenger_name": "أحمد محمد",
"passenger_phone": "01012345678"
}'
الاستجابة
{
"success": true,
"message": "تم الحجز بنجاح",
"data": {
"booking_number": "BK202603151234",
"booking_id": 58,
"total_amount": 280,
"tickets": [
{ "ticket_id": 101, "seat_number": 5, "price": 140 },
{ "ticket_id": 102, "seat_number": 6, "price": 140 }
]
}
}
الرصيد
المبلغ يتخصم من رصيدك تلقائياً. لو رصيدك غير كافي هيرجع خطأ 400.
عرض حجز
يرجع تفاصيل الحجز مع كل التذاكر.
{
"data": {
"booking_number": "BK202603151234",
"total_amount": 280,
"trip_date": "2026-03-15",
"route_name": "القاهرة - الخارجة",
"tickets": [
{ "id": 101, "seat_number": 5, "ticket_status": "confirmed", "final_price": 140 }
]
}
}
استرجاع تذكرة
يلغي كل التذاكر المؤكدة في الحجز. المبلغ يرجع لرصيدك (ناقص الغرامة لو فيه).
{
"success": true,
"message": "تم إلغاء الحجز"
}
قيود الاسترجاع
لا يمكن الاسترجاع بعد قفل التابلو من المحطة. وقد تكون هناك غرامة استرجاع حسب إعدادات النظام.
استرجاع تذاكر محددة
// Body
{ "ticket_ids": [101, 102] }
// Response
{ "success": true, "data": { "refund_amount": 280 } }
نقل تذكرة
نقل تذكرة من رحلة لرحلة تانية (نفس الخط أو خط مختلف).
حساب التكلفة
غرامة نقل ثابتة + فرق السعر (لو الجديدة أغلى يتخصم، لو أرخص يرجع الفرق لرصيدك).
حالياً النقل متاح عبر واجهة العميل الأونلاين فقط. لو محتاج API endpoint مخصص للنقل تواصل مع الإدارة.
الرصيد
{ "success": true, "data": { "balance": 4500.00 } }
المعاملات
{
"data": [
{
"booking_number": "BK202603151234",
"total_amount": 280,
"booking_type": "confirmed",
"trip_date": "2026-03-15",
"route_name": "القاهرة - الخارجة",
"created_at": "2026-03-12 14:30:00"
}
],
"page": 1
}
سيناريو حجز كامل
الخطوة 1: جيب المحطات
GET /api/v1/stations
الخطوة 2: ابحث عن رحلات
GET /api/v1/trips?from_station_id=1&to_station_id=4&date=2026-03-15
الخطوة 3: شوف المقاعد المتاحة
GET /api/v1/trips/15/seats?from_station_id=1
اعرض المقاعد حسب row_number و column_number. المقاعد اللي status = "available" هي اللي يقدر العميل يختارها.
الخطوة 4: احجز
POST /api/v1/bookings
{
"instance_id": 15,
"from_station_id": 1,
"to_station_id": 4,
"seat_ids": [42],
"passenger_name": "أحمد محمد",
"passenger_phone": "01012345678"
}
الخطوة 5: (اختياري) استرجاع
POST /api/v1/bookings/BK202603151234/cancel
نصيحة
احفظ booking_number و ticket_id عندك عشان تقدر تعمل استرجاع أو عرض الحجز لاحقاً.
أكواد جاهزة
JavaScript / Node.js
class ExpressElwadiAPI {
constructor(apiKey, apiSecret) {
this.base = 'https://clinic.khobraelmal.com/api/v1';
this.headers = {
'Content-Type': 'application/json',
'X-API-KEY': apiKey,
'X-API-SECRET': apiSecret,
};
}
async getStations() {
const r = await fetch(this.base + '/stations', { headers: this.headers });
return r.json();
}
async searchTrips(from, to, date) {
const r = await fetch(
this.base + `/trips?from_station_id=${from}&to_station_id=${to}&date=${date}`,
{ headers: this.headers }
);
return r.json();
}
async getSeats(instanceId, fromStationId) {
const r = await fetch(
this.base + `/trips/${instanceId}/seats?from_station_id=${fromStationId}`,
{ headers: this.headers }
);
return r.json();
}
async book(instanceId, from, to, seatIds, name, phone) {
const r = await fetch(this.base + '/bookings', {
method: 'POST',
headers: this.headers,
body: JSON.stringify({
instance_id: instanceId,
from_station_id: from,
to_station_id: to,
seat_ids: seatIds,
passenger_name: name,
passenger_phone: phone,
})
});
return r.json();
}
async cancel(bookingNumber) {
const r = await fetch(this.base + `/bookings/${bookingNumber}/cancel`, {
method: 'POST', headers: this.headers
});
return r.json();
}
async getBalance() {
const r = await fetch(this.base + '/account/balance', { headers: this.headers });
return r.json();
}
}
// الاستخدام
const api = new ExpressElwadiAPI('your_key', 'your_secret');
const trips = await api.searchTrips(1, 4, '2026-03-15');
const booking = await api.book(15, 1, 4, [42], 'أحمد', '01012345678');
PHP
class ExpressElwadiAPI {
private $base = 'https://clinic.khobraelmal.com/api/v1';
private $key, $secret;
public function __construct($key, $secret) {
$this->key = $key;
$this->secret = $secret;
}
private function request($method, $path, $body = null) {
$ch = curl_init($this->base . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-API-KEY: ' . $this->key,
'X-API-SECRET: ' . $this->secret,
],
]);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
public function searchTrips($from, $to, $date) {
return $this->request('GET', "/trips?from_station_id=$from&to_station_id=$to&date=$date");
}
public function book($instanceId, $from, $to, $seatIds, $name, $phone) {
return $this->request('POST', '/bookings', [
'instance_id' => $instanceId,
'from_station_id' => $from,
'to_station_id' => $to,
'seat_ids' => $seatIds,
'passenger_name' => $name,
'passenger_phone' => $phone,
]);
}
}