دليل واجهات برمجة تطبيقات الإدارة (FreeNet Manager Suite)

توثيق مرجعي شامل ومتكامل لكافة وحدات ونقاط الاتصال الخاصة بلوحة تحكم المدير، وتطبيق الهاتف الذكي للأندرويد (المطور في مسار /var/www/html/android_app_export/app/src). يغطي كافة جوانب إدارة المشتركين بالصور، التجديدات، تغيير الباقات، فواتير المبيعات، كروت الشحن، أجهزة الشبكة، والتقارير المالية المتقدمة.

نقاط الاتصال الموثقة: 108
وحدات التحكم المغطاة: 13 وحدات (15 Controllers)
لغات البرمجة المدعومة: Kotlin, cURL, JS, Flutter, PHP

🔐 المصادقة والورديات والملف الشخصي

AuthController
5 نقطة اتصال

إدارة عمليات تسجيل دخول المديرين والمشرفين، والتحقق الصارم من جداول الورديات (Shift Enforcement)، وإصدار توكن الأجهزة المتعددة (Multi-Device Bearer Token)، وإدارة الملف الشخصي وصور العرض.

POST /api/manager/login تسجيل دخول المدير / المشرف عام (بدون توكن)

التحقق من بيانات الدخول (اسم المستخدم أو رقم الهاتف المحمول محلياً أو دولياً)، التحقق من تفعيل الحساب، فحص جدول الوردية ومواعيد العمل الرسمية، وتسجيل مخالفات الدخول خارج الوردية، ثم إصدار توكن Bearer مخصص للجهاز.

الصلاحية المطلوبة: متاح للجميع البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
login body string إلزامي اسم المستخدم أو رقم الهاتف المحمول (يدعم 010... أو 2010...)
username body string اختياري بديل لحقل login للتوافق مع شاشات تسجيل الدخول القديمة
password body string إلزامي كلمة مرور حساب المدير
device_name body string اختياري اسم الجهاز أو نوع الهاتف لتتبعه في جلسات التوكن (الافتراضي Android App)
جسم الطلب (Request Payload JSON)
{
  "login": "admin",
  "password": "SecretPassword123@",
  "device_name": "Samsung Galaxy S24 Ultra (Manager App)"
}
نماذج الأكواد البرمجية:
// ApiService.kt
@POST("login")
@FormUrlEncoded
suspend fun login(
    @Field("login") identifier: String,
    @Field("password") pass: String,
    @Field("device_name") deviceName: String? = "Android Manager App"
): Response<LoginResponse>

// ViewModel Usage
val response = apiService.login("admin", "SecretPassword123@", "Samsung S24")
if (response.isSuccessful) {
    val token = response.body()?.token
    val admin = response.body()?.subscriber // User model
    TokenManager.saveToken(token)
}
curl -X POST "https://isp.freenet.net/api/manager/login" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"login": "admin", "password": "SecretPassword123@", "device_name": "Terminal App"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/login`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"login": "admin", "password": "SecretPassword123@", "device_name": "Samsung Galaxy S24 Ultra (Manager App)"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/login');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"login": "admin", "password": "SecretPassword123@", "device_name": "Samsung Galaxy S24 Ultra (Manager App)"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/login', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"login": "admin", "password": "SecretPassword123@", "device_name": "Samsung Galaxy S24 Ultra (Manager App)"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "token": "a1b2c3d4e5f67890123456789abcdef0123456789abcdef0123456789abcdef0",
  "admin": {
    "id": 1,
    "name": "المدير العام للنظام",
    "username": "admin",
    "phone": "01012345678",
    "email": "admin@freenet.local",
    "is_active": true,
    "balance": 15450.0,
    "credit_limit": 50000.0,
    "currency_symbol": "EGP",
    "role": "Super Admin",
    "role_names": [
      "Super Admin"
    ],
    "all_permissions": [
      "*"
    ],
    "avatar_url": "https://isp.freenet.net/storage/avatars/admin_avatar.png",
    "is_within_shift": true
  }
}
HTTP 403 Error / Exception
{
  "message": "عذراً، لا يمكنك تسجيل الدخول حالياً خارج مواعيد العمل الرسمية. مواعيد عملك: من 09:00 ص إلى 05:00 م (الأحد - الخميس)",
  "off_shift": true,
  "shift_info": "من 09:00 ص إلى 05:00 م"
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/me بيانات الملف الشخصي للمدير الحالي Bearer Token (auth.admin_app)

استرجاع بيانات المشرف المسجل حالياً، الرصيد المالي الحالي، والحد الائتماني، والصلاحيات الكاملة، والتأكد من استمرار صلاحية الوردية.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@GET("me")
suspend fun getProfile(): Response<User>

// Usage
val profile = apiService.getProfile().body()
println("مرحباً ${profile?.name} - الرصيد: ${profile?.balance} ج.م")
curl -X GET "https://isp.freenet.net/api/manager/me" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Accept: application/json" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/me`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/me');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/me', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "id": 1,
  "name": "المدير العام للنظام",
  "username": "admin",
  "phone": "01012345678",
  "email": "admin@freenet.local",
  "balance": 15450.0,
  "credit_limit": 50000.0,
  "admin_prefix": "adm",
  "is_within_shift": true,
  "profile_photo": "avatars/photo_123.jpg",
  "role_names": [
    "Super Admin"
  ],
  "all_permissions": [
    "view all subscribers",
    "renew subscriptions",
    "manage vouchers"
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/profile الملف الشخصي (Alias متوافق مع الإضافات) Bearer Token (auth.admin_app)

رابط بديل (Alias) مطابق لـ /api/manager/me لضمان التوافق مع إضافات المتصفح وتطبيقات الويب الخارجية القديمة.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@GET("profile")
suspend fun getProfileAlias(): Response<User>
curl -X GET "https://isp.freenet.net/api/manager/profile" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/profile`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/profile');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/profile', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "id": 1,
  "name": "المدير العام للنظام",
  "username": "admin",
  "balance": 15450.0,
  "is_within_shift": true
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/profile/photo رفع وتحديث الصورة الشخصية للمدير Bearer Token (auth.admin_app)

رفع صورة رمزية شخصية لحساب المدير بصيغة Multipart Form Data وتحديث رابط الصورة فورياً في النظام وحساب المشرف.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
photo body (multipart) file (image: jpeg, png, webp) إلزامي ملف الصورة الشخصية بحد أقصى 5 ميجابايت
جسم الطلب (Request Payload JSON)
multipart/form-data: photo=@avatar.jpg
نماذج الأكواد البرمجية:
@Multipart
@POST("profile/photo")
suspend fun uploadProfilePhoto(
    @Part photo: MultipartBody.Part
): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/profile/photo" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -F "photo=@/path/to/my_avatar.png" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/profile/photo`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/profile/photo');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/profile/photo', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "avatar_url": "https://isp.freenet.net/storage/avatars/admin_photo_abc.jpg",
  "message": "Profile photo updated successfully."
}
HTTP 422 Error / Exception
{
  "message": "The photo field is required.",
  "errors": {
    "photo": [
      "الملف المرفق يجب أن يكون صورة صالحة."
    ]
  }
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/logout تسجيل الخروج وإبطال التوكن Bearer Token (auth.admin_app)

تسجيل خروج المشرف فورياً وحذف سجل التوكن المخصص لهذا الجهاز من جدول AdminDeviceToken في قاعدة البيانات لمنع إعادة استخدامه.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@POST("logout")
suspend fun logout(): Response<Map<String, String>>
curl -X POST "https://isp.freenet.net/api/manager/logout" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/logout`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/logout');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/logout', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "message": "Successfully logged out"
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)

📊 لوحة المؤشرات وتحديثات النظام وسيرفرات الراديوس

SystemController
8 نقطة اتصال

استعلام لوحة المؤشرات الرئيسية (Dashboard KPIs)، وإحصائيات استهلاك السيرفر، وسيرفرات FreeRADIUS و NAS، وفحص التحديثات لتطبيقات الأندرويد وإدارة الإشعارات.

GET /api/manager/dashboard إحصائيات لوحة التحكم الرئيسية Bearer Token (auth.admin_app)

جلب بطاقات الأداء اللحظية: إجمالي عدد المشتركين، المتصلين أونلاين الآن، المشتركين النشطين، المشتركين المقترب انتهاؤهم، المنتهية اشتراكاتهم، وعدد تذاكر الدعم المفتوحة.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@GET("dashboard")
suspend fun getDashboard(): Response<DashboardResponse>
curl -X GET "https://isp.freenet.net/api/manager/dashboard" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/dashboard`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/dashboard');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/dashboard', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "subscribers_count": 1420,
  "online_users": 648,
  "active_users": 1180,
  "expiring_soon": 72,
  "expired_users": 240,
  "open_tickets_count": 5,
  "today_sales": 12850.0,
  "today_renewals_count": 42
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
PUT /api/manager/dashboard/settings تحديث تخصيصات وترتيب ودجات لوحة التحكم Bearer Token (auth.admin_app)

حفظ تخصيصات وتفضيلات المدير لعرض البطاقات الرسومية والودجات الإحصائية في الواجهة وتطبيق الهاتف.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
widgets body array of strings إلزامي قائمة بأسماء الودجات المفعلة وترتيب ظهورها
refresh_interval body integer اختياري الفاصل الزمني لتحديث البيانات بالثواني (15-300)
جسم الطلب (Request Payload JSON)
{
  "widgets": [
    "subscribers_kpi",
    "online_chart",
    "sales_counter",
    "nas_telemetry"
  ],
  "refresh_interval": 30
}
نماذج الأكواد البرمجية:
@PUT("dashboard/settings")
suspend fun updateDashboardSettings(@Body data: Map<String, Any>): Response<Map<String, Any>>
curl -X PUT "https://isp.freenet.net/api/manager/dashboard/settings" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"widgets": ["subscribers_kpi", "online_chart"], "refresh_interval": 30}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/dashboard/settings`, {
    method: 'PUT',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"widgets": ["subscribers_kpi", "online_chart", "sales_counter", "nas_telemetry"], "refresh_interval": 30})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/dashboard/settings');
  final response = await http.put(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"widgets": ["subscribers_kpi", "online_chart", "sales_counter", "nas_telemetry"], "refresh_interval": 30}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('PUT', 'api/manager/dashboard/settings', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"widgets": ["subscribers_kpi", "online_chart", "sales_counter", "nas_telemetry"], "refresh_interval": 30}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "Dashboard settings updated successfully"
}
HTTP 422 Error / Exception
{
  "message": "The given data was invalid."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/system/updates فحص تحديثات النظام وتطبيقات الأندرويد عام (متاح للجميع / التطبيقات)

الاستعلام عن حالة ترخيص النظام، وأحدث إصدارات تطبيقات الهاتف (تطبيق المدير Manager APK، وتطبيق المشترك Consumer APK) مع روابط التنزيل المباشر وإشعار التحديث الإجباري.

الصلاحية المطلوبة: متاح للجميع البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@GET("system/updates")
suspend fun getSystemInfo(): Response<SystemInfoResponse>
curl -X GET "https://isp.freenet.net/api/manager/system/updates" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/system/updates`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/system/updates');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/system/updates', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "license_key": "FREENET-PRO-XXXX-XXXX",
  "app_name": "FreeNet Billing & Network Master",
  "status": "active",
  "max_users": 5000,
  "current_users": 1420,
  "admin_prefix": "adm",
  "logo_url": "https://isp.freenet.net/assets/logo.png",
  "system_apps_updates": {
    "manager": {
      "version_code": 24,
      "version_name": "3.8.0",
      "file_url": "https://isp.freenet.net/downloads/freenet-manager-v3.8.0.apk",
      "changelog": "إضافة ميزة البيع السريع للكروت POS ودعم الطابعات الحرارية وإدارة السيرفرات",
      "force_update": false
    },
    "consumer": {
      "version_code": 18,
      "version_name": "2.4.1",
      "file_url": "https://isp.freenet.net/downloads/freenet-subscriber-v2.4.1.apk",
      "force_update": false
    }
  }
}
HTTP 500 Error / Exception
{
  "message": "License validation error"
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/system/info المراقبة الفنية للسيرفر واستهلاك الموارد Bearer Token (auth.admin_app)

استعلام القياسات اللحظية للسيرفر الفيزيائي: نسبة استهلاك المعالج CPU، والذاكرة العشوائية RAM، والقرص الصلب Disk، وزمن التشغيل Uptime، وحالة خدمات النظام (FreeRADIUS, MariaDB, Nginx, Redis).

الصلاحية المطلوبة: مشرف مسجل (Super Admin / NOC) البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@GET("system/info")
suspend fun getServerHardwareMetrics(): Response<Map<String, Any>>
curl -X GET "https://isp.freenet.net/api/manager/system/info" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/system/info`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/system/info');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/system/info', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "cpu_usage_percentage": 14.5,
  "cpu_cores": 8,
  "ram_usage_percentage": 42.1,
  "ram_used_gb": 6.7,
  "ram_total_gb": 16.0,
  "disk_usage_percentage": 33.8,
  "disk_free_gb": 185.4,
  "system_uptime": "45 days, 14 hours, 22 mins",
  "services": {
    "mariadb": "running",
    "freeradius": "running",
    "nginx": "running",
    "redis": "running",
    "supervisor": "running"
  }
}
HTTP 403 Error / Exception
{
  "message": "Unauthorized access."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/nas قائمة سيرفرات وبوابات الراديوس (NAS Routers) Bearer Token (auth.admin_app)

جلب قائمة راوترات وسيرفرات FreeRADIUS المعرفة على النظام (MikroTik, Cisco, Accel-PPP) مع عناوين الـ IP وعدد الجلسات النشطة المتصلة بكل سيرفر.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@GET("nas")
suspend fun getNasList(): Response<List<Map<String, Any>>>
curl -X GET "https://isp.freenet.net/api/manager/nas" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/nas`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/nas');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/nas', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
[
  {
    "id": 1,
    "nasname": "192.168.10.1",
    "shortname": "Core-CCR2004-Downtown",
    "type": "mikrotik",
    "ports": 3799,
    "secret": "SharedRadiusSecret2026",
    "description": "راوتر الدمج الرئيسي - برج التحرير",
    "active_subscribers_count": 412,
    "status": "online",
    "last_seen_ping": "2.4ms"
  },
  {
    "id": 2,
    "nasname": "10.50.0.1",
    "shortname": "Tower-East-CCR1009",
    "type": "mikrotik",
    "active_subscribers_count": 236,
    "status": "online"
  }
]
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/nas/{id}/close-sessions طرد وفصل كافة المشتركين المتصلين بسيرفر راديوس محدد Bearer Token (auth.admin_app)

إرسال أوامر فصل جماعية عبر MikroTik CoA / Disconnect-Request إلى راوتر الـ NAS المحدد لفصل كافة الجلسات النشطة دفعة واحدة (مفيد عند الصيانة أو إعادة تشغيل الراوتر).

الصلاحية المطلوبة: إدارة الشبكة (manage network devices) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف سيرفر الـ NAS
نماذج الأكواد البرمجية:
@POST("nas/{id}/close-sessions")
suspend fun closeAllNasSessions(@Path("id") id: Int): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/nas/1/close-sessions" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/nas/{id}/close-sessions`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/nas/{id}/close-sessions');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/nas/{id}/close-sessions', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم إرسال حزم الفصل الجماعية بنجاح إلى الراوتر",
  "sessions_terminated_count": 412
}
HTTP 500 Error / Exception
{
  "message": "Failed to communicate with NAS device via RADIUS CoA port."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/notifications استعراض الإشعارات والتنبيهات الإدارية Bearer Token (auth.admin_app)

استرجاع الإشعارات الخاصة بالمدير: تنبيهات توقف الراوترات، اقتراب رصيد المشرف من النفاد، وتذاكر الدعم الجديدة، وتجاوز حدود الـ FUP.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
unread_only query boolean اختياري تصفية الإشعارات غير المقروءة فقط (true/false)
نماذج الأكواد البرمجية:
@GET("notifications")
suspend fun getNotifications(@Query("unread_only") unreadOnly: Boolean? = null): Response<Map<String, Any>>
curl -X GET "https://isp.freenet.net/api/manager/notifications" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/notifications`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/notifications');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/notifications', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "unread_count": 3,
  "data": [
    {
      "id": "notif_uuid_01",
      "title": "تنبيه انقطاع جهاز شبكة",
      "message": "تم رصد انقطاع الاتصال مع الراوتر: Tower-North بعد 3 محاولات Ping فاشلة.",
      "type": "device_down",
      "created_at": "2026-09-09 14:15:00",
      "is_read": false
    },
    {
      "id": "notif_uuid_02",
      "title": "شحن محفظة موزع",
      "message": "قام الموزع 'مكتب الأمل' بطلب شحن رصيد بمبلغ 5,000 ج.م.",
      "type": "recharge_request",
      "created_at": "2026-09-09 13:40:00",
      "is_read": false
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/notifications/{id}/read تحديد إشعار كمقروء Bearer Token (auth.admin_app)

تحديث حالة الإشعار المحدد ليصبح مقروءاً وإزالته من شارة التنبيهات النشطة.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path string إلزامي معرف الإشعار (UUID أو ID)
نماذج الأكواد البرمجية:
@POST("notifications/{id}/read")
suspend fun markNotificationAsRead(@Path("id") id: String): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/notifications/notif_uuid_01/read" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/notifications/{id}/read`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/notifications/{id}/read');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/notifications/{id}/read', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "Notification marked as read"
}
HTTP 404 Error / Exception
{
  "message": "Notification not found"
}
⚡ اختبار الطلب الحي (Interactive Tester)

👥 إدارة المشتركين بالكامل وقوائم الفلترة والصور

SubscriberController
15 نقطة اتصال

إدارة المشتركين الشاملة: القوائم المصفاة متعددة المعايير، البحث السريع، استعراض الملف الكامل بما يطابق نماذج الأندرويد وLaravel، إضافة وتعديل وحذف المشتركين، رفع وحذف صور البطاقات الشخصية والعقود، والعمليات الجماعية وسجلات الجلسات والاستهلاك.

GET /api/manager/subscribers قائمة المشتركين مع الفلترة المتقدمة Bearer Token (auth.admin_app)

استعلام قائمة المشتركين بصفحات مجزأة (Pagination) مع دعم الفلترة الشاملة حسب الحالة (نشط، منتهي، متصل، محظور)، نوع الاتصال (Hotspot, PPPoE)، الباقة، المشرف، راوتر NAS، البرج/المجموعة، والبحث الشامل.

الصلاحية المطلوبة: مشرف مسجل (يتم عزل البيانات حسب نطاق المشرف وصلاحياته) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
page query integer اختياري رقم الصفحة (الافتراضي: 1)
per_page query integer اختياري عدد السجلات بالصفحة (الافتراضي: 20)
search query string اختياري البحث بالاسم، اسم المستخدم، رقم الهاتف، الآيبي، الماك أدرس، أو رقم العقد
filter_status query string اختياري الحالة: active_users, online_users, expired_users, expiring_soon, paused, disabled, all
filter_connection_type query string اختياري نوع الاتصال: pppoe, hotspot, static
filter_plan_id query integer اختياري معرف باقة الخدمة
filter_admin_id query integer اختياري معرف المشرف المسؤول
filter_nas_id query integer اختياري معرف راوتر الـ NAS
filter_group_id query integer اختياري معرف المجموعة أو البرج
sort_field query string اختياري حقل الترتيب: id, name, expires_at, balance
sort_direction query string اختياري اتجاه الترتيب: asc أو desc
نماذج الأكواد البرمجية:
@GET("subscribers")
suspend fun getSubscribers(
    @Query("page") page: Int = 1,
    @Query("per_page") perPage: Int? = 20,
    @Query("search") search: String? = null,
    @Query("filter_status") status: String? = null,
    @Query("filter_connection_type") connectionType: String? = null,
    @Query("filter_plan_id") planId: Int? = null,
    @Query("filter_admin_id") adminId: Int? = null,
    @Query("filter_nas_id") nasId: Int? = null
): Response<SubscriberListResponse>
curl -X GET "https://isp.freenet.net/api/manager/subscribers?filter_status=active_users&page=1" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/subscribers', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "current_page": 1,
  "per_page": 20,
  "total": 1420,
  "last_page": 71,
  "data": [
    {
      "id": 105,
      "name": "محمود أحمد حسن",
      "username": "mahmoud.ahmed",
      "phone": "01098765432",
      "address": "شارع الجمهورية - عمارة الأمل الدور الثالث",
      "avatar_url": "https://isp.freenet.net/storage/subscribers/105_photo.jpg",
      "status": "active",
      "online_status": true,
      "active_session_type": "pppoe",
      "balance": 25.0,
      "currency_symbol": "EGP",
      "current_plan_name": "باقة سوبر 30 ميجا - 250 جيجا",
      "total_unpaid_debt": 0.0,
      "days_remaining": 14,
      "days_remaining_string": "متبقي 14 يوم",
      "expires_at_formatted": "2026-09-23 23:59:59",
      "usage": {
        "used": "142.5 GB",
        "total": "250.0 GB",
        "remaining": "107.5 GB",
        "percentage": 57
      },
      "status_badges": [
        {
          "text": "متصل أونلاين",
          "text_ar": "متصل",
          "color": "#10b981",
          "bg_color": "#ecfdf5"
        }
      ]
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/subscribers/filter-data بيانات القوائم المنسدلة للفلاتر ونماذج الإضافة Bearer Token (auth.admin_app)

استرجاع كافة خيارات الفلاتر ومساعدات الإدخال: قائمة الباقات المتاحة للمدير، المشرفين التابعين، الأبراج والمجموعات، وأجهزة الشبكة وراوترات الراديوس.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@GET("subscribers/filter-data")
suspend fun getFilterData(): Response<FilterDataResponse>
curl -X GET "https://isp.freenet.net/api/manager/subscribers/filter-data" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/filter-data`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/filter-data');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/subscribers/filter-data', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "plans": [
    {
      "id": 1,
      "name": "باقة سوبر 30 ميجا",
      "price": 180.0,
      "quota_gb": 250
    },
    {
      "id": 2,
      "name": "باقة الترا 70 ميجا",
      "price": 320.0,
      "quota_gb": 400
    }
  ],
  "admins": [
    {
      "id": 1,
      "name": "المدير العام",
      "username": "admin"
    },
    {
      "id": 2,
      "name": "موزع سنترال المحطة",
      "username": "reseller_station"
    }
  ],
  "groups": [
    {
      "id": 1,
      "name": "برج التحرير"
    },
    {
      "id": 2,
      "name": "برج الزهور"
    }
  ],
  "nas_devices": [
    {
      "id": 1,
      "shortname": "CCR-Main-2004",
      "nasname": "192.168.10.1"
    }
  ],
  "connection_types": [
    {
      "value": "pppoe",
      "label": "PPPoE (المنزلي)"
    },
    {
      "value": "hotspot",
      "label": "Hotspot (الوايرلس)"
    },
    {
      "value": "static",
      "label": "Static IP (الشركات)"
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/subscribers/search البحث السريع المباشر (Quick Omni-Search) Bearer Token (auth.admin_app)

رابط بحث سريع فوري مخصص لأشرطة البحث المنبثقة وشاشات الإكمال التلقائي في تطبيق الأندرويد والويب.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
search query string إلزامي الكلمة المفتاحية للبحث
نماذج الأكواد البرمجية:
@GET("subscribers/search")
suspend fun searchSubscribers(@Query("search") query: String): Response<SubscriberListResponse>
curl -X GET "https://isp.freenet.net/api/manager/subscribers/search?search=mahmoud" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/search`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/search');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/subscribers/search', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "data": [
    {
      "id": 105,
      "name": "محمود أحمد حسن",
      "username": "mahmoud.ahmed",
      "phone": "01098765432",
      "status": "active"
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/subscribers/{id} تفاصيل المشترك الكاملة (Detailed Subscriber Profile) Bearer Token (auth.admin_app)

جلب الملف الشامل للمشترك: البيانات الشخصية، الاشتراك النشط وتفاصيل الباقة، الاستهلاك اللحظي واليومي، سجل الجلسة الحية، الماكات المقترنة، الفواتير غير المسددة، المديونيات، وصور العقد والبطاقة الشخصية.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك الفريد
نماذج الأكواد البرمجية:
@GET("subscribers/{id}")
suspend fun getSubscriberDetails(@Path("id") id: Int): Response<DetailedSubscriberResponse>
curl -X GET "https://isp.freenet.net/api/manager/subscribers/105" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/subscribers/{id}', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "id": 105,
  "name": "محمود أحمد حسن",
  "username": "mahmoud.ahmed",
  "phone": "01098765432",
  "national_id": "29508120102554",
  "address": "شارع الجمهورية - عمارة الأمل الدور الثالث",
  "notes": "تم التوصيل على بورت 8 في سويتش الدور 3",
  "avatar_url": "https://isp.freenet.net/storage/subscribers/105_photo.jpg",
  "status": "active",
  "is_active": true,
  "online_status": true,
  "active_session_type": "pppoe",
  "balance": 25.0,
  "total_unpaid_debt": 0.0,
  "mac_limit": 1,
  "simultaneous_sessions": 1,
  "whatsapp_opt_in": true,
  "whatsapp_status": "verified",
  "is_dashboard_locked": false,
  "active_subscription": {
    "id": 892,
    "service_plan_id": 1,
    "plan_name": "باقة سوبر 30 ميجا",
    "download_speed_kbps": 30720,
    "upload_speed_kbps": 5120,
    "starts_at": "2026-08-24 00:00:00",
    "expires_at": "2026-09-23 23:59:59",
    "total_quota_bytes": 268435456000,
    "used_quota_bytes": 153008209920,
    "remaining_quota_bytes": 115427246080
  },
  "active_session": {
    "nas_ip": "192.168.10.1",
    "framed_ip_address": "10.100.4.55",
    "calling_station_id": "48:8F:5A:12:34:56",
    "session_start": "2026-09-09 08:30:12",
    "uptime_seconds": 21328,
    "uploaded_formatted": "1.2 GB",
    "downloaded_formatted": "8.4 GB"
  },
  "macs": [
    {
      "id": 44,
      "mac_address": "48:8F:5A:12:34:56",
      "created_at": "2026-08-01"
    }
  ],
  "admin": {
    "id": 1,
    "name": "المدير العام",
    "phone": "01012345678"
  }
}
HTTP 404 Error / Exception
{
  "message": "المشترك غير موجود أو ليس لديك صلاحية الوصول إليه."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers إضافة مشترك جديد (إنشاء حساب واشتراك) Bearer Token (auth.admin_app)

تسجيل مشترك جديد في النظام: توليد حساب الراديوس (radcheck/radreply)، تعيين الباقة، خصم تكلفة الاشتراك من رصيد المدير/الموزع داخل معاملة مالية آمنة (DB::transaction & lockForUpdate)، وتحديد نوع الاتصال والماك أدرس.

الصلاحية المطلوبة: صلاحية إضافة مشتركين (create subscribers) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
username body string إلزامي اسم مستخدم الاتصال الفريد (حروف إنجليزية وأرقام ونقاط)
password body string إلزامي كلمة مرور اتصال الراديوس
name body string إلزامي الاسم الكامل للمشترك بالعربية
phone body string اختياري رقم الهاتف المحمول للتنبيهات وإشعارات الواتساب
national_id body string اختياري الرقم القومي / الهوية (14 رقم)
address body string اختياري العنوان التفصيلي ومكان التركيب
service_plan_id body integer إلزامي معرف باقة الخدمة المختارة
admin_id body integer اختياري معرف المشرف التابع له (الافتراضي: المدير المنشئ)
connection_type body string اختياري نوع الاتصال: pppoe (الافتراضي) أو hotspot أو static
mac_address body string اختياري الماك أدرس لتقييد المشترك بجهاز محدد
static_ip body string اختياري عنوان IP ثابت اختياري
initial_balance body number اختياري رصيد افتتاحي يُضاف لمحفظة المشترك
notes body string اختياري ملاحظات إدارية وفنية
جسم الطلب (Request Payload JSON)
{
  "username": "karim.tarek",
  "password": "KarimPass2026@",
  "name": "كريم طارق عبد الرحمن",
  "phone": "01234567890",
  "national_id": "29805140101993",
  "address": "شارع النصر - برج الياسمين",
  "service_plan_id": 1,
  "connection_type": "pppoe",
  "initial_balance": 0.0,
  "notes": "تم التركيب عبر راوتر تي بي لينك جديد"
}
نماذج الأكواد البرمجية:
@POST("subscribers")
suspend fun createSubscriber(@Body data: RequestBody): Response<Subscriber>

// In Repository
val json = JSONObject().apply {
    put("username", "karim.tarek")
    put("password", "KarimPass2026@")
    put("name", "كريم طارق عبد الرحمن")
    put("service_plan_id", 1)
    put("phone", "01234567890")
}.toString()
val body = json.toRequestBody("application/json".toMediaTypeOrNull())
val res = apiService.createSubscriber(body)
curl -X POST "https://isp.freenet.net/api/manager/subscribers" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"username":"karim.tarek","password":"Pass123@","name":"كريم طارق","service_plan_id":1}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"username": "karim.tarek", "password": "KarimPass2026@", "name": "كريم طارق عبد الرحمن", "phone": "01234567890", "national_id": "29805140101993", "address": "شارع النصر - برج الياسمين", "service_plan_id": 1, "connection_type": "pppoe", "initial_balance": 0.0, "notes": "تم التركيب عبر راوتر تي بي لينك جديد"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"username": "karim.tarek", "password": "KarimPass2026@", "name": "كريم طارق عبد الرحمن", "phone": "01234567890", "national_id": "29805140101993", "address": "شارع النصر - برج الياسمين", "service_plan_id": 1, "connection_type": "pppoe", "initial_balance": 0.0, "notes": "تم التركيب عبر راوتر تي بي لينك جديد"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"username": "karim.tarek", "password": "KarimPass2026@", "name": "كريم طارق عبد الرحمن", "phone": "01234567890", "national_id": "29805140101993", "address": "شارع النصر - برج الياسمين", "service_plan_id": 1, "connection_type": "pppoe", "initial_balance": 0.0, "notes": "تم التركيب عبر راوتر تي بي لينك جديد"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 201 Success
{
  "status": "success",
  "message": "تم إنشاء حساب المشترك وتفعيل اشتراكه بنجاح",
  "data": {
    "id": 106,
    "username": "karim.tarek",
    "name": "كريم طارق عبد الرحمن",
    "status": "active",
    "expires_at": "2026-10-09 23:59:59"
  }
}
HTTP 422 Error / Exception
{
  "message": "The given data was invalid.",
  "errors": {
    "username": [
      "اسم المستخدم هذا مسجل بالفعل لمشترك آخر."
    ],
    "balance": [
      "رصيد المشرف غير كافٍ لتغطية تكلفة تفعيل هذه الباقة."
    ]
  }
}
⚡ اختبار الطلب الحي (Interactive Tester)
PUT /api/manager/subscribers/{id} تعديل بيانات المشترك Bearer Token (auth.admin_app)

تحديث بيانات المشترك الشخصية، العنوان، رقم الهاتف، وتعديل كلمة المرور أو الماك أدرس دون المساس بسجل الاشتراك التاريخي.

الصلاحية المطلوبة: مشرف مسجل (edit subscribers) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
name body string اختياري الاسم الكامل للمشترك
phone body string اختياري رقم الهاتف المحمول
address body string اختياري العنوان
password body string اختياري كلمة مرور جديدة للاتصال في حال الرغبة بتغييرها
notes body string اختياري ملاحظات إدارية
جسم الطلب (Request Payload JSON)
{
  "name": "محمود أحمد حسن الشريف",
  "phone": "01098765432",
  "address": "شارع الجمهورية - عمارة الأمل الدور الرابع (شقة معدلة)",
  "notes": "تم نقل الروزيتا لشقة الدور الرابع"
}
نماذج الأكواد البرمجية:
@PUT("subscribers/{id}")
suspend fun updateSubscriber(
    @Path("id") id: Int, 
    @Body data: RequestBody
): Response<Subscriber>
curl -X PUT "https://isp.freenet.net/api/manager/subscribers/105" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"name": "محمود أحمد حسن الشريف"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}`, {
    method: 'PUT',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"name": "محمود أحمد حسن الشريف", "phone": "01098765432", "address": "شارع الجمهورية - عمارة الأمل الدور الرابع (شقة معدلة)", "notes": "تم نقل الروزيتا لشقة الدور الرابع"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}');
  final response = await http.put(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"name": "محمود أحمد حسن الشريف", "phone": "01098765432", "address": "شارع الجمهورية - عمارة الأمل الدور الرابع (شقة معدلة)", "notes": "تم نقل الروزيتا لشقة الدور الرابع"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('PUT', 'api/manager/subscribers/{id}', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"name": "محمود أحمد حسن الشريف", "phone": "01098765432", "address": "شارع الجمهورية - عمارة الأمل الدور الرابع (شقة معدلة)", "notes": "تم نقل الروزيتا لشقة الدور الرابع"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تحديث بيانات المشترك بنجاح",
  "subscriber": {
    "id": 105,
    "name": "محمود أحمد حسن الشريف"
  }
}
HTTP 422 Error / Exception
{
  "message": "The given data was invalid."
}
⚡ اختبار الطلب الحي (Interactive Tester)
DELETE /api/manager/subscribers/{id} حذف المشترك نهائياً Bearer Token (auth.admin_app)

حذف حساب المشترك وسجلاته، فصل جلسته النشطة فوراً من راوتر المايكروتك عبر الـ CoA، وحذف بياناته من جداول FreeRADIUS (radcheck, radreply).

الصلاحية المطلوبة: صلاحية الحذف (delete subscribers) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك المراد حذفه
نماذج الأكواد البرمجية:
@DELETE("subscribers/{id}")
suspend fun deleteSubscriber(@Path("id") id: Int): Response<Map<String, Any>>
curl -X DELETE "https://isp.freenet.net/api/manager/subscribers/105" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}`, {
    method: 'DELETE',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}');
  final response = await http.delete(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('DELETE', 'api/manager/subscribers/{id}', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم حذف المشترك وإلغاء كافة قيوده وجلساته بنجاح"
}
HTTP 403 Error / Exception
{
  "message": "ليس لديك صلاحية لحذف المشتركين."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/photo رفع وتحديث صورة المشترك أو بطاقة الهوية Bearer Token (auth.admin_app)

رفع صورة للمشترك أو صورة بطاقة الرقم القومي أو العقد عبر Multipart Form Data وحفظها في التخزين السحابي الآمن مع ربطها بملف المشترك.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
photo body (multipart) file (jpeg, png, webp) إلزامي ملف الصورة المرفقة بحد أقصى 5 ميجابايت
جسم الطلب (Request Payload JSON)
multipart/form-data: photo=@national_id.jpg
نماذج الأكواد البرمجية:
@Multipart
@POST("subscribers/{id}/photo")
suspend fun uploadSubscriberPhoto(
    @Path("id") id: Int,
    @Part photo: MultipartBody.Part
): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/photo" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -F "photo=@/path/to/contract.jpg" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/photo`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/photo');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/photo', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم رفع وتحديث صورة المشترك بنجاح",
  "photo_url": "https://isp.freenet.net/storage/subscribers/105_photo_xyz.jpg"
}
HTTP 422 Error / Exception
{
  "message": "The photo field must be an image."
}
⚡ اختبار الطلب الحي (Interactive Tester)
DELETE /api/manager/subscribers/{id}/photo حذف صورة المشترك المرفقة Bearer Token (auth.admin_app)

حذف الصورة المرفقة للمشترك وحذف الملف الفيزيائي من السيرفر وإعادة الصورة للافتراضي.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
نماذج الأكواد البرمجية:
@DELETE("subscribers/{id}/photo")
suspend fun deleteSubscriberPhoto(@Path("id") id: Int): Response<Map<String, Any>>
curl -X DELETE "https://isp.freenet.net/api/manager/subscribers/105/photo" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/photo`, {
    method: 'DELETE',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/photo');
  final response = await http.delete(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('DELETE', 'api/manager/subscribers/{id}/photo', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم حذف صورة المشترك بنجاح"
}
HTTP 404 Error / Exception
{
  "message": "لا توجد صورة مسجلة لهذا المشترك."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/bulk-delete الحذف الجماعي للمشتركين المحددين Bearer Token (auth.admin_app)

حذف مجموعة من المشتركين دفعة واحدة عبر تمرير مصفوفة المعرفات IDs مع فصل جلساتهم ومسحهم من جداول الراديوس.

الصلاحية المطلوبة: صلاحية الحذف الجماعي (bulk delete subscribers) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
ids body array of integers إلزامي مصفوفة معرفات المشتركين المراد حذفهم
جسم الطلب (Request Payload JSON)
{
  "ids": [
    105,
    106,
    107
  ]
}
نماذج الأكواد البرمجية:
@POST("subscribers/bulk-delete")
suspend fun bulkDeleteSubscribers(@Body data: Map<String, List<Int>>): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/bulk-delete" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"ids": [105, 106, 107]}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/bulk-delete`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"ids": [105, 106, 107]})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/bulk-delete');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"ids": [105, 106, 107]}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/bulk-delete', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"ids": [105, 106, 107]}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم حذف 3 مشتركين بنجاح",
  "deleted_count": 3
}
HTTP 422 Error / Exception
{
  "message": "حقل المعرفات ids مطلوب."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/bulk-toggle-status التعطيل / التفعيل الجماعي للمشتركين Bearer Token (auth.admin_app)

تغيير حالة مجموعة من المشتركين (تفعيل أو تعطيل) دفعة واحدة مع فصل جلسات النشطين المعطلين فورياً.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
ids body array of integers إلزامي مصفوفة معرفات المشتركين
status body string إلزامي الحالة المستهدفة: active أو inactive
جسم الطلب (Request Payload JSON)
{
  "ids": [
    105,
    108
  ],
  "status": "inactive"
}
نماذج الأكواد البرمجية:
@POST("subscribers/bulk-toggle-status")
suspend fun bulkToggleStatus(@Body data: Map<String, Any>): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/bulk-toggle-status" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"ids": [105, 108], "status": "inactive"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/bulk-toggle-status`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"ids": [105, 108], "status": "inactive"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/bulk-toggle-status');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"ids": [105, 108], "status": "inactive"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/bulk-toggle-status', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"ids": [105, 108], "status": "inactive"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تحديث حالة 2 مشترك بنجاح"
}
HTTP 422 Error / Exception
{
  "message": "The given data was invalid."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/bulk-transfer-admin نقل المشتركين لمشرف أو موزع آخر جماعياً Bearer Token (auth.admin_app)

إعادة إسناد مجموعة من المشتركين ونقل تبعيتهم إلى مشرف أو موزع جديد وتحديث صلاحيات إدارتهم.

الصلاحية المطلوبة: مشرف عام (transfer subscribers) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
ids body array of integers إلزامي مصفوفة معرفات المشتركين
admin_id body integer إلزامي معرف المشرف الجديد المراد النقل إليه
جسم الطلب (Request Payload JSON)
{
  "ids": [
    105,
    106
  ],
  "admin_id": 2
}
نماذج الأكواد البرمجية:
@POST("subscribers/bulk-transfer-admin")
suspend fun bulkTransferAdmin(@Body data: Map<String, Any>): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/bulk-transfer-admin" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"ids": [105, 106], "admin_id": 2}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/bulk-transfer-admin`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"ids": [105, 106], "admin_id": 2})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/bulk-transfer-admin');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"ids": [105, 106], "admin_id": 2}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/bulk-transfer-admin', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"ids": [105, 106], "admin_id": 2}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم نقل 2 مشتركين إلى المشرف الجديد بنجاح"
}
HTTP 404 Error / Exception
{
  "message": "المشرف المستهدف غير موجود."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/bulk-transfer-group نقل المشتركين إلى برج أو مجموعة أخرى جماعياً Bearer Token (auth.admin_app)

نقل مجموعة من المشتركين إلى برج أو مجموعة شبكية جديدة (Group Transfer).

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
ids body array of integers إلزامي مصفوفة معرفات المشتركين
group_id body integer إلزامي معرف المجموعة أو البرج الجديد
جسم الطلب (Request Payload JSON)
{
  "ids": [
    105,
    106
  ],
  "group_id": 3
}
نماذج الأكواد البرمجية:
@POST("subscribers/bulk-transfer-group")
suspend fun bulkTransferGroup(@Body data: Map<String, Any>): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/bulk-transfer-group" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"ids": [105, 106], "group_id": 3}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/bulk-transfer-group`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"ids": [105, 106], "group_id": 3})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/bulk-transfer-group');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"ids": [105, 106], "group_id": 3}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/bulk-transfer-group', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"ids": [105, 106], "group_id": 3}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم نقل المشتركين للمجموعة المحددة بنجاح"
}
HTTP 422 Error / Exception
{
  "message": "The given data was invalid."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/subscribers/{id}/sessions سجل جلسات الراديوس للمشترك (RADIUS Sessions History) Bearer Token (auth.admin_app)

استعراض سجل الاتصالات والجلسات التاريخية للمشترك من جدول radacct / SubscriberSessions: وقت البدء، وقت الانتهاء، المدة، الآيبي المسحوب، الماك، وسبب انتهاء الجلسة (User-Request, Lost-Carrier, Admin-Reset).

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
page query integer اختياري رقم الصفحة
start_date query string (YYYY-MM-DD) اختياري تاريخ البداية
end_date query string (YYYY-MM-DD) اختياري تاريخ النهاية
نماذج الأكواد البرمجية:
@GET("subscribers/{id}/sessions")
suspend fun getSubscriberSessions(
    @Path("id") id: Int,
    @Query("page") page: Int = 1,
    @Query("start_date") startDate: String? = null,
    @Query("end_date") endDate: String? = null
): Response<SessionListResponse>
curl -X GET "https://isp.freenet.net/api/manager/subscribers/105/sessions" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/sessions`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/sessions');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/subscribers/{id}/sessions', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "data": [
    {
      "id": 9012,
      "framed_ip_address": "10.100.4.55",
      "calling_station_id": "48:8F:5A:12:34:56",
      "session_start": "2026-09-08 10:14:02",
      "session_end": "2026-09-08 23:45:10",
      "duration_formatted": "13 hours 31 mins",
      "uploaded_formatted": "2.4 GB",
      "downloaded_formatted": "14.8 GB",
      "total_formatted": "17.2 GB",
      "terminate_cause": "User-Request"
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/subscribers/{id}/usage-history سجل الاستهلاك اليومي للرسم البياني (Daily Usage Telemetry) Bearer Token (auth.admin_app)

جلب بيانات استهلاك الباندويث اليومية (الرفع والتحميل والإجمالي بالبايت) للمشترك على مدار آخر 30 يوماً لرسم المنحنيات البيانية في تطبيق الهاتف والداشبورد.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
start_date query string اختياري تاريخ البداية
end_date query string اختياري تاريخ النهاية
نماذج الأكواد البرمجية:
@GET("subscribers/{id}/usage-history")
suspend fun getSubscriberUsageHistory(
    @Path("id") id: Int,
    @Query("start_date") startDate: String? = null,
    @Query("end_date") endDate: String? = null
): Response<List<DailyUsageHistoryItem>>
curl -X GET "https://isp.freenet.net/api/manager/subscribers/105/usage-history" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/usage-history`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/usage-history');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/subscribers/{id}/usage-history', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
[
  {
    "date": "2026-09-08",
    "download_bytes": 15899345920,
    "upload_bytes": 2576980377,
    "total_bytes": 18476326297,
    "download_formatted": "14.8 GB",
    "upload_formatted": "2.4 GB",
    "total_formatted": "17.2 GB"
  },
  {
    "date": "2026-09-07",
    "download_bytes": 11200345920,
    "upload_bytes": 1576980377,
    "total_bytes": 12777326297,
    "download_formatted": "10.4 GB",
    "upload_formatted": "1.5 GB",
    "total_formatted": "11.9 GB"
  }
]
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)

⚡ العمليات السريعة للمشترك وأوامر المايكروتك

SubscriberActionController
37 نقطة اتصال

كافة العمليات الفورية والتفاعلية على خط المشترك: فصل الجلسات اللحظي عبر حزم MikroTik CoA Disconnect، تفعيل/تعطيل، إيقاف النت المؤقت، شحن وسحب الرصيد، تسجيل المديونيات، الدفع الذكي، تجديد وتغيير الباقة، زيادة الجيجات وتعديل الصلاحية، السرعات المؤقتة، قفل ومسح الماك، الرقابة العائلية، والمستخدمين الفرعيين وقيود الراديوس.

POST /api/manager/subscribers/{id}/disconnect فصل جلسة المشترك اللحظي (MikroTik CoA Disconnect) Bearer Token (auth.admin_app)

إرسال حزمة Disconnect-Request فورية إلى راوتر المايكروتك أو سيرفر الراديوس لطرد جلسة المشترك النشطة حالياً وقطع اتصاله بالإنترنت في أجزاء من الثانية.

الصلاحية المطلوبة: مشرف مسجل (disconnect sessions) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/disconnect")
suspend fun disconnectSubscriber(@Path("id") id: Int): Response<Unit>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/disconnect" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/disconnect`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/disconnect');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/disconnect', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم إرسال أمر فصل الجلسة إلى الراوتر بنجاح"
}
HTTP 404 Error / Exception
{
  "message": "المشترك غير متصل حالياً بأي جلسة نشطة."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/toggle-status تفعيل / تعطيل حساب المشترك Bearer Token (auth.admin_app)

تبديل حالة المشترك بين نشط ومعطل. في حال التعطيل، يتم فصل جلسته النشطة فورياً من الراوتر ومنعه من تسجيل الدخول مجدداً حتى يتم تفعيله.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/toggle-status")
suspend fun toggleSubscriberStatus(@Path("id") id: Int): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/toggle-status" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/toggle-status`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/toggle-status');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/toggle-status', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "is_active": false,
  "message": "تم تعطيل حساب المشترك وفصل اتصاله بنجاح"
}
HTTP 403 Error / Exception
{
  "message": "ليس لديك صلاحية لتعديل حالة المشترك."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/toggle-internet إيقاف / استئناف الإنترنت المؤقت (Pause / Resume Internet) Bearer Token (auth.admin_app)

إيقاف خدمة الإنترنت مؤقتاً عن المشترك (تحويله لبروفايل الحظر/صفحة الهبوط) دون المساس بفترة اشتراكه أو تاريخ انتهائه، ثم استئناف الخدمة في أي وقت.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/toggle-internet")
suspend fun toggleInternet(@Path("id") id: Int): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/toggle-internet" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/toggle-internet`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/toggle-internet');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/toggle-internet', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "is_internet_paused": true,
  "message": "تم إيقاف الإنترنت مؤقتاً عن المشترك بنجاح"
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/balance/add إيداع وشحن رصيد في محفظة المشترك Bearer Token (auth.admin_app)

إيداع مبلغ مالي في محفظة المشترك عبر WalletService داخل معاملة بنكية آمنة مع قفل القيد (DB::transaction & lockForUpdate) وخصم المبلغ المقابل من رصيد المشرف وتسجيل إيصال إيداع.

الصلاحية المطلوبة: مشرف مسجل (financial balance operations) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
amount body number إلزامي المبلغ المراد إيداعه (أكبر من 0)
description body string اختياري بيان أو سبب الإيداع
جسم الطلب (Request Payload JSON)
{
  "amount": 200.0,
  "description": "شحن نقدي بمقر السنترال"
}
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/balance/add")
suspend fun addBalance(
    @Path("id") id: Int,
    @Body data: Map<String, Any>
): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/balance/add" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"amount": 200.00, "description": "شحن كاش"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/balance/add`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"amount": 200.0, "description": "شحن نقدي بمقر السنترال"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/balance/add');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"amount": 200.0, "description": "شحن نقدي بمقر السنترال"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/balance/add', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"amount": 200.0, "description": "شحن نقدي بمقر السنترال"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "new_balance": 225.0,
  "amount_added": 200.0,
  "admin_balance": 15250.0,
  "message": "تم إيداع مبلغ 200 ج.م في محفظة المشترك بنجاح"
}
HTTP 422 Error / Exception
{
  "message": "رصيد المشرف غير كافٍ لتنفيذ هذه العملية."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/balance/withdraw سحب رصيد من محفظة المشترك Bearer Token (auth.admin_app)

سحب مبلغ مالي من رصيد المشترك وإرجاعه إلى رصيد المشرف مع التحقق من كفاية رصيد العميل وتسجيل القيد المحاسبي.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
amount body number إلزامي المبلغ المراد سحبه
description body string اختياري بيان أو سبب السحب
جسم الطلب (Request Payload JSON)
{
  "amount": 50.0,
  "description": "استرجاع رصيد متبقي"
}
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/balance/withdraw")
suspend fun withdrawBalance(
    @Path("id") id: Int,
    @Body data: Map<String, Any>
): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/balance/withdraw" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"amount": 50.00}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/balance/withdraw`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"amount": 50.0, "description": "استرجاع رصيد متبقي"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/balance/withdraw');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"amount": 50.0, "description": "استرجاع رصيد متبقي"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/balance/withdraw', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"amount": 50.0, "description": "استرجاع رصيد متبقي"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "new_balance": 175.0,
  "message": "تم سحب مبلغ 50 ج.م من رصيد المشترك بنجاح"
}
HTTP 422 Error / Exception
{
  "message": "رصيد المشترك غير كافٍ لإتمام عملية السحب."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/register-debt تسجيل مديونية (آجل) على المشترك Bearer Token (auth.admin_app)

تسجيل فاتورة أو قيد دين على المشترك مع تحديد القيمة والبيان وتاريخ الاستحقاق لتظهر في كشف حساب العميل والفواتير المستحقة.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
amount body number إلزامي مبلغ المديونية
description body string اختياري سبب تسجيل الدين (مثال: باقي تجديد باقة الشهر)
due_date body string (YYYY-MM-DD) اختياري تاريخ الاستحقاق
جسم الطلب (Request Payload JSON)
{
  "amount": 100.0,
  "description": "باقي اشتراك شهر سبتمبر",
  "due_date": "2026-09-15"
}
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/register-debt")
suspend fun registerDebt(
    @Path("id") id: Int,
    @Body data: Map<String, Any?>
): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/register-debt" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"amount": 100.00, "description": "آجل اشتراك"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/register-debt`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"amount": 100.0, "description": "باقي اشتراك شهر سبتمبر", "due_date": "2026-09-15"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/register-debt');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"amount": 100.0, "description": "باقي اشتراك شهر سبتمبر", "due_date": "2026-09-15"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/register-debt', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"amount": 100.0, "description": "باقي اشتراك شهر سبتمبر", "due_date": "2026-09-15"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "total_unpaid_debt": 100.0,
  "invoice_id": 4012,
  "message": "تم تسجيل المديونية وإنشاء فاتورة مستحقة بنجاح"
}
HTTP 422 Error / Exception
{
  "message": "المبلغ يجب أن يكون أكبر من الصفر."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/smart-pay السداد الذكي (Smart Pay: خصم رصيد أو تسجيل آجل) Bearer Token (auth.admin_app)

نظام تحصيل ذكي يقوم بفحص رصيد محفظة العميل وسداد الفاتورة تلقائياً من الرصيد المتوفر، وفي حال عدم كفاية الرصيد يتم استهلاك المتوفر وتسجيل المبلغ المتبقي كمديونية آمنة.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
invoice_id body integer إلزامي معرف الفاتورة المراد سدادها
allow_partial body boolean اختياري السماح بالسداد الجزئي في حال نقص الرصيد (الافتراضي: true)
جسم الطلب (Request Payload JSON)
{
  "invoice_id": 4012,
  "allow_partial": true
}
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/smart-pay")
suspend fun smartPay(
    @Path("id") id: Int,
    @Body data: Map<String, Any>
): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/smart-pay" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"invoice_id": 4012}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/smart-pay`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"invoice_id": 4012, "allow_partial": true})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/smart-pay');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"invoice_id": 4012, "allow_partial": true}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/smart-pay', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"invoice_id": 4012, "allow_partial": true}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "paid_amount": 100.0,
  "remaining_debt": 0.0,
  "invoice_status": "paid",
  "message": "تم سداد الفاتورة بنجاح عبر النظام الذكي"
}
HTTP 404 Error / Exception
{
  "message": "الفاتورة غير موجودة."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/renew تجديد اشتراك المشترك (Subscription Renewal) Bearer Token (auth.admin_app)

تجديد اشتراك المشترك مع خيارات متقدمة: تحديد طريقة الدفع (كاش، محفظة المشترك، آجل/دين)، وتطبيق خصم مخصص (نسبة أو مبلغ ثابت)، وإمكانية إضافة أيام إضافية كهدية، وتحديث بيانات الراديوس فورياً وإرسال CoA لإعادة ضبط السرعة.

الصلاحية المطلوبة: صلاحية التجديد (renew subscriptions) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
payment_method body string اختياري طريقة الدفع: cash (كاش للمدير), wallet (من رصيد المشترك), debt (آجل)
custom_discount body number اختياري قيمة الخصم المالي المطبق
extra_days body integer اختياري أيام إضافية تُضاف للصلاحية
notes body string اختياري ملاحظات عملية التجديد
جسم الطلب (Request Payload JSON)
{
  "payment_method": "cash",
  "custom_discount": 20.0,
  "extra_days": 2,
  "notes": "تجديد اشتراك باقة سوبر كاش مع خصم العملاء المميزين"
}
نماذج الأكواد البرمجية:
// ApiService.kt
@POST("subscribers/{id}/renew")
suspend fun renewSubscriber(
    @Path("id") id: Int,
    @Body data: Map<String, Any?> = emptyMap()
): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/renew" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"payment_method": "cash", "custom_discount": 20.00}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/renew`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"payment_method": "cash", "custom_discount": 20.0, "extra_days": 2, "notes": "تجديد اشتراك باقة سوبر كاش مع خصم العملاء المميزين"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/renew');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"payment_method": "cash", "custom_discount": 20.0, "extra_days": 2, "notes": "تجديد اشتراك باقة سوبر كاش مع خصم العملاء المميزين"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/renew', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"payment_method": "cash", "custom_discount": 20.0, "extra_days": 2, "notes": "تجديد اشتراك باقة سوبر كاش مع خصم العملاء المميزين"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تجديد اشتراك المشترك بنجاح",
  "subscription": {
    "id": 893,
    "plan_name": "باقة سوبر 30 ميجا",
    "price": 160.0,
    "starts_at": "2026-09-09 14:30:00",
    "expires_at": "2026-10-11 23:59:59",
    "quota_gb": 250
  },
  "renewal_log_id": 512
}
HTTP 422 Error / Exception
{
  "message": "رصيد المشرف غير كافٍ لتغطية تكلفة الجملة للباقة."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/change-plan تغيير باقة المشترك (ترقية / تخفيض مع حساب الفارق) Bearer Token (auth.admin_app)

ترقية أو تخفيض باقة المشترك مع حساب فارق السعر تلقائياً بنظام الـ Prorated أو التحصيل الكامل، وتحديث سرعة المشترك لحظياً عبر MikroTik CoA مع خيار التنفيذ الفوري أو الجدولة لنهاية الفترة الحالية.

الصلاحية المطلوبة: مشرف مسجل (change subscriber plans) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
service_plan_id body integer إلزامي معرف الباقة الجديدة المراد الانتقال إليها
timing body string اختياري توقيت التنفيذ: immediate (فوري الآن) أو end_of_cycle (عند انتهاء الفترة الحالية)
calculation_method body string اختياري طريقة الحساب: prorated (حساب فرق الأيام) أو full_price (تحصيل كامل الباقة)
payment_method body string اختياري طريقة السداد: cash أو wallet أو debt
جسم الطلب (Request Payload JSON)
{
  "service_plan_id": 2,
  "timing": "immediate",
  "calculation_method": "prorated",
  "payment_method": "cash"
}
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/change-plan")
suspend fun changePlan(
    @Path("id") id: Int,
    @Body data: Map<String, Any>
): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/change-plan" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"service_plan_id": 2, "timing": "immediate"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/change-plan`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"service_plan_id": 2, "timing": "immediate", "calculation_method": "prorated", "payment_method": "cash"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/change-plan');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"service_plan_id": 2, "timing": "immediate", "calculation_method": "prorated", "payment_method": "cash"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/change-plan', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"service_plan_id": 2, "timing": "immediate", "calculation_method": "prorated", "payment_method": "cash"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم ترقية باقة المشترك وتحديث السرعة بنجاح عبر MikroTik CoA",
  "new_plan_name": "باقة الترا 70 ميجا",
  "price_difference": 70.0,
  "expires_at": "2026-09-23 23:59:59"
}
HTTP 422 Error / Exception
{
  "message": "الباقة المختارة غير متوافقة مع هذا المشترك."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/reverse-renewal إلغاء والتراجع عن عملية التجديد (Reverse Renewal) Bearer Token (auth.admin_app)

إلغاء آخر عملية تجديد تمت بالخطأ للمشترك واسترداد الرصيد المخصوم من محفظة المشرف وإعادة تاريخ الانتهاء والكوتا إلى ما كانا عليه قبل التجديد بدقة متناهية.

الصلاحية المطلوبة: مشرف مسجل (reverse renewals) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/reverse-renewal")
suspend fun reverseRenewal(
    @Path("id") subscriberId: Int,
    @Body data: Map<String, Any> = emptyMap()
): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/reverse-renewal" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/reverse-renewal`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/reverse-renewal');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/reverse-renewal', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم التراجع عن عملية التجديد واسترجاع الرصيد وتاريخ الانتهاء بنجاح",
  "refunded_amount": 160.0,
  "previous_expires_at": "2026-09-09 14:00:00"
}
HTTP 422 Error / Exception
{
  "message": "لا يمكن إلغاء التجديد؛ مرت أكثر من 24 ساعة على العملية أو تم استهلاك جزء كبير من الكوتا."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/subscribers/{id}/invoices/{invoiceId}/check-reversal فحص إمكانية إلغاء الفاتورة أو التجديد بأمان Bearer Token (auth.admin_app)

التحقق البرمجي المسبق مما إذا كانت الفاتورة أو عملية التجديد مؤهلة للإلغاء الآمن دون أخطاء محاسبية قبل تأكيد الإلغاء للمستخدم.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
invoiceId path integer إلزامي معرف الفاتورة
نماذج الأكواد البرمجية:
@GET("subscribers/{id}/invoices/{invoiceId}/check-reversal")
suspend fun checkReversal(
    @Path("id") subscriberId: Int,
    @Path("invoiceId") invoiceId: Int
): Response<Map<String, Any>>
curl -X GET "https://isp.freenet.net/api/manager/subscribers/105/invoices/4012/check-reversal" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/invoices/{invoiceId}/check-reversal`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/invoices/{invoiceId}/check-reversal');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/subscribers/{id}/invoices/{invoiceId}/check-reversal', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "can_reverse": true,
  "refund_amount": 180.0,
  "reason": "العملية تمت اليوم ولم تتجاوز المدة المسموحة"
}
HTTP 404 Error / Exception
{
  "message": "الفاتورة غير موجودة."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/quota تعديل وإضافة الجيجات والكوتا (Adjust Quota) Bearer Token (auth.admin_app)

إضافة جيجابايت إضافية (Bonus Quota) للمشترك أو تعديل إجمالي وسعة الكوتا الحالية فورياً وتحديث عدادات الراديوس.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
quota_gb body number إلزامي عدد الجيجابايت المراد إضافتها أو ضبطها
mode body string اختياري الوضع: add (إضافة للكوتا الحالية) أو set (تعيين قيمة جديدة)
جسم الطلب (Request Payload JSON)
{
  "quota_gb": 50,
  "mode": "add"
}
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/quota")
suspend fun adjustQuota(
    @Path("id") id: Int,
    @Body data: Map<String, Any>
): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/quota" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"quota_gb": 50, "mode": "add"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/quota`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"quota_gb": 50, "mode": "add"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/quota');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"quota_gb": 50, "mode": "add"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/quota', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"quota_gb": 50, "mode": "add"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "new_total_quota_gb": 300,
  "new_remaining_quota_gb": 157.5,
  "message": "تمت إضافة 50 جيجابايت لاشتراك المشترك بنجاح"
}
HTTP 422 Error / Exception
{
  "message": "The given data was invalid."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/expiry تعديل وتمديد تاريخ انتهاء الاشتراك (Adjust Expiry) Bearer Token (auth.admin_app)

تمديد أو تقديم تاريخ انتهاء الاشتراك بعدد أيام محدد أو تحديد تاريخ ووقت انتهاء جديد بالكامل.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
days body integer اختياري عدد الأيام المراد إضافتها (أو طرحها بقيمة سالبة)
exact_date body string (YYYY-MM-DD HH:mm:ss) اختياري تاريخ محدد لانتهاء الاشتراك
جسم الطلب (Request Payload JSON)
{
  "days": 7
}
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/expiry")
suspend fun adjustExpiry(
    @Path("id") id: Int,
    @Body data: Map<String, Any>
): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/expiry" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"days": 7}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/expiry`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"days": 7})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/expiry');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"days": 7}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/expiry', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"days": 7}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "new_expires_at": "2026-09-30 23:59:59",
  "message": "تم تمديد فترة الاشتراك لمدة 7 أيام بنجاح"
}
HTTP 422 Error / Exception
{
  "message": "يرجى تحديد عدد الأيام أو التاريخ المحدد."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/speed-limit تطبيق سرعة مخصصة أو مؤقتة (Custom Speed via CoA) Bearer Token (auth.admin_app)

تطبيق سرعة تحميل ورفع مخصصة للمشترك مع إرسال أمر MikroTik CoA فوري لتحديث سرعة الـ Queue دون فصل اتصال العميل، مع إمكانية تحديد مدة زمنية لعودة السرعة تلقائياً.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
download_speed_kbps body integer إلزامي سرعة التحميل بالكيلوبت (مثال 51200 لـ 50 ميجا)
upload_speed_kbps body integer إلزامي سرعة الرفع بالكيلوبت (مثال 10240 لـ 10 ميجا)
duration_hours body integer اختياري مدة تطبيق السرعة بالساعات (اختياري للسرعة المؤقتة)
جسم الطلب (Request Payload JSON)
{
  "download_speed_kbps": 51200,
  "upload_speed_kbps": 10240,
  "duration_hours": 24
}
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/speed-limit")
suspend fun updateSpeedLimits(
    @Path("id") id: Int,
    @Body data: Map<String, Any?>
): Response<Unit>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/speed-limit" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"download_speed_kbps": 51200, "upload_speed_kbps": 10240}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/speed-limit`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"download_speed_kbps": 51200, "upload_speed_kbps": 10240, "duration_hours": 24})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/speed-limit');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"download_speed_kbps": 51200, "upload_speed_kbps": 10240, "duration_hours": 24}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/speed-limit', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"download_speed_kbps": 51200, "upload_speed_kbps": 10240, "duration_hours": 24}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تطبيق السرعة وإرسال حزم الـ CoA بنجاح إلى الراوتر"
}
HTTP 500 Error / Exception
{
  "message": "فشل إرسال حزمة الـ CoA إلى الراوتر."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/speed-limit/revoke إلغاء السرعة المؤقتة والرجوع لسرعة الباقة الأصلية Bearer Token (auth.admin_app)

إلغاء أي سرعة استثنائية أو مؤقتة وإعادة فرض سرعة الباقة المسجلة في الاشتراك عبر الـ CoA فورياً.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/speed-limit/revoke")
suspend fun revokeTemporarySpeed(@Path("id") id: Int): Response<SimpleResponse>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/speed-limit/revoke" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/speed-limit/revoke`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/speed-limit/revoke');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/speed-limit/revoke', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم إلغاء السرعة المؤقتة والعودة لسرعة الباقة بنجاح"
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/mac-lock تفعيل / إلغاء القفل على الماك أدرس (Toggle MAC Lock) Bearer Token (auth.admin_app)

تفعيل أو إلغاء ميزة قفل الحساب على الماك أدرس لجهاز المشترك لمنع مشاركة الحساب أو تشغيله من رواتر آخر.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/mac-lock")
suspend fun toggleMacLock(@Path("id") id: Int): Response<Unit>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/mac-lock" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/mac-lock`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/mac-lock');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/mac-lock', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "mac_lock_enabled": true,
  "message": "تم تفعيل القفل على الماك أدرس بنجاح"
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/reset-mac مسح وإعادة تعيين الماك أدرس المقترن (Reset MAC) Bearer Token (auth.admin_app)

مسح عنوان الماك المقيد بحساب العميل للسماح له بالاتصال بجهاز أو راوتر جديد، مع فصل أي جلسة عالقة قديمة.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/reset-mac")
suspend fun resetMac(@Path("id") id: Int): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/reset-mac" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/reset-mac`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/reset-mac');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/reset-mac', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم مسح الماك أدرس المقترن بنجاح وأصبح الحساب جاهزاً للاتصال بالراوتر الجديد"
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
DELETE /api/manager/subscribers/{id}/macs/{macId} حذف ماك أدرس محدد من قائمة الأجهزة المسموحة Bearer Token (auth.admin_app)

حذف عنوان ماك محدد من قائمة الأجهزة المقترنة بحساب المشترك عند السماح بعدة أجهزة.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
macId path integer إلزامي معرف سجل الماك
نماذج الأكواد البرمجية:
@DELETE("subscribers/{id}/macs/{macId}")
suspend fun deleteMac(@Path("id") id: Int, @Path("macId") macId: Int): Response<Unit>
curl -X DELETE "https://isp.freenet.net/api/manager/subscribers/105/macs/44" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/macs/{macId}`, {
    method: 'DELETE',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/macs/{macId}');
  final response = await http.delete(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('DELETE', 'api/manager/subscribers/{id}/macs/{macId}', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم حذف الماك المحدد بنجاح"
}
HTTP 404 Error / Exception
{
  "message": "سجل الماك غير موجود."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/sessions تعديل عدد الجلسات المتزامنة المسموحة (Simultaneous Sessions) Bearer Token (auth.admin_app)

تعديل خاصية Simultaneous-Use في الراديوس لتحديد عدد الأجهزة المسموح لها بالاتصال بالحساب في نفس اللحظة.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
simultaneous_sessions body integer إلزامي العدد الأقصى للجلسات (مثال: 1 أو 2 أو 5)
جسم الطلب (Request Payload JSON)
{
  "simultaneous_sessions": 2
}
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/sessions")
suspend fun updateSimultaneousSessions(@Path("id") id: Int, @Body data: Map<String, Any?>): Response<Unit>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/sessions" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"simultaneous_sessions": 2}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/sessions`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"simultaneous_sessions": 2})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/sessions');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"simultaneous_sessions": 2}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/sessions', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"simultaneous_sessions": 2}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "simultaneous_sessions": 2,
  "message": "تم تحديث حد الجلسات المتزامنة بنجاح"
}
HTTP 422 Error / Exception
{
  "message": "القيمة يجب أن تكون 1 على الأقل."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/usage/reset تصفير استهلاك الباندويث الشهري للمشترك Bearer Token (auth.admin_app)

تصفير عدادات الاستهلاك الشهري المسجلة في جداول الاشتراكات والراديوس ليعود الاستهلاك إلى 0 جيجابايت مع الحفاظ على فترة الاشتراك.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/usage/reset")
suspend fun resetSubscriberUsage(@Path("id") id: Int): Response<Unit>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/usage/reset" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/usage/reset`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/usage/reset');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/usage/reset', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تصفير استهلاك المشترك بنجاح"
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/usage/reset-daily تصفير الاستهلاك اليومي وسياسة الاستخدام العادل (FUP Reset) Bearer Token (auth.admin_app)

تصفير استهلاك اليوم الحالي لفك خنق السرعة الناتج عن تجاوز الحد اليومي للـ Fair Usage Policy فورياً وإرسال CoA لإعادة السرعة القصوى.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/usage/reset-daily")
suspend fun resetDailyUsage(@Path("id") id: Int): Response<Unit>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/usage/reset-daily" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/usage/reset-daily`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/usage/reset-daily');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/usage/reset-daily', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تصفير الاستهلاك اليومي بنجاح واستعادة السرعة الكاملة"
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/voucher/redeem شحن وتجديد المشترك عبر كود كارت (Redeem Voucher) Bearer Token (auth.admin_app)

إدخال كود كارت شحن وشحنه مباشرة لحساب المشترك لتجديد باقته أو إضافة رصيد أو شحن باقة إضافية.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
code body string إلزامي كود كارت الشحن (PIN Code)
جسم الطلب (Request Payload JSON)
{
  "code": "FREE-9874-2145-6632"
}
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/voucher/redeem")
suspend fun redeemVoucher(
    @Path("id") id: Int,
    @Body data: Map<String, Any>
): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/voucher/redeem" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"code": "FREE-9874-2145-6632"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/voucher/redeem`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"code": "FREE-9874-2145-6632"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/voucher/redeem');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"code": "FREE-9874-2145-6632"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/voucher/redeem', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"code": "FREE-9874-2145-6632"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم شحن الكارت وتجديد الاشتراك بنجاح",
  "plan_name": "باقة سوبر 30 ميجا",
  "new_expires_at": "2026-10-09 23:59:59"
}
HTTP 422 Error / Exception
{
  "message": "كود الكارت غير صالح أو تم استخدامه مسبقاً."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/subscribers/{id}/services الباقات الإضافية والخدمات التكميلية المتاحة (Addon Services) Bearer Token (auth.admin_app)

استعراض باقات الجيجات الإضافية وباقات السلف والخدمات التكميلية المتوافقة مع باقة هذا المشترك لإمكانية إضافتها.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
type query string اختياري نوع الخدمة: addon, loan, speed_boost
نماذج الأكواد البرمجية:
@GET("subscribers/{id}/services")
suspend fun getAvailableServices(@Path("id") id: Int, @Query("type") type: String): Response<AdditionalPackageListResponse>
curl -X GET "https://isp.freenet.net/api/manager/subscribers/105/services?type=addon" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/services`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/services');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/subscribers/{id}/services', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "services": [
    {
      "id": 10,
      "name": "باقة 20 جيجا إضافية",
      "price": 30.0,
      "quota_gb": 20
    },
    {
      "id": 11,
      "name": "باقة 50 جيجا إضافية",
      "price": 60.0,
      "quota_gb": 50
    },
    {
      "id": 12,
      "name": "سلفني 5 جيجا (صالحة 48 ساعة)",
      "price": 10.0,
      "quota_gb": 5
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/services تعيين وشراء باقة إضافية أو خدمة للمشترك Bearer Token (auth.admin_app)

شراء وإضافة باقة جيجات إضافية أو تفعيل خدمة سلفة للمشترك مع خصم السعر وتحديث الكوتا الحالية فورياً.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
service_id body integer إلزامي معرف الخدمة أو الباقة الإضافية
payment_method body string اختياري طريقة الدفع: wallet, cash, debt
جسم الطلب (Request Payload JSON)
{
  "service_id": 10,
  "payment_method": "cash"
}
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/services")
suspend fun assignService(@Path("id") id: Int, @Body data: Map<String, Any>): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/services" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"service_id": 10, "payment_method": "cash"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/services`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"service_id": 10, "payment_method": "cash"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/services');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"service_id": 10, "payment_method": "cash"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/services', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"service_id": 10, "payment_method": "cash"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تفعيل باقة 20 جيجا إضافية للمشترك بنجاح",
  "new_remaining_quota": "127.5 GB"
}
HTTP 422 Error / Exception
{
  "message": "الرصيد غير كافٍ لتفعيل هذه الباقة الإضافية."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/scheduled/activate تفعيل الاشتراك المجدول فوراً (Activate Scheduled) Bearer Token (auth.admin_app)

في حال وجود اشتراك أو باقة مجدولة للبدء عند انتهاء الفترة الحالية، تتيح هذه الدالة تفعيلها فوراً واستبدال الفترة الحالية.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/scheduled/activate")
suspend fun activateScheduledSubscription(@Path("id") id: Int): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/scheduled/activate" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/scheduled/activate`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/scheduled/activate');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/scheduled/activate', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تفعيل الاشتراك المجدول فورياً وتحديث صلاحية العميل"
}
HTTP 404 Error / Exception
{
  "message": "لا يوجد اشتراك مجدول قيد الانتظار لهذا المشترك."
}
⚡ اختبار الطلب الحي (Interactive Tester)
DELETE /api/manager/subscribers/{id}/scheduled إلغاء الاشتراك المجدول واسترجاع الرصيد Bearer Token (auth.admin_app)

إلغاء الاشتراك المجدول قيد الانتظار واسترداد قيمته المدفوعة لمحفظة المشرف أو المشترك بأمان.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
نماذج الأكواد البرمجية:
@DELETE("subscribers/{id}/scheduled")
suspend fun cancelScheduledSubscription(@Path("id") id: Int): Response<Map<String, Any>>
curl -X DELETE "https://isp.freenet.net/api/manager/subscribers/105/scheduled" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/scheduled`, {
    method: 'DELETE',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/scheduled');
  final response = await http.delete(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('DELETE', 'api/manager/subscribers/{id}/scheduled', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم إلغاء الاشتراك المجدول واسترداد الرصيد بنجاح"
}
HTTP 404 Error / Exception
{
  "message": "لا يوجد اشتراك مجدول."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/subscribers/{id}/internet/schedule جدول الرقابة العائلية وساعات إتاحة الإنترنت Bearer Token (auth.admin_app)

استعلام جدول ساعات عمل الإنترنت المخصص للمشترك (ساعات السماح وساعات الحظر التلقائي لكل يوم من أيام الأسبوع).

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
نماذج الأكواد البرمجية:
@GET("subscribers/{id}/internet/schedule")
suspend fun getInternetSchedule(@Path("id") id: Int): Response<InternetScheduleResponse>
curl -X GET "https://isp.freenet.net/api/manager/subscribers/105/internet/schedule" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/internet/schedule`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/internet/schedule');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/subscribers/{id}/internet/schedule', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "is_schedule_enabled": true,
  "schedule": {
    "saturday": {
      "enabled": true,
      "allow_from": "08:00",
      "allow_to": "23:00"
    },
    "sunday": {
      "enabled": true,
      "allow_from": "08:00",
      "allow_to": "22:00"
    }
  }
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/internet/schedule تحديث جدول الرقابة العائلية وإتاحة الإنترنت Bearer Token (auth.admin_app)

حفظ وضبط جدول مواعيد إيقاف وتشغيل النت الأسبوعي الخاص بالمشترك.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
is_enabled body boolean إلزامي تفعيل أو تعطيل الجدولة
schedule body object إلزامي كائن الجدولة الأسبوعية
جسم الطلب (Request Payload JSON)
{
  "is_enabled": true,
  "schedule": {
    "all_days": {
      "allow_from": "08:00",
      "allow_to": "23:00"
    }
  }
}
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/internet/schedule")
suspend fun updateInternetSchedule(@Path("id") id: Int, @Body payload: Map<String, Any>): Response<ResponseBody>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/internet/schedule" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"is_enabled": true, "schedule": {}}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/internet/schedule`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"is_enabled": true, "schedule": {"all_days": {"allow_from": "08:00", "allow_to": "23:00"}}})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/internet/schedule');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"is_enabled": true, "schedule": {"all_days": {"allow_from": "08:00", "allow_to": "23:00"}}}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/internet/schedule', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"is_enabled": true, "schedule": {"all_days": {"allow_from": "08:00", "allow_to": "23:00"}}}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تحديث جدول الرقابة العائلية بنجاح"
}
HTTP 422 Error / Exception
{
  "message": "The given data was invalid."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/reset-pin إعادة تعيين رمز الدخول السريع (PIN Code) Bearer Token (auth.admin_app)

إعادة تعيين أو توليد رمز دخول سريع جديد (4 أرقام) لبوابة المشترك الذاتية وتطبيق الهاتف.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/reset-pin")
suspend fun resetAccessPin(@Path("id") id: Int): Response<SimpleResponse>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/reset-pin" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/reset-pin`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/reset-pin');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/reset-pin', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "new_pin": "4891",
  "message": "تم إنشاء رمز PIN جديد للمشترك بنجاح"
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/credentials تعديل اسم المستخدم وكلمة المرور للمشترك Bearer Token (auth.admin_app)

تعديل بيانات الدخول الخاصة بالراديوس (Username & Password) وتحديثها فورياً في جداول radcheck وفصل أي جلسة مفتوحة بالبيانات القديمة.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
username body string اختياري اسم مستخدم جديد فريد
password body string اختياري كلمة مرور جديدة
جسم الطلب (Request Payload JSON)
{
  "username": "mahmoud.new",
  "password": "NewSecret2026@"
}
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/credentials")
suspend fun updateCredentials(@Path("id") id: Int, @Body payload: Map<String, Any?>): Response<SimpleResponse>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/credentials" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"password": "NewSecret2026@"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/credentials`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"username": "mahmoud.new", "password": "NewSecret2026@"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/credentials');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"username": "mahmoud.new", "password": "NewSecret2026@"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/credentials', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"username": "mahmoud.new", "password": "NewSecret2026@"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تحديث بيانات تسجيل الدخول وتحديث سجلات الراديوس بنجاح"
}
HTTP 422 Error / Exception
{
  "message": "اسم المستخدم هذا مسجل بالفعل لمشترك آخر."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/subscribers/{id}/sub-users قائمة المستخدمين الفرعيين ومشاركة الإنترنت (Sub-Users) Bearer Token (auth.admin_app)

استعراض حسابات المستخدمين الفرعيين (أفراد الأسرة أو الأجهزة المستقلة) التابعة لاشتراك العميل والتي تشارك نفس الباقة بحسابات دخول مستقلة.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك الرئيسي
نماذج الأكواد البرمجية:
@GET("subscribers/{id}/sub-users")
suspend fun getSubUsers(@Path("id") subscriberId: Int): Response<SubUsersResponse>
curl -X GET "https://isp.freenet.net/api/manager/subscribers/105/sub-users" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/sub-users`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/sub-users');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/subscribers/{id}/sub-users', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "allow_sub_users": true,
  "max_sub_users": 5,
  "data": [
    {
      "id": 12,
      "username": "mahmoud.ahmed_sub1",
      "name": "هاتف أحمد (الابن)",
      "is_active": true,
      "online_status": true,
      "quota_limit_gb": 30,
      "used_quota_gb": 18.4
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/sub-users إضافة مستخدم فرعي جديد للحساب Bearer Token (auth.admin_app)

إنشاء حساب دخول مستقل لمستخدم فرعي يشارك كوتا الحساب الرئيسي مع إمكانية تحديد سقف كوتا خاص به (Quota Cap).

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك الرئيسي
username_suffix body string إلزامي اللاحقة المضافة لاسم المستخدم
password body string إلزامي كلمة مرور المستخدم الفرعي
name body string إلزامي اسم صاحب الجهاز / المستخدم
quota_limit_gb body number اختياري الحد الأقصى للكوتا المسموحة له بالجيجابايت
جسم الطلب (Request Payload JSON)
{
  "username_suffix": "child1",
  "password": "ChildPass123@",
  "name": "لابتوب سارة",
  "quota_limit_gb": 50
}
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/sub-users")
suspend fun createSubUser(@Path("id") subscriberId: Int, @Body body: Map<String, Any?>): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/sub-users" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"username_suffix": "child1", "password": "Pass", "name": "لابتوب سارة"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/sub-users`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"username_suffix": "child1", "password": "ChildPass123@", "name": "لابتوب سارة", "quota_limit_gb": 50})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/sub-users');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"username_suffix": "child1", "password": "ChildPass123@", "name": "لابتوب سارة", "quota_limit_gb": 50}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/sub-users', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"username_suffix": "child1", "password": "ChildPass123@", "name": "لابتوب سارة", "quota_limit_gb": 50}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 201 Success
{
  "status": "success",
  "message": "تم إنشاء المستخدم الفرعي بنجاح",
  "sub_user": {
    "id": 13,
    "username": "mahmoud.ahmed_child1",
    "name": "لابتوب سارة"
  }
}
HTTP 422 Error / Exception
{
  "message": "تم الوصول للحد الأقصى لعدد المستخدمين الفرعيين المسموح به."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/sub-users/{subUserId}/toggle-status تفعيل / تعطيل مستخدم فرعي Bearer Token (auth.admin_app)

إيقاف أو استئناف خدمة الإنترنت لمستخدم فرعي محدد دون التأثير على باقي أجهزة الحساب.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك الرئيسي
subUserId path integer إلزامي معرف المستخدم الفرعي
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/sub-users/{subUserId}/toggle-status")
suspend fun toggleSubUserStatus(@Path("id") subscriberId: Int, @Path("subUserId") subUserId: Int): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/sub-users/12/toggle-status" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/sub-users/{subUserId}/toggle-status`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/sub-users/{subUserId}/toggle-status');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/sub-users/{subUserId}/toggle-status', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تحديث حالة المستخدم الفرعي بنجاح"
}
HTTP 404 Error / Exception
{
  "message": "المستخدم الفرعي غير موجود."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/sub-users/{subUserId}/disconnect فصل جلسة مستخدم فرعي عبر CoA Bearer Token (auth.admin_app)

فصل اتصال المستخدم الفرعي المحدد فقط من الراوتر دون قطع باقي جلسات المشترك.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك الرئيسي
subUserId path integer إلزامي معرف المستخدم الفرعي
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/sub-users/{subUserId}/disconnect")
suspend fun disconnectSubUser(@Path("id") subscriberId: Int, @Path("subUserId") subUserId: Int): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/sub-users/12/disconnect" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/sub-users/{subUserId}/disconnect`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/sub-users/{subUserId}/disconnect');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/sub-users/{subUserId}/disconnect', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم إرسال أمر فصل الجلسة للمستخدم الفرعي بنجاح"
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
DELETE /api/manager/subscribers/{id}/sub-users/{subUserId} حذف مستخدم فرعي نهائياً Bearer Token (auth.admin_app)

حذف حساب المستخدم الفرعي وإلغاء قيوده من سيرفر الراديوس.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك الرئيسي
subUserId path integer إلزامي معرف المستخدم الفرعي
نماذج الأكواد البرمجية:
@DELETE("subscribers/{id}/sub-users/{subUserId}")
suspend fun deleteSubUser(@Path("id") subscriberId: Int, @Path("subUserId") subUserId: Int): Response<Map<String, Any>>
curl -X DELETE "https://isp.freenet.net/api/manager/subscribers/105/sub-users/12" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/sub-users/{subUserId}`, {
    method: 'DELETE',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/sub-users/{subUserId}');
  final response = await http.delete(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('DELETE', 'api/manager/subscribers/{id}/sub-users/{subUserId}', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم حذف المستخدم الفرعي بنجاح"
}
HTTP 404 Error / Exception
{
  "message": "المستخدم الفرعي غير موجود."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/subscribers/{id}/nas-restriction قيود سيرفرات الراديوس المسموح بالاتصال منها (NAS Restriction) Bearer Token (auth.admin_app)

استعلام قائمة راوترات وسيرفرات الـ NAS المسموح للمشترك بالاتصال عبرها حصراً لمنع تشغيل الحساب خارج نطاق برجه الجغرافي.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
نماذج الأكواد البرمجية:
@GET("subscribers/{id}/nas-restriction")
suspend fun getNasRestriction(@Path("id") subscriberId: Int): Response<NasRestrictionResponse>
curl -X GET "https://isp.freenet.net/api/manager/subscribers/105/nas-restriction" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/nas-restriction`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/nas-restriction');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/subscribers/{id}/nas-restriction', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "is_restricted": true,
  "allowed_nas_ids": [
    1,
    2
  ],
  "allowed_nas_names": [
    "Core-Downtown",
    "Tower-East"
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/subscribers/{id}/nas-restriction تحديث قيود سيرفرات الراديوس المسموحة Bearer Token (auth.admin_app)

تحديث وحفظ قائمة راوترات الـ NAS المسموح للعميل بتسجيل الدخول منها في قاعدة بيانات الراديوس (radcheck Nas-IP-Address).

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
is_restricted body boolean إلزامي تفعيل القيد الجغرافي أو إلغاؤه
nas_ids body array of integers اختياري مصفوفة معرفات راوترات الـ NAS المسموحة
جسم الطلب (Request Payload JSON)
{
  "is_restricted": true,
  "nas_ids": [
    1
  ]
}
نماذج الأكواد البرمجية:
@POST("subscribers/{id}/nas-restriction")
suspend fun updateNasRestriction(@Path("id") subscriberId: Int, @Body body: Map<String, Any?>): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/subscribers/105/nas-restriction" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"is_restricted": true, "nas_ids": [1]}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/nas-restriction`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"is_restricted": true, "nas_ids": [1]})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/nas-restriction');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"is_restricted": true, "nas_ids": [1]}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/subscribers/{id}/nas-restriction', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"is_restricted": true, "nas_ids": [1]}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تحديث قيود سيرفرات الراديوس للمشترك بنجاح"
}
HTTP 422 Error / Exception
{
  "message": "The given data was invalid."
}
⚡ اختبار الطلب الحي (Interactive Tester)

📜 سجلات التجديد وإلغاء العمليات

SubscriptionRenewalLogController
3 نقطة اتصال

استعراض سجلات كافة عمليات التجديد وتغيير الباقات في النظام مع تفاصيل المبالغ المخصومة والمشرف المنفذ، وإمكانية التراجع عن أي عملية وإلغائها واسترداد الرصيد.

GET /api/manager/renewal-logs السجل العام لعمليات التجديد وتغيير الباقات Bearer Token (auth.admin_app)

استعراض جميع عمليات التجديد وتغيير الباقات التي تمت في الشبكة مع الفلترة حسب نوع العملية (renewal, plan_change)، أو المشرف، أو الباقة، أو حالة السداد، وتاريخ البداية والنهاية.

الصلاحية المطلوبة: مشرف مسجل (view renewal logs) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
page query integer اختياري رقم الصفحة
per_page query integer اختياري عدد السجلات (الافتراضي: 20)
search query string اختياري البحث باسم المشترك أو اسم المستخدم أو المشرف
action_type query string اختياري نوع العملية: renewal أو plan_change
service_plan_id query integer اختياري معرف الباقة
payment_status query string اختياري حالة السداد: paid, unpaid, debt
date_from query string (YYYY-MM-DD) اختياري من تاريخ
date_to query string (YYYY-MM-DD) اختياري إلى تاريخ
نماذج الأكواد البرمجية:
@GET("renewal-logs")
suspend fun getRenewalLogs(
    @Query("page") page: Int = 1,
    @Query("per_page") perPage: Int = 20,
    @Query("search") search: String? = null,
    @Query("action_type") actionType: String? = null,
    @Query("service_plan_id") servicePlanId: Int? = null,
    @Query("payment_status") paymentStatus: String? = null
): Response<RenewalLogsResponse>
curl -X GET "https://isp.freenet.net/api/manager/renewal-logs?page=1" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/renewal-logs`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/renewal-logs');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/renewal-logs', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "current_page": 1,
  "total": 384,
  "data": [
    {
      "id": 512,
      "subscriber_id": 105,
      "subscriber_name": "محمود أحمد حسن",
      "username": "mahmoud.ahmed",
      "action_type": "renewal",
      "service_plan_name": "باقة سوبر 30 ميجا",
      "price": 180.0,
      "discount": 20.0,
      "final_amount": 160.0,
      "payment_method": "cash",
      "admin_name": "المدير العام",
      "starts_at": "2026-09-09 14:30:00",
      "expires_at": "2026-10-11 23:59:59",
      "created_at": "2026-09-09 14:30:15",
      "can_reverse": true
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/subscribers/{id}/renewal-logs سجل تجديدات مشترك محدد Bearer Token (auth.admin_app)

استعراض السجل التاريخي لتجديدات وتغييرات باقات مشترك معين.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
page query integer اختياري رقم الصفحة
نماذج الأكواد البرمجية:
@GET("subscribers/{id}/renewal-logs")
suspend fun getSubscriberRenewalLogs(
    @Path("id") subscriberId: Int,
    @Query("page") page: Int = 1
): Response<RenewalLogsResponse>
curl -X GET "https://isp.freenet.net/api/manager/subscribers/105/renewal-logs" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/renewal-logs`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/renewal-logs');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/subscribers/{id}/renewal-logs', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "data": [
    {
      "id": 512,
      "action_type": "renewal",
      "service_plan_name": "باقة سوبر 30 ميجا",
      "final_amount": 160.0,
      "created_at": "2026-09-09 14:30:15"
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/renewal-logs/{id}/reverse التراجع المباشر عن عملية التجديد برقم السجل (Reverse by Log ID) Bearer Token (auth.admin_app)

التراجع المحاسبي والفني عن عملية التجديد المحددة بواسطة معرف السجل Log ID واسترداد الرصيد وإعادة فترة الاشتراك.

الصلاحية المطلوبة: مشرف مسجل (reverse renewals) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف سجل التجديد
نماذج الأكواد البرمجية:
@POST("renewal-logs/{id}/reverse")
suspend fun reverseRenewalLog(@Path("id") logId: Int, @Body body: Map<String, Any?> = emptyMap()): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/renewal-logs/512/reverse" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/renewal-logs/{id}/reverse`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/renewal-logs/{id}/reverse');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/renewal-logs/{id}/reverse', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم التراجع عن عملية التجديد بنجاح واسترداد المبلغ"
}
HTTP 422 Error / Exception
{
  "message": "لا يمكن التراجع عن هذه العملية؛ لقد مضى وقت طويل أو تم تعديل الاشتراك لاحقاً."
}
⚡ اختبار الطلب الحي (Interactive Tester)

💳 الفواتير والمتحصلات وطباعة الإيصالات الحرارية

InvoiceController
8 نقطة اتصال

إدارة دورة الفوترة الشاملة: استعلام فواتير النظام، إنشاء فواتير الاشتراكات، إنشاء فواتير مخصصة متعددة البنود والخدمات، سداد وإلغاء الفواتير، تعديل مواعيد الاستحقاق، واستخراج بيانات الطباعة الحرارية المجهزة لطابعات الفواتير المحمولة والـ 80mm POS Printers.

GET /api/manager/invoices/all قائمة فواتير النظام بالكامل مع الفلترة Bearer Token (auth.admin_app)

استرجاع جميع الفواتير المسجلة في النظام مع خيارات البحث والفلترة حسب الحالة (مدفوعة، غير مدفوعة، ملغاة، جزئية)، المشرف المسؤول، ونطاق التاريخ.

الصلاحية المطلوبة: مشرف مسجل (view invoices) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
page query integer اختياري رقم الصفحة
per_page query integer اختياري عدد الفواتير بالصفحة (الافتراضي 20)
search query string اختياري البحث برقم الفاتورة أو اسم المشترك أو هاتفه
status query string اختياري الحالة: paid, unpaid, partially_paid, cancelled
نماذج الأكواد البرمجية:
@GET("invoices/all")
suspend fun getAllInvoices(
    @Query("page") page: Int = 1,
    @Query("per_page") perPage: Int = 20,
    @Query("search") search: String? = null,
    @Query("status") status: String? = null
): Response<Map<String, Any>>
curl -X GET "https://isp.freenet.net/api/manager/invoices/all?status=unpaid" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/invoices/all`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/invoices/all');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/invoices/all', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "current_page": 1,
  "total": 920,
  "data": [
    {
      "id": 4012,
      "invoice_number": "INV-2026-09-0012",
      "subscriber_name": "محمود أحمد حسن",
      "subscriber_phone": "01098765432",
      "total_amount": 180.0,
      "paid_amount": 180.0,
      "remaining_amount": 0.0,
      "status": "paid",
      "due_date": "2026-09-24",
      "created_at": "2026-09-09 14:30:15"
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/subscribers/{id}/invoices فواتير مشترك محدد Bearer Token (auth.admin_app)

استعراض قائمة الفواتير التاريخية لمشترك معين في الشاشة المخصصة للمشترك.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف المشترك
page query integer اختياري رقم الصفحة
نماذج الأكواد البرمجية:
@GET("subscribers/{id}/invoices")
suspend fun getSubscriberInvoices(
    @Path("id") subscriberId: Int,
    @Query("page") page: Int = 1
): Response<InvoiceListResponse>
curl -X GET "https://isp.freenet.net/api/manager/subscribers/105/invoices" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/subscribers/{id}/invoices`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/subscribers/{id}/invoices');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/subscribers/{id}/invoices', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "data": [
    {
      "id": 4012,
      "invoice_number": "INV-2026-09-0012",
      "total_amount": 180.0,
      "status": "paid"
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/invoices/custom إنشاء فاتورة مخصصة متعددة البنود والخدمات Bearer Token (auth.admin_app)

إنشاء فاتورة يدوية مخصصة تحتوي على بنود متنوعة (مثل: تكلفة راوتر، رسوم تركيب وصيانة، كابلات، اشتراكات خاصة) مع حساب الإجمالي والضريبة والخصم.

الصلاحية المطلوبة: مشرف مسجل (create invoices) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
subscriber_id body integer إلزامي معرف المشترك
items body array of objects إلزامي قائمة البنود (الاسم، الكمية، سعر الوحدة)
notes body string اختياري ملاحظات الفاتورة
due_date body string (YYYY-MM-DD) اختياري تاريخ الاستحقاق
جسم الطلب (Request Payload JSON)
{
  "subscriber_id": 105,
  "items": [
    {
      "description": "راوتر تي بي لينك AC1200",
      "quantity": 1,
      "unit_price": 850.0
    },
    {
      "description": "كابل سلك شبكة 30 متر",
      "quantity": 30,
      "unit_price": 8.0
    },
    {
      "description": "مصنعية تركيب وصيانة",
      "quantity": 1,
      "unit_price": 100.0
    }
  ],
  "notes": "تركيب جديد الدور الثالث",
  "due_date": "2026-09-20"
}
نماذج الأكواد البرمجية:
@POST("invoices/custom")
suspend fun createCustomInvoice(@Body body: Map<String, Any?>): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/invoices/custom" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"subscriber_id": 105, "items": [{"description": "راوتر", "quantity": 1, "unit_price": 850}]}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/invoices/custom`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"subscriber_id": 105, "items": [{"description": "راوتر تي بي لينك AC1200", "quantity": 1, "unit_price": 850.0}, {"description": "كابل سلك شبكة 30 متر", "quantity": 30, "unit_price": 8.0}, {"description": "مصنعية تركيب وصيانة", "quantity": 1, "unit_price": 100.0}], "notes": "تركيب جديد الدور الثالث", "due_date": "2026-09-20"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/invoices/custom');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"subscriber_id": 105, "items": [{"description": "راوتر تي بي لينك AC1200", "quantity": 1, "unit_price": 850.0}, {"description": "كابل سلك شبكة 30 متر", "quantity": 30, "unit_price": 8.0}, {"description": "مصنعية تركيب وصيانة", "quantity": 1, "unit_price": 100.0}], "notes": "تركيب جديد الدور الثالث", "due_date": "2026-09-20"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/invoices/custom', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"subscriber_id": 105, "items": [{"description": "راوتر تي بي لينك AC1200", "quantity": 1, "unit_price": 850.0}, {"description": "كابل سلك شبكة 30 متر", "quantity": 30, "unit_price": 8.0}, {"description": "مصنعية تركيب وصيانة", "quantity": 1, "unit_price": 100.0}], "notes": "تركيب جديد الدور الثالث", "due_date": "2026-09-20"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 201 Success
{
  "status": "success",
  "message": "تم إنشاء الفاتورة المخصصة بنجاح",
  "invoice": {
    "id": 4013,
    "invoice_number": "INV-CUSTOM-2026-0045",
    "total_amount": 1190.0,
    "status": "unpaid"
  }
}
HTTP 422 Error / Exception
{
  "message": "قائمة البنود items مطلوبة."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/invoices/{invoiceId} عرض تفاصيل الفاتورة الكاملة والبنود Bearer Token (auth.admin_app)

استعراض تفاصيل الفاتورة، البنود الفردية، المدفوعات المسجلة عليها، والمتبقي وتاريخ الاستحقاق.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
invoiceId path integer إلزامي معرف الفاتورة
نماذج الأكواد البرمجية:
@GET("invoices/{invoiceId}")
suspend fun getInvoiceDetails(@Path("invoiceId") invoiceId: Int): Response<InvoiceDetailResponse>
curl -X GET "https://isp.freenet.net/api/manager/invoices/4013" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/invoices/{invoiceId}`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/invoices/{invoiceId}');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/invoices/{invoiceId}', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "id": 4013,
  "invoice_number": "INV-CUSTOM-2026-0045",
  "subscriber": {
    "id": 105,
    "name": "محمود أحمد حسن",
    "phone": "01098765432"
  },
  "total_amount": 1190.0,
  "paid_amount": 0.0,
  "remaining_amount": 1190.0,
  "status": "unpaid",
  "items": [
    {
      "description": "راوتر تي بي لينك AC1200",
      "quantity": 1,
      "unit_price": 850.0,
      "total": 850.0
    }
  ]
}
HTTP 404 Error / Exception
{
  "message": "الفاتورة غير موجودة."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/invoices/{invoiceId}/pay سداد الفاتورة (Pay Invoice) Bearer Token (auth.admin_app)

تسجيل سداد الفاتورة نقداً أو عبر محفظة المشترك أو بطرق الدفع الإلكتروني وتحديث حالة الفاتورة لـ Paid.

الصلاحية المطلوبة: مشرف مسجل (settle invoices) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
invoiceId path integer إلزامي معرف الفاتورة
amount body number اختياري المبلغ المدفوع (الافتراضي: كامل قيمة الفاتورة)
payment_method body string اختياري طريقة الدفع: cash, wallet
جسم الطلب (Request Payload JSON)
{
  "amount": 1190.0,
  "payment_method": "cash"
}
نماذج الأكواد البرمجية:
@POST("invoices/{invoiceId}/pay")
suspend fun payInvoiceDirect(
    @Path("invoiceId") invoiceId: Int,
    @Body payload: Map<String, Any?>
): Response<SimpleResponse>
curl -X POST "https://isp.freenet.net/api/manager/invoices/4013/pay" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"amount": 1190.00, "payment_method": "cash"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/invoices/{invoiceId}/pay`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"amount": 1190.0, "payment_method": "cash"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/invoices/{invoiceId}/pay');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"amount": 1190.0, "payment_method": "cash"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/invoices/{invoiceId}/pay', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"amount": 1190.0, "payment_method": "cash"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم سداد الفاتورة بنجاح وإصدار إيصال السداد",
  "remaining_amount": 0.0,
  "invoice_status": "paid"
}
HTTP 422 Error / Exception
{
  "message": "الفاتورة مسددة بالكامل بالفعل."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/invoices/{invoiceId}/reverse إلغاء والتراجع عن سداد الفاتورة (Reverse Payment) Bearer Token (auth.admin_app)

إلغاء عملية سداد الفاتورة وإعادة فتحها كـ Unpaid واسترجاع المبالغ لحساب المشرف أو المشترك في حال الخطأ.

الصلاحية المطلوبة: مشرف مسجل (reverse invoices) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
invoiceId path integer إلزامي معرف الفاتورة
نماذج الأكواد البرمجية:
@POST("invoices/{invoiceId}/reverse")
suspend fun reverseInvoice(
    @Path("invoiceId") invoiceId: Int,
    @Body payload: Map<String, Any?> = emptyMap()
): Response<SimpleResponse>
curl -X POST "https://isp.freenet.net/api/manager/invoices/4013/reverse" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/invoices/{invoiceId}/reverse`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/invoices/{invoiceId}/reverse');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/invoices/{invoiceId}/reverse', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم التراجع عن سداد الفاتورة وإعادة فتحها بنجاح"
}
HTTP 422 Error / Exception
{
  "message": "لا يمكن التراجع عن هذه الفاتورة."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/invoices/{invoiceId}/print-data بيانات الطباعة الحرارية للإيصالات (80mm Thermal Receipt POS) Bearer Token (auth.admin_app)

جلب بيانات الفاتورة المنسقة والمجهزة خصيصاً للإرسال المباشر لطابعات البلوتوث والـ USB والـ Network Thermal Printers (80mm / 58mm ESC/POS) مع الترويسة وQR Code والفواتير الإلكترونية.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
invoiceId path integer إلزامي معرف الفاتورة
نماذج الأكواد البرمجية:
@GET("invoices/{invoiceId}/print-data")
suspend fun getInvoicePrintData(@Path("invoiceId") invoiceId: Int): Response<InvoiceDetailResponse>
curl -X GET "https://isp.freenet.net/api/manager/invoices/4012/print-data" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/invoices/{invoiceId}/print-data`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/invoices/{invoiceId}/print-data');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/invoices/{invoiceId}/print-data', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "company_name": "شبكة فري نت لخدمات الإنترنت",
  "branch_name": "فرع وسط البلد",
  "company_phone": "01000000000",
  "tax_number": "987-654-321",
  "receipt_number": "REC-2026-09-0012",
  "date": "2026-09-09 14:30:15",
  "subscriber_name": "محمود أحمد حسن",
  "username": "mahmoud.ahmed",
  "items": [
    {
      "name": "تجديد باقة سوبر 30 ميجا",
      "qty": 1,
      "price": 180.0,
      "total": 180.0
    }
  ],
  "subtotal": 180.0,
  "discount": 20.0,
  "total_paid": 160.0,
  "cashier_name": "أحمد علي",
  "qr_code_data": "https://isp.freenet.net/verify/inv/REC-2026-09-0012",
  "footer_message": "شكراً لاختياركم فري نت - خدمة العملاء 24/7"
}
HTTP 404 Error / Exception
{
  "message": "الفاتورة غير موجودة."
}
⚡ اختبار الطلب الحي (Interactive Tester)
PATCH /api/manager/invoices/{invoiceId}/due-date تعديل تاريخ استحقاق الفاتورة Bearer Token (auth.admin_app)

تأجيل أو تقديم تاريخ استحقاق الفاتورة للمشترك.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
invoiceId path integer إلزامي معرف الفاتورة
due_date body string (YYYY-MM-DD) إلزامي تاريخ الاستحقاق الجديد
جسم الطلب (Request Payload JSON)
{
  "due_date": "2026-10-01"
}
نماذج الأكواد البرمجية:
@PATCH("invoices/{invoiceId}/due-date")
suspend fun updateInvoiceDueDate(
    @Path("invoiceId") invoiceId: Int,
    @Body payload: Map<String, Any?>
): Response<SimpleResponse>
curl -X PATCH "https://isp.freenet.net/api/manager/invoices/4012/due-date" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"due_date": "2026-10-01"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/invoices/{invoiceId}/due-date`, {
    method: 'PATCH',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"due_date": "2026-10-01"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/invoices/{invoiceId}/due-date');
  final response = await http.patch(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"due_date": "2026-10-01"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('PATCH', 'api/manager/invoices/{invoiceId}/due-date', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"due_date": "2026-10-01"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تحديث تاريخ استحقاق الفاتورة بنجاح"
}
HTTP 422 Error / Exception
{
  "message": "تاريخ الاستحقاق غير صالح."
}
⚡ اختبار الطلب الحي (Interactive Tester)

🎫 كروت الشحن ونقاط البيع السريع (POS)

VoucherController
9 نقطة اتصال

إدارة دفعات كروت الشحن وطباعتها وتصديرها، التحقق من صلاحية الكروت، البيع السريع عبر نقاط البيع المخصصة (POS Fast Selling) مع بيانات الطباعة الفورية للطابعات الحرارية، وإحصائيات مبيعات الكروت اللحظية.

GET /api/manager/vouchers قائمة دفعات كروت الشحن (Voucher Batches) Bearer Token (auth.admin_app)

استعراض دفعات الكروت المنشأة مع إحصائيات كل دفعة: إجمالي عدد الكروت، الكروت المباعة/المستخدمة، الكروت المتاحة، وسعر الكارت.

الصلاحية المطلوبة: مشرف مسجل (manage vouchers) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
page query integer اختياري رقم الصفحة
search query string اختياري البحث باسم الدفعة أو الباقة
نماذج الأكواد البرمجية:
@GET("vouchers")
suspend fun getVoucherBatches(@Query("page") page: Int, @Query("search") search: String? = null): Response<VoucherBatchListResponse>
curl -X GET "https://isp.freenet.net/api/manager/vouchers?page=1" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/vouchers`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/vouchers');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/vouchers', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "data": [
    {
      "id": 15,
      "name": "دفعة كروت كافيه 5 جنيه - سبتمبر",
      "plan_name": "باقة 5 جنيه - 3 ساعات",
      "total_vouchers": 500,
      "used_vouchers": 312,
      "unused_vouchers": 188,
      "card_price": 5.0,
      "status": "active",
      "created_at": "2026-09-01 10:00:00"
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/vouchers/create-data بيانات مساعدة لإنشاء دفعة كروت جديدة Bearer Token (auth.admin_app)

جلب قائمة الباقات المدعومة للكروت، القوالب التصميمية المتاحة، والخيارات المسبقة لإنشاء الدفعة.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@GET("vouchers/create-data")
suspend fun getVoucherCreateData(): Response<VoucherCreateData>
curl -X GET "https://isp.freenet.net/api/manager/vouchers/create-data" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/vouchers/create-data`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/vouchers/create-data');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/vouchers/create-data', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "plans": [
    {
      "id": 5,
      "name": "كارت 5 جنيه - 500 ميجا",
      "price": 5.0
    },
    {
      "id": 6,
      "name": "كارت 10 جنيه - 1.5 جيجا",
      "price": 10.0
    }
  ],
  "templates": [
    {
      "id": 1,
      "name": "قالب كروت قياسي (6 في الورقة)"
    },
    {
      "id": 2,
      "name": "قالب مصغر (10 في الورقة)"
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/vouchers توليد وإنشاء دفعة كروت جديدة (Generate Batch) Bearer Token (auth.admin_app)

توليد دفعة جديدة من كروت الشحن العشوائية المشفرة بالكمية المحددة، واختيار طول الكود (مثال: 8 أو 12 رقم)، والبادئة (Prefix)، وتاريخ الصلاحية وتكلفة الكارت.

الصلاحية المطلوبة: مشرف مسجل (create vouchers) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
name body string إلزامي اسم الدفعة التوضيحي
service_plan_id body integer إلزامي معرف الباقة المربوطة بالكارت
quantity body integer إلزامي عدد الكروت المراد توليدها (10 - 2000)
code_length body integer اختياري طول كود الكارت (الافتراضي: 10 أرقام)
prefix body string اختياري بادئة اختيارية تسبق الأرقام (مثال: FN)
price body number اختياري سعر البيع الافتراضي للكارت
جسم الطلب (Request Payload JSON)
{
  "name": "دفعة كروت الأسبوع 10ج",
  "service_plan_id": 6,
  "quantity": 200,
  "code_length": 10,
  "prefix": "F10",
  "price": 10.0
}
نماذج الأكواد البرمجية:
@POST("vouchers")
@FormUrlEncoded
suspend fun createVoucherBatch(@FieldMap params: Map<String, Any>): Response<VoucherBatch>
curl -X POST "https://isp.freenet.net/api/manager/vouchers" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"name": "دفعة جديدة", "service_plan_id": 6, "quantity": 100}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/vouchers`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"name": "دفعة كروت الأسبوع 10ج", "service_plan_id": 6, "quantity": 200, "code_length": 10, "prefix": "F10", "price": 10.0})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/vouchers');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"name": "دفعة كروت الأسبوع 10ج", "service_plan_id": 6, "quantity": 200, "code_length": 10, "prefix": "F10", "price": 10.0}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/vouchers', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"name": "دفعة كروت الأسبوع 10ج", "service_plan_id": 6, "quantity": 200, "code_length": 10, "prefix": "F10", "price": 10.0}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 201 Success
{
  "status": "success",
  "message": "تم توليد 200 كارت بنجاح",
  "batch": {
    "id": 16,
    "name": "دفعة كروت الأسبوع 10ج",
    "total_vouchers": 200
  }
}
HTTP 422 Error / Exception
{
  "message": "رصيد المشرف غير كافٍ لتوليد هذه الكمية من الكروت."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/vouchers/batch/{id} استعراض كروت دفعة محددة Bearer Token (auth.admin_app)

جلب قائمة الكروت المفردة داخل الدفعة وحالة كل كارت (مباع، غير مستخدم، ملغي) وبيانات الاستخدام.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف الدفعة
نماذج الأكواد البرمجية:
@GET("vouchers/batch/{id}")
suspend fun getBatchVouchers(@Path("id") id: Int): Response<BatchVouchersResponse>
curl -X GET "https://isp.freenet.net/api/manager/vouchers/batch/16" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/vouchers/batch/{id}`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/vouchers/batch/{id}');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/vouchers/batch/{id}', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "batch_id": 16,
  "batch_name": "دفعة كروت الأسبوع 10ج",
  "vouchers": [
    {
      "id": 8410,
      "code": "F1089452174",
      "status": "unused",
      "used_at": null,
      "mac_address": null
    }
  ]
}
HTTP 404 Error / Exception
{
  "message": "الدفعة غير موجودة."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/vouchers/check فحص والتحقق من كود الكارت (Check Voucher) Bearer Token (auth.admin_app)

التحقق من حالة كود كارت الشحن وصلاحيته، ونوع الباقة وسعرها، وما إذا كان مستخدماً أو متاحاً للشحن.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
code body string إلزامي كود كارت الشحن
جسم الطلب (Request Payload JSON)
{
  "code": "F1089452174"
}
نماذج الأكواد البرمجية:
@POST("vouchers/check")
@FormUrlEncoded
suspend fun checkVoucher(@Field("code") code: String): Response<VoucherCheckResponse>
curl -X POST "https://isp.freenet.net/api/manager/vouchers/check" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"code": "F1089452174"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/vouchers/check`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"code": "F1089452174"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/vouchers/check');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"code": "F1089452174"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/vouchers/check', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"code": "F1089452174"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "is_valid": true,
  "status": "unused",
  "card_price": 10.0,
  "plan_name": "كارت 10 جنيه - 1.5 جيجا",
  "batch_name": "دفعة كروت الأسبوع 10ج"
}
HTTP 404 Error / Exception
{
  "message": "كود الكارت غير موجود بالنظام."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/vouchers/pos/batches كتالوج البيع السريع لنقاط البيع (POS Batches Catalog) Bearer Token (auth.admin_app)

قائمة الكروت والدفعات المجهزة لواجهات الكاشير والبيع السريع POS، مقسمة حسب الفئات والأسعار لسرعة اختيارها وبيعها بلمسة واحدة.

الصلاحية المطلوبة: مشرف مسجل (POS sell) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
type query string اختياري نوع الباقة: hotspot, time, quota
search query string اختياري البحث السريع
نماذج الأكواد البرمجية:
@GET("vouchers/pos/batches")
suspend fun getPosBatches(@Query("type") type: String? = null): Response<Map<String, Any>>
curl -X GET "https://isp.freenet.net/api/manager/vouchers/pos/batches" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/vouchers/pos/batches`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/vouchers/pos/batches');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/vouchers/pos/batches', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "categories": [
    {
      "category_name": "كروت الهوت سبوت",
      "items": [
        {
          "batch_id": 15,
          "name": "كارت 5ج (3 ساعات)",
          "price": 5.0,
          "available_count": 188
        },
        {
          "batch_id": 16,
          "name": "كارت 10ج (1.5 جيجا)",
          "price": 10.0,
          "available_count": 200
        }
      ]
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/vouchers/pos/sell البيع الفوري لكارت الشحن وطباعته (POS Instant Sell) Bearer Token (auth.admin_app)

سحب وبيع أول كارت متاح من الدفعة فورياً وتغيير حالته لـ Sold، وإرجاع بيانات الكارت المنسقة للطباعة الحرارية المباشرة (ESC/POS) متضمنة الكود وQR Code وتعليمات تسجيل الدخول.

الصلاحية المطلوبة: مشرف مسجل (POS sell) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
batch_id body integer إلزامي معرف دفعة الكروت المراد السحب منها
customer_phone body string اختياري رقم هاتف المشتري (اختياري لإرسال الكود له عبر الواتساب)
جسم الطلب (Request Payload JSON)
{
  "batch_id": 15,
  "customer_phone": "01011122233"
}
نماذج الأكواد البرمجية:
@POST("vouchers/pos/sell")
suspend fun posSellVoucher(@Body data: Map<String, Any?>): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/vouchers/pos/sell" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"batch_id": 15}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/vouchers/pos/sell`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"batch_id": 15, "customer_phone": "01011122233"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/vouchers/pos/sell');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"batch_id": 15, "customer_phone": "01011122233"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/vouchers/pos/sell', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"batch_id": 15, "customer_phone": "01011122233"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم بيع الكارت بنجاح",
  "voucher": {
    "id": 8411,
    "code": "FREE-9081234",
    "price": 5.0,
    "plan_name": "باقة 5 جنيه - 3 ساعات",
    "sold_at": "2026-09-09 14:35:00"
  },
  "print_payload": {
    "company_name": "شبكة فري نت",
    "card_code": "FREE-9081234",
    "price_formatted": "5.00 ج.م",
    "duration": "3 ساعات صالحة لمدة 24 ساعة",
    "login_url": "http://freenet.wifi",
    "support_phone": "01000000000"
  }
}
HTTP 422 Error / Exception
{
  "message": "نفدت الكروت غير المستخدمة من هذه الدفعة."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/vouchers/pos/reprint-last إعادة طباعة آخر كارت مباع (Reprint Last Sold Voucher) Bearer Token (auth.admin_app)

استرجاع بيانات طباعة آخر كارت تم بيعه عبر نقطة البيع في حال انحشار ورق الطابعة أو انقطاع البلوتوث.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@POST("vouchers/pos/reprint-last")
suspend fun posReprintLast(@Body data: Map<String, Any?> = emptyMap()): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/vouchers/pos/reprint-last" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/vouchers/pos/reprint-last`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/vouchers/pos/reprint-last');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/vouchers/pos/reprint-last', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "card_code": "FREE-9081234",
  "price": 5.0,
  "print_payload": {
    "card_code": "FREE-9081234",
    "price_formatted": "5.00 ج.م"
  }
}
HTTP 404 Error / Exception
{
  "message": "لا توجد عمليات بيع حديثة لإعادة طباعتها."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/vouchers/statistics إحصائيات وأرباح مبيعات الكروت (Voucher POS Analytics) Bearer Token (auth.admin_app)

تقارير مبيعات الكروت اللحظية: إجمالي المبيعات بالجنيه اليوم، عدد الكروت المباعة اليوم وهذا الشهر، وأكثر الدفعات مبيعاً.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
start_date query string اختياري من تاريخ
end_date query string اختياري إلى تاريخ
نماذج الأكواد البرمجية:
@GET("vouchers/statistics")
suspend fun getVoucherStatistics(@QueryMap params: Map<String, String> = emptyMap()): Response<Map<String, Any>>
curl -X GET "https://isp.freenet.net/api/manager/vouchers/statistics" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/vouchers/statistics`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/vouchers/statistics');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/vouchers/statistics', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "today_sales_amount": 1450.0,
  "today_sold_count": 185,
  "month_sales_amount": 32800.0,
  "month_sold_count": 4120,
  "top_batches": [
    {
      "name": "كارت 5 جنيه",
      "count": 120,
      "revenue": 600.0
    },
    {
      "name": "كارت 10 جنيه",
      "count": 65,
      "revenue": 650.0
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)

🌐 أجهزة الشبكة وراوترات المايكروتك والمراقبة اللحظية

NetworkDeviceController
5 نقطة اتصال

إدارة كامل البنية التحتية للشبكة: السويتشات، أجهزة البث، كبائن الـ OLT، وراوترات المايكروتك MikroTik، وفحص الاتصال الحي (Ping Tests)، وإعادة التشغيل عن بُعد (Remote Reboot)، وسجلات التوقف، وحصر المشتركين المتصلين بكل جهاز وتوليد أنفاق الاتصال الآمنة (Tunnels).

GET /api/manager/network-devices قائمة أجهزة ومعدات الشبكة (Network Inventory) Bearer Token (auth.admin_app)

استرجاع قائمة أجهزة الشبكة الموزعة بالأبراج والمواقع مع فلترة حسب النوع (سويتش، راوتر، أكسس بوينت)، الموقع، وحالة الاتصال (online / offline).

الصلاحية المطلوبة: مشرف مسجل (manage network devices) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
page query integer اختياري رقم الصفحة
type query string اختياري نوع الجهاز: switch, router, ap, olt
status query string اختياري الحالة: online, offline
search query string اختياري البحث بالاسم أو الـ IP أو الموقع
نماذج الأكواد البرمجية:
@GET("network-devices")
suspend fun getNetworkDevices(
    @Query("page") page: Int = 1,
    @Query("type") type: String? = null,
    @Query("status") status: String? = null
): Response<Map<String, Any>>
curl -X GET "https://isp.freenet.net/api/manager/network-devices" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/network-devices`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/network-devices');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/network-devices', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "data": [
    {
      "id": 1,
      "name": "Switch-Cisco-Tower1",
      "ip_address": "192.168.10.15",
      "type": "switch",
      "location": "برج التحرير - كابينة 1",
      "status": "online",
      "latency_ms": 1.8,
      "last_ping_at": "2026-09-09 14:34:20",
      "online_subscribers_count": 64
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/network-devices/{id}/ping فحص الاتصال بالجهاز اللحظي (Ping Test) Bearer Token (auth.admin_app)

إرسال حزم ICMP فحص لحظية للجهاز وقياس زمن الاستجابة (Latency) ونسبة فقد الحزم (Packet Loss).

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف الجهاز
نماذج الأكواد البرمجية:
@POST("network-devices/{id}/ping")
suspend fun pingNetworkDevice(@Path("id") id: Int): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/network-devices/1/ping" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/network-devices/{id}/ping`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/network-devices/{id}/ping');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/network-devices/{id}/ping', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "online",
  "latency_ms": 2.4,
  "packets_sent": 4,
  "packets_received": 4,
  "packet_loss_percentage": 0,
  "message": "الجهاز متصل ويعمل بشكل سليم"
}
HTTP 504 Error / Exception
{
  "message": "تعذر الوصول للجهاز؛ زمن الانتظار انتهى (Request Timeout)."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/network-devices/ping-all فحص اتصال كافة أجهزة الشبكة دفعة واحدة (Ping All) Bearer Token (auth.admin_app)

إطلاق ديمون فحص الاتصال التلقائي لكافة أجهزة الشبكة وتحديث لوحة المراقبة وإرسال تنبيهات بالأجهزة المتوقفة.

الصلاحية المطلوبة: مشرف مسجل (NOC) البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@POST("network-devices/ping-all")
suspend fun pingAllNetworkDevices(): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/network-devices/ping-all" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/network-devices/ping-all`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/network-devices/ping-all');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/network-devices/ping-all', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "total_devices_scanned": 48,
  "online_count": 47,
  "offline_count": 1,
  "offline_devices": [
    "AP-North-04"
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/network-devices/{id}/reboot إعادة تشغيل الجهاز عن بُعد (Remote Reboot) Bearer Token (auth.admin_app)

إرسال أمر إعادة تشغيل آمن للجهاز عبر بروتوكول SSH أو MikroTik API أو SNMP.

الصلاحية المطلوبة: مشرف عام (reboot devices) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف الجهاز
نماذج الأكواد البرمجية:
@POST("network-devices/{id}/reboot")
suspend fun rebootNetworkDevice(@Path("id") id: Int): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/network-devices/1/reboot" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/network-devices/{id}/reboot`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/network-devices/{id}/reboot');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/network-devices/{id}/reboot', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم إرسال أمر إعادة التشغيل بنجاح؛ جاري إعادة إقلاع الجهاز"
}
HTTP 500 Error / Exception
{
  "message": "فشل الاتصال بواجهة الجهاز لإرسال أمر إعادة التشغيل."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/network-devices/{id}/online-subscribers المشتركين المتصلين حالياً عبر الجهاز Bearer Token (auth.admin_app)

استعراض قائمة المشتركين المتصلين بالإنترنت حالياً والتي تمر حركة بياناتهم عبر هذا السويتش أو الراوتر المحدد.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف الجهاز
نماذج الأكواد البرمجية:
@GET("network-devices/{id}/online-subscribers")
suspend fun getDeviceOnlineSubscribers(@Path("id") id: Int): Response<Map<String, Any>>
curl -X GET "https://isp.freenet.net/api/manager/network-devices/1/online-subscribers" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/network-devices/{id}/online-subscribers`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/network-devices/{id}/online-subscribers');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/network-devices/{id}/online-subscribers', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "device_name": "Switch-Cisco-Tower1",
  "total_online": 64,
  "subscribers": [
    {
      "id": 105,
      "name": "محمود أحمد",
      "username": "mahmoud.ahmed",
      "ip": "10.100.4.55"
    }
  ]
}
HTTP 404 Error / Exception
{
  "message": "الجهاز غير موجود."
}
⚡ اختبار الطلب الحي (Interactive Tester)

👔 المشرفين والموزعين والعمليات المالية

AdminController & TransactionController
4 نقطة اتصال

إدارة شبكة المشرفين والموزعين، شحن وتعديل الأرصدة، تحويل الرصيد بين الحسابات، إدارة الحدود الائتمانية والديون، وتتبع السجل المالي للحركات المحاسبية.

GET /api/manager/admins قائمة المشرفين والموزعين Bearer Token (auth.admin_app)

استعراض المشرفين والموزعين التابعين للنظام مع أرصدتهم الحالية، ديونهم، وحدودهم الائتمانية، وحالة حساباتهم.

الصلاحية المطلوبة: مشرف مسجل (view admins) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
search query string اختياري البحث بالاسم أو اسم المستخدم أو الهاتف
نماذج الأكواد البرمجية:
@GET("admins")
suspend fun getAdmins(@Query("search") search: String? = null): Response<AdminListResponse>
curl -X GET "https://isp.freenet.net/api/manager/admins" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/admins`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/admins');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/admins', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "data": [
    {
      "id": 2,
      "name": "موزع سنترال المحطة",
      "username": "reseller_station",
      "phone": "01099887766",
      "balance": 4500.0,
      "credit_limit": 10000.0,
      "total_debt": 2500.0,
      "is_active": true,
      "subscribers_count": 184
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/admins/transaction التحويلات والعمليات المالية بين المشرفين (Admin Balance Transfer) Bearer Token (auth.admin_app)

شحن أو تحويل رصيد من حساب المدير العام إلى موزع أو بين المشرفين، مع تسجيل القيد المزدوج في دفتر اليومية داخل معاملة مالية آمنة ومقفلة (DB Transaction).

الصلاحية المطلوبة: مشرف عام (transfer balance) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
admin_id body integer إلزامي معرف المشرف أو الموزع المستهدف
amount body number إلزامي المبلغ المراد تحويله أو شحنه
type body string إلزامي نوع العملية: credit (إيداع/شحن) أو debit (خصم)
notes body string اختياري بيان الحركة المالية
جسم الطلب (Request Payload JSON)
{
  "admin_id": 2,
  "amount": 5000.0,
  "type": "credit",
  "notes": "شحن رصيد كروت واشتراكات سبتمبر"
}
نماذج الأكواد البرمجية:
@POST("admins/transaction")
suspend fun adminTransaction(@Body payload: Map<String, Any?>): Response<Unit>
curl -X POST "https://isp.freenet.net/api/manager/admins/transaction" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"admin_id": 2, "amount": 5000, "type": "credit"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/admins/transaction`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"admin_id": 2, "amount": 5000.0, "type": "credit", "notes": "شحن رصيد كروت واشتراكات سبتمبر"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/admins/transaction');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"admin_id": 2, "amount": 5000.0, "type": "credit", "notes": "شحن رصيد كروت واشتراكات سبتمبر"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/admins/transaction', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"admin_id": 2, "amount": 5000.0, "type": "credit", "notes": "شحن رصيد كروت واشتراكات سبتمبر"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تحويل المبلغ بنجاح وتحديث أرصدة المشرفين",
  "new_balance": 9500.0
}
HTTP 422 Error / Exception
{
  "message": "رصيد المحفظة المصدر غير كافٍ لإتمام التحويل."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/admins/pay-debts سداد ديون المشرف أو الموزع (Settle Admin Debts) Bearer Token (auth.admin_app)

تسجيل سداد نقدي لمديونيات المشرف وتصفية حسابه الآجل.

الصلاحية المطلوبة: مشرف عام البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
admin_id body integer إلزامي معرف المشرف
amount body number إلزامي المبلغ المسدد
جسم الطلب (Request Payload JSON)
{
  "admin_id": 2,
  "amount": 2500.0
}
نماذج الأكواد البرمجية:
@POST("admins/pay-debts")
suspend fun payDebts(@Body data: Map<String, Any>): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/admins/pay-debts" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"admin_id": 2, "amount": 2500}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/admins/pay-debts`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"admin_id": 2, "amount": 2500.0})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/admins/pay-debts');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"admin_id": 2, "amount": 2500.0}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/admins/pay-debts', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"admin_id": 2, "amount": 2500.0}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تسجيل سداد الدين بنجاح وتصفية المديونية",
  "remaining_debt": 0.0
}
HTTP 422 Error / Exception
{
  "message": "المبلغ المسدد أكبر من إجمالي المديونية المستحقة."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/transactions دفتر اليومية للحركات المالية (Financial Transactions Ledger) Bearer Token (auth.admin_app)

سجل الحركات المالية المكتملة في النظام: الشحن، التحويل، سداد الاشتراكات، والفواتير مع تاريخ ووقت كل حركة.

الصلاحية المطلوبة: مشرف مسجل (financial ledger) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
page query integer اختياري رقم الصفحة
نماذج الأكواد البرمجية:
@GET("transactions")
suspend fun getTransactions(): Response<Map<String, Any>>
curl -X GET "https://isp.freenet.net/api/manager/transactions" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/transactions`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/transactions');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/transactions', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "data": [
    {
      "id": 182,
      "type": "credit",
      "amount": 5000.0,
      "balance_before": 4500.0,
      "balance_after": 9500.0,
      "description": "شحن رصيد كروت واشتراكات سبتمبر",
      "created_at": "2026-09-09 14:38:00"
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)

📈 التقارير المالية والتشغيلية

ReportController
5 نقطة اتصال

تقارير الأعمال والتحليلات: ملخص الإيرادات والمبيعات، منحنيات المبيعات البيانية، نسب توزيع المشتركين على الباقات، كشف الحساب المالي المفصل للمدير الحالي، وقائمة أكثر المشتركين استهلاكاً للباندويث.

GET /api/manager/reports/sales-summary ملخص المبيعات والإيرادات (Sales Summary) Bearer Token (auth.admin_app)

إحصائيات الإيرادات والمتحصلات الإجمالية: مبيعات الاشتراكات، مبيعات الكروت، إجمالي الديون، وصافي الأرباح.

الصلاحية المطلوبة: مشرف مسجل (view reports) البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@GET("reports/sales-summary")
suspend fun getSalesSummary(): Response<Map<String, Any>>
curl -X GET "https://isp.freenet.net/api/manager/reports/sales-summary" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/reports/sales-summary`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/reports/sales-summary');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/reports/sales-summary', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "total_revenue": 142850.0,
  "subscriptions_revenue": 110050.0,
  "vouchers_revenue": 32800.0,
  "total_unpaid_debts": 8450.0,
  "net_cash": 134400.0
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/reports/sales-chart بيانات الرسم البياني للمبيعات (Sales Chart Data) Bearer Token (auth.admin_app)

نقاط الرسم البياني للمبيعات اليومية والشهرية لرسم منحنيات الإيرادات في تطبيق الهاتف ولوحة التحكم.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
period query string اختياري الفترة: week, month, year (الافتراضي: month)
نماذج الأكواد البرمجية:
@GET("reports/sales-chart")
suspend fun getSalesChart(@Query("period") period: String? = "month"): Response<Map<String, Any>>
curl -X GET "https://isp.freenet.net/api/manager/reports/sales-chart?period=month" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/reports/sales-chart`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/reports/sales-chart');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/reports/sales-chart', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "labels": [
    "2026-09-01",
    "2026-09-02",
    "2026-09-03",
    "2026-09-04",
    "2026-09-05"
  ],
  "data": [
    4500.0,
    6200.0,
    3800.0,
    8900.0,
    7100.0
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/reports/plans-distribution نسب توزيع المشتركين على الباقات (Plans Distribution) Bearer Token (auth.admin_app)

نسبة المشتركين في كل باقة لرسم المخطط الدائري (Pie Chart) وتحليل الإقبال على السرعات.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@GET("reports/plans-distribution")
suspend fun getPlansDistribution(): Response<Map<String, Any>>
curl -X GET "https://isp.freenet.net/api/manager/reports/plans-distribution" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/reports/plans-distribution`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/reports/plans-distribution');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/reports/plans-distribution', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
[
  {
    "plan_name": "باقة سوبر 30 ميجا",
    "subscribers_count": 820,
    "percentage": 57.7
  },
  {
    "plan_name": "باقة الترا 70 ميجا",
    "subscribers_count": 420,
    "percentage": 29.6
  },
  {
    "plan_name": "باقات أخرى",
    "subscribers_count": 180,
    "percentage": 12.7
  }
]
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/reports/my-financial التقرير المالي المفصل للمدير وصندوق النقدية Bearer Token (auth.admin_app)

كشف حساب مالي لحظي للمدير المسجل حالياً: المبالغ المحصلة كاش في عهدته، التحويلات الصادرة والواردة، وقيمة المبيعات المحققة.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
start_date query string اختياري من تاريخ
end_date query string اختياري إلى تاريخ
نماذج الأكواد البرمجية:
@GET("reports/my-financial")
suspend fun getMyFinancialReport(
    @Query("start_date") startDate: String? = null,
    @Query("end_date") endDate: String? = null
): Response<FinancialReportResponse>
curl -X GET "https://isp.freenet.net/api/manager/reports/my-financial" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/reports/my-financial`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/reports/my-financial');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/reports/my-financial', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "admin_id": 1,
  "admin_name": "المدير العام",
  "cash_in_hand": 12450.0,
  "wallet_balance": 15450.0,
  "renewals_collected": 11200.0,
  "vouchers_sold": 1250.0
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/reports/top-consumers أعلى المشتركين استهلاكاً للباندويث (Top Bandwidth Consumers) Bearer Token (auth.admin_app)

قائمة بأكثر المشتركين استهلاكاً للجيجابايت خلال الفترة المحددة لمراقبة الأحمال واكتشاف السلوكيات غير العادية.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
period query string اختياري الفترة: month أو week (الافتراضي: month)
limit query integer اختياري عدد المشتركين (الافتراضي: 10)
نماذج الأكواد البرمجية:
@GET("reports/top-consumers")
suspend fun getTopConsumers(
    @Query("period") period: String? = "month",
    @Query("limit") limit: Int? = 10
): Response<TopConsumersResponse>
curl -X GET "https://isp.freenet.net/api/manager/reports/top-consumers?limit=10" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/reports/top-consumers`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/reports/top-consumers');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/reports/top-consumers', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
[
  {
    "subscriber_id": 105,
    "name": "محمود أحمد حسن",
    "username": "mahmoud.ahmed",
    "total_usage_gb": 485.6,
    "plan_name": "باقة سوبر 30 ميجا"
  }
]
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)

💸 المصروفات وإدارة الخزينة

ExpenseController
2 نقطة اتصال

تسجيل ومتابعة مصروفات الشبكة والتشغيل: الصيانة، فواتير الكهرباء، إيجارات الأبراج، السيرفرات، ومشتريات الأجهزة وتصنيفها ضمن فئات المصروفات.

GET /api/manager/expenses قائمة المصروفات Bearer Token (auth.admin_app)

استعراض المصروفات المسجلة مع إمكانية التصفية بنطاق التاريخ وتصنيف المصروف.

الصلاحية المطلوبة: مشرف مسجل (manage expenses) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
page query integer اختياري رقم الصفحة
start_date query string اختياري تاريخ البداية
end_date query string اختياري تاريخ النهاية
نماذج الأكواد البرمجية:
@GET("expenses")
suspend fun getExpenses(
    @Query("page") page: Int = 1,
    @Query("start_date") startDate: String? = null,
    @Query("end_date") endDate: String? = null
): Response<ExpenseListResponse>
curl -X GET "https://isp.freenet.net/api/manager/expenses" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/expenses`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/expenses');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/expenses', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "current_page": 1,
  "total": 45,
  "data": [
    {
      "id": 1,
      "title": "إيجار برج التحرير - شهر سبتمبر",
      "amount": 2500.0,
      "date": "2026-09-01",
      "description": "سداد إيجار موقع البرج",
      "category": {
        "id": 2,
        "name": "إيجار أبراج ومواقع"
      }
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/expenses تسجيل مصروف جديد Bearer Token (auth.admin_app)

إضافة قيد مصروف مالي جديد وتحديد البند والمبلغ وخصمه من رصيد الخزينة.

الصلاحية المطلوبة: مشرف مسجل (create expenses) البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
title body string إلزامي عنوان المصروف
amount body number إلزامي المبلغ المنصرف
category_id body integer اختياري معرف فئة المصروف
date body string (YYYY-MM-DD) إلزامي تاريخ الصرف
description body string اختياري شرح وتفاصيل إضافية
جسم الطلب (Request Payload JSON)
{
  "title": "شراء بنشات وسويتش 8 بورت",
  "amount": 650.0,
  "date": "2026-09-09",
  "description": "توصيل كابينة شارع المحطة"
}
نماذج الأكواد البرمجية:
@POST("expenses")
suspend fun createExpense(@Body data: Map<String, Any?>): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/expenses" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"title": "شراء معدات", "amount": 650, "date": "2026-09-09"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/expenses`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"title": "شراء بنشات وسويتش 8 بورت", "amount": 650.0, "date": "2026-09-09", "description": "توصيل كابينة شارع المحطة"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/expenses');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"title": "شراء بنشات وسويتش 8 بورت", "amount": 650.0, "date": "2026-09-09", "description": "توصيل كابينة شارع المحطة"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/expenses', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"title": "شراء بنشات وسويتش 8 بورت", "amount": 650.0, "date": "2026-09-09", "description": "توصيل كابينة شارع المحطة"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 201 Success
{
  "status": "success",
  "message": "تم تسجيل المصروف بنجاح",
  "expense": {
    "id": 2,
    "title": "شراء بنشات وسويتش 8 بورت",
    "amount": 650.0
  }
}
HTTP 422 Error / Exception
{
  "message": "The given data was invalid."
}
⚡ اختبار الطلب الحي (Interactive Tester)

🎫 تذاكر الدعم الفني وشكاوى العملاء

SupportTicketController
3 نقطة اتصال

متابعة تذاكر الدعم الفني وبلاغات الأعطال الواردة من المشتركين عبر تطبيق الهاتف، والرد عليها، وتغيير حالات التذاكر، وإسنادها لفنيي الصيانة.

GET /api/manager/support/tickets قائمة تذاكر الدعم الفني Bearer Token (auth.admin_app)

استعراض تذاكر الدعم الفني مع الفلترة حسب الحالة (مفتوحة، قيد المعالجة، مغلقة) والبحث السريع.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
page query integer اختياري رقم الصفحة
status query string اختياري الحالة: open, in_progress, closed
search query string اختياري البحث برقم التذكرة أو اسم المشترك
نماذج الأكواد البرمجية:
@GET("support/tickets")
suspend fun getSupportTickets(
    @Query("page") page: Int = 1,
    @Query("status") status: String? = null
): Response<Map<String, Any>>
curl -X GET "https://isp.freenet.net/api/manager/support/tickets?status=open" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/support/tickets`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/support/tickets');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/support/tickets', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "current_page": 1,
  "data": [
    {
      "id": 88,
      "ticket_number": "TCK-88",
      "subscriber_name": "محمود أحمد حسن",
      "subject": "بطء تصفح في ساعات المساء",
      "priority": "medium",
      "status": "open",
      "created_at": "2026-09-09 11:20:00"
    }
  ]
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/support/tickets/{id}/reply الرد على تذكرة المشترك Bearer Token (auth.admin_app)

إرسال رد رسمي من الدعم الفني على التذكرة مع إشعار المشترك بالرد فورياً عبر تطبيق الهاتف والواتساب.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف التذكرة
message body string إلزامي نص الرد الفني
جسم الطلب (Request Payload JSON)
{
  "message": "أهلاً بك يا فندم، تم فحص البورت وإعادة ضبط إشارات الروتر ويرجى التجربة الآن."
}
نماذج الأكواد البرمجية:
@POST("support/tickets/{id}/reply")
suspend fun replySupportTicket(@Path("id") id: Int, @Body body: Map<String, String>): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/support/tickets/88/reply" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"message": "تم فحص المشكلة بنجاح"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/support/tickets/{id}/reply`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"message": "أهلاً بك يا فندم، تم فحص البورت وإعادة ضبط إشارات الروتر ويرجى التجربة الآن."})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/support/tickets/{id}/reply');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"message": "أهلاً بك يا فندم، تم فحص البورت وإعادة ضبط إشارات الروتر ويرجى التجربة الآن."}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/support/tickets/{id}/reply', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"message": "أهلاً بك يا فندم، تم فحص البورت وإعادة ضبط إشارات الروتر ويرجى التجربة الآن."}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم إرسال الرد وإشعار المشترك بنجاح"
}
HTTP 422 Error / Exception
{
  "message": "نص الرد message مطلوب."
}
⚡ اختبار الطلب الحي (Interactive Tester)
PATCH /api/manager/support/tickets/{id}/status تحديث حالة التذكرة (إغلاق / قيد المعالجة) Bearer Token (auth.admin_app)

تحديث حالة التذكرة ليصبح closed أو in_progress.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
id path integer إلزامي معرف التذكرة
status body string إلزامي الحالة الجديدة: open, in_progress, closed
جسم الطلب (Request Payload JSON)
{
  "status": "closed"
}
نماذج الأكواد البرمجية:
@PATCH("support/tickets/{id}/status")
suspend fun updateSupportTicketStatus(@Path("id") id: Int, @Body body: Map<String, String>): Response<Map<String, Any>>
curl -X PATCH "https://isp.freenet.net/api/manager/support/tickets/88/status" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"status": "closed"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/support/tickets/{id}/status`, {
    method: 'PATCH',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"status": "closed"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/support/tickets/{id}/status');
  final response = await http.patch(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"status": "closed"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('PATCH', 'api/manager/support/tickets/{id}/status', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"status": "closed"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تحديث حالة التذكرة بنجاح"
}
HTTP 404 Error / Exception
{
  "message": "التذكرة غير موجودة."
}
⚡ اختبار الطلب الحي (Interactive Tester)

⚙️ الإعدادات العامة والأدوار والباقات

SettingsController, RoleController & PlanController
4 نقطة اتصال

استعراض وتحديث إعدادات النظام المخصصة للمدير، استعراض قائمة الأدوار والصلاحيات (Spatie Permissions)، وقائمة باقات الخدمة العامة النشطة.

GET /api/manager/settings إعدادات وتفضيلات المدير Bearer Token (auth.admin_app)

استرجاع الإعدادات المخصصة للمدير: اسم الشركة، بادئة الفواتير، العملة الافتراضية، وخيارات التنبيهات.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@GET("settings")
suspend fun getSettings(): Response<Map<String, Any>>
curl -X GET "https://isp.freenet.net/api/manager/settings" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/settings`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/settings');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/settings', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "company_name": "شبكة فري نت لخدمات الإنترنت",
  "currency_symbol": "EGP",
  "admin_prefix": "adm",
  "invoice_footer": "شكراً لاختياركم فري نت",
  "enable_whatsapp_alerts": true
}
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
POST /api/manager/settings تحديث إعدادات وتفضيلات المدير Bearer Token (auth.admin_app)

تحديث وحفظ الإعدادات المخصصة للمشرف في النظام.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
المدخلات والباراميترز (Parameters)
اسم الحقل الموضع النوع الحالة الوصف
company_name body string اختياري اسم الشبكة أو الشركة
invoice_footer body string اختياري رسالة ذيل الفاتورة
جسم الطلب (Request Payload JSON)
{
  "company_name": "شبكة فري نت - المنصورة",
  "invoice_footer": "خدمة العملاء: 01000000000"
}
نماذج الأكواد البرمجية:
@POST("settings")
suspend fun updateSettings(@Body data: Map<String, Any>): Response<Map<String, Any>>
curl -X POST "https://isp.freenet.net/api/manager/settings" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"company_name": "فري نت"}'
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/settings`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({"company_name": "شبكة فري نت - المنصورة", "invoice_footer": "خدمة العملاء: 01000000000"})
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/settings');
  final response = await http.post(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: jsonEncode({"company_name": "شبكة فري نت - المنصورة", "invoice_footer": "خدمة العملاء: 01000000000"}),
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('POST', 'api/manager/settings', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ],
    'json' => {"company_name": "شبكة فري نت - المنصورة", "invoice_footer": "خدمة العملاء: 01000000000"}
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
{
  "status": "success",
  "message": "تم تحديث الإعدادات بنجاح"
}
HTTP 422 Error / Exception
{
  "message": "The given data was invalid."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/roles قائمة الأدوار والصلاحيات (System Roles & Permissions) Bearer Token (auth.admin_app)

استعراض قائمة الأدوار الوظيفية (Super Admin, Reseller, Technician) والصلاحيات المعرفة في النظام لاستخدامها في نماذج إنشاء وتعديل المشرفين.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@GET("roles")
suspend fun getRoles(): Response<List<Map<String, Any>>>
curl -X GET "https://isp.freenet.net/api/manager/roles" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/roles`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/roles');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/roles', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
[
  {
    "id": 1,
    "name": "Super Admin",
    "display_name": "المدير العام",
    "permissions_count": 48
  },
  {
    "id": 2,
    "name": "Reseller",
    "display_name": "موزع كروت واشتراكات",
    "permissions_count": 14
  }
]
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
GET /api/manager/plans قائمة باقات الخدمة العامة (Service Plans) Bearer Token (auth.admin_app)

استعراض جميع باقات الإنترنت النشطة مع السرعات (الرفع والتحميل بالكيلوبت)، سعة الكوتا بالجيجابايت، الأسعار، وفترات الصلاحية.

الصلاحية المطلوبة: مشرف مسجل البروتوكول: HTTPS REST JSON
نماذج الأكواد البرمجية:
@GET("plans")
suspend fun getPlans(): Response<List<ServicePlan>>
curl -X GET "https://isp.freenet.net/api/manager/plans" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" 
// JavaScript (Fetch)
async function request() {
  const token = localStorage.getItem('freenet_token') || 'YOUR_TOKEN_HERE';
  const baseUrl = localStorage.getItem('freenet_base_url') || 'https://isp.freenet.net/api/manager';
  
  const response = await fetch(`${baseUrl}/api/manager/plans`, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json'
    }
  });
  
  const data = await response.json();
  console.log(data);
}
// Dart (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert';

Future<void> sendRequest() async {
  final url = Uri.parse('$baseUrl/api/manager/plans');
  final response = await http.get(
    url,
    headers: {
      'Authorization': 'Bearer $token',
      'Accept': 'application/json'
    },
  );
  
  print('Status: ${response.statusCode}');
  print('Body: ${response.body}');
}
// PHP (GuzzleHttp)
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://isp.freenet.net/api/manager/']);
$response = $client->request('GET', 'api/manager/plans', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'application/json',
    ]
]);

$data = json_decode($response->getBody(), true);
نماذج الاستجابة المرجعة (Responses):
HTTP 200 Success
[
  {
    "id": 1,
    "name": "باقة سوبر 30 ميجا",
    "price": 180.0,
    "download_speed": 30720,
    "upload_speed": 5120,
    "quota_gb": 250,
    "duration_days": 30,
    "type": "limited"
  },
  {
    "id": 2,
    "name": "باقة الترا 70 ميجا",
    "price": 320.0,
    "download_speed": 71680,
    "upload_speed": 10240,
    "quota_gb": 400,
    "duration_days": 30,
    "type": "limited"
  }
]
HTTP 401 Error / Exception
{
  "message": "Unauthenticated."
}
⚡ اختبار الطلب الحي (Interactive Tester)
تم النسخ بنجاح!