2026-03-13-1

This commit is contained in:
Hamza-Ayed
2026-03-13 18:27:21 +03:00
parent 9de4cb0a84
commit fdfea5582a
11 changed files with 338 additions and 497 deletions

View File

@@ -47,8 +47,8 @@ android {
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdkVersion = 24
targetSdk = 36
versionCode = 59
versionName = '1.1.59'
versionCode = 60
versionName = '1.1.60'
multiDexEnabled = true
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"

View File

@@ -1,24 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
</dict>
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>16.1</string>
</dict>
</plist>

View File

@@ -1,107 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSSupportsLiveActivities</key>
<true />
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>intaleqapp.com</string>
<key>CFBundleURLSchemes</key>
<array>
<string>
com.googleusercontent.apps.1086900987150-9jv4oa8l3t23d54lrf27c1d22tbt9i6d</string>
<string>intaleq</string>
</array>
</dict>
</array>
<key>FlutterDeepLinkingEnabled</key>
<true />
<key>CADisableMinimumFrameDurationOnPhone</key>
<true />
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Intaleq</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Intaleq</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>31</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.1.31</string>
<key>FirebaseAppDelegateProxyEnabled</key>
<string>NO</string>
<key>GMSApiKey</key>
<string>YOUR_API_KEY</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>googlechromes</string>
<string>comgooglemaps</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true />
<key>NSCameraUsageDescription</key>
<string>This app requires access to your camera in order to scan QR codes and capture images
for uploading and access to connect to a call.</string>
<key>NSContactsUsageDescription</key>
<string>This app requires contacts access to function properly.</string>
<key>NSFaceIDUsageDescription</key>
<string>Use Face ID to securely authenticate payment accounts.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs access to your location to provide you with the best ride experience.
Your location data will be used to find the nearest available cars and connect you with
the closest captain for efficient and convenient rides.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs access to location.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to your location to provide you with the best ride experience.
Your location data will be used to find the nearest available cars and connect you with
the closest captain for efficient and convenient rides.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app requires access to your microphone to record audio, allowing you to add
voice recordings to your photos and videos and access to connect to a call.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires access to the photo library to upload pictures.</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true />
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>location</string>
<string>remote-notification</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false />
</dict>
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true />
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Intaleq</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Intaleq</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>32</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>intaleqapp.com</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.googleusercontent.apps.1086900987150-9jv4oa8l3t23d54lrf27c1d22tbt9i6d</string>
<string>intaleq</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.1.32</string>
<key>FirebaseAppDelegateProxyEnabled</key>
<string>NO</string>
<key>FlutterDeepLinkingEnabled</key>
<true />
<key>GMSApiKey</key>
<string>YOUR_API_KEY</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>googlechromes</string>
<string>comgooglemaps</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true />
<key>NSCameraUsageDescription</key>
<string>This app requires access to your camera in order to scan QR codes and capture images
for uploading and access to connect to a call.</string>
<key>NSContactsUsageDescription</key>
<string>This app requires contacts access to function properly.</string>
<key>NSFaceIDUsageDescription</key>
<string>Use Face ID to securely authenticate payment accounts.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs access to your location to provide you with the best ride experience.
Your location data will be used to find the nearest available cars and connect you with
the closest captain for efficient and convenient rides.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs access to location.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to your location to provide you with the best ride experience.
Your location data will be used to find the nearest available cars and connect you with
the closest captain for efficient and convenient rides.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app requires access to your microphone to record audio, allowing you to add
voice recordings to your photos and videos and access to connect to a call.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires access to the photo library to upload pictures.</string>
<key>NSSupportsLiveActivities</key>
<true />
<key>UIApplicationSupportsIndirectInputEvents</key>
<true />
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>location</string>
<string>remote-notification</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false />
</dict>
</plist>

View File

@@ -10,6 +10,7 @@ class BoxName {
static const String gender = "gender";
static const String jwt = "jwt";
static const String lowEndMode = "lowEndMode";
static const String deviceFpEncrypted = "deviceFpEncrypted";
static const String appVersionChecked = "appVersionChecked";
static const String lastName = "lastName";
static const String fingerPrint = "fingerPrint";

View File

@@ -3,10 +3,10 @@ import 'package:Intaleq/main.dart';
class AppLink {
///https://walletintaleq.intaleq.xyz/v1/main
static String paymentServer = 'https://walletintaleq.intaleq.xyz/v1/main';
static String paymentServer = 'https://walletintaleq.intaleq.xyz/v2/main';
///https://api.intaleq.xyz/intaleq/ride/location
static String location = 'https://api.intaleq.xyz/intaleq/ride/location';
static String location = 'https://api.intaleq.xyz/intaleq_v1/ride/location';
/// هذا الرابط خاص برحلات الركاب، ويستخدمه السيرفر الجانبي للرحلات (Ride Server Side) للتعامل مع عمليات إلغاء الرحلات وتحديث حالة الرحلات وغيرها من العمليات المتعلقة بالرحلات.
/// https://routesy.intaleq.xyz for syria
@@ -19,7 +19,7 @@ class AppLink {
'https://location.intaleq.xyz/intaleq/ride/location';
///https://api.intaleq.xyz/intaleq
static final String endPoint = 'https://api.intaleq.xyz/intaleq';
static final String endPoint = 'https://api.intaleq.xyz/intaleq_v1';
/// هذا الرابط خاص برحلات الركاب، ويستخدمه السيرفر الجانبي للرحلات (Ride Server Side) للتعامل مع عمليات إلغاء الرحلات وتحديث حالة الرحلات وغيرها من العمليات المتعلقة بالرحلات.
/// https://rides.intaleq.xyz/intaleq
@@ -27,7 +27,7 @@ class AppLink {
///https://api.intaleq.xyz/intaleq
/// main api link for all api calls except rides and location
static final String server = 'https://api.intaleq.xyz/intaleq';
static final String server = 'https://api.intaleq.xyz/intaleq_v1';
///https://rides.intaleq.xyz
/// هذا الرابط خاص برحلات الركاب، ويستخدمه السيرفر الجانبي للرحلات (Ride Server Side) للتعامل مع عمليات إلغاء الرحلات وتحديث حالة الرحلات وغيرها من العمليات المتعلقة بالرحلات.

View File

@@ -87,54 +87,6 @@ class LoginController extends GetxController {
update();
}
getJwtWallet() async {
try {
if (box.read(BoxName.security_check).toString() != 'passed') {
Log.print('Security check failed');
return;
}
Log.print('Security check passed');
String fingerPrint = await DeviceHelper.getDeviceFingerprint();
final dev = GetPlatform.isAndroid ? 'android' : 'ios';
var payload = {
'id': box.read(BoxName.passengerID),
'password': AK.passnpassenger,
'aud': '${AK.allowed}$dev',
'fingerPrint': fingerPrint,
};
var response = await http.post(
Uri.parse(AppLink.loginJwtWalletRider),
body: payload,
);
// Handle bad responses
if (response.statusCode != 200) {
_showJwtErrorDialog(
"حدث خطأ أثناء الاتصال بالخادم. يرجى المحاولة مرة أخرى.");
throw Exception("JWT request failed");
}
var data = jsonDecode(response.body);
// Validate JWT response structure
if (!data.containsKey('jwt') || !data.containsKey('hmac')) {
_showJwtErrorDialog("تعذّر التحقق من الأمان. يرجى إعادة المحاولة.");
throw Exception("Invalid JWT response format");
}
// Save HMAC locally
await box.write(BoxName.hmac, data['hmac']);
return data['jwt'].toString();
} catch (e) {
_showJwtErrorDialog("حدث خلل غير متوقع. يرجى المحاولة مرة أخرى.");
rethrow;
}
}
void _showJwtErrorDialog(String message) {
if (Get.context == null) return;
@@ -149,72 +101,128 @@ class LoginController extends GetxController {
},
);
}
// ═══════════════════════════════════════════════════════════════
// LoginController — دوال إدارة الـ JWT
// ───────────────────────────────────────────────────────────────
// لا تغيير على اسم الكلاس أو أسماء الدوال
// ═══════════════════════════════════════════════════════════════
getJWT() async {
// print(Pasenger.pasengerpas);
// await SecurityHelper.performSecurityChecks();
Log.print('firstTimeLoadKey: ${box.read(BoxName.firstTimeLoadKey)}');
// داخل class LoginController
// ─────────────────────────────────────────────────────────────
// getJWT: الحصول على توكن للراكب
// ─────────────────────────────────────────────────────────────
// المنطق:
// • firstTimeLoadKey != false ← أول مرة يفتح التطبيق → loginFirstTime
// • firstTimeLoadKey == false ← مستخدم موجود → loginJwtRider
// ─────────────────────────────────────────────────────────────
Future<void> getJWT() async {
dev = Platform.isAndroid ? 'android' : 'ios';
// تأكد إن البصمة محدّثة قبل أي طلب
await DeviceHelper.getDeviceFingerprint();
final String fp = box.read(BoxName.deviceFpEncrypted) ?? '';
if (box.read(BoxName.firstTimeLoadKey).toString() != 'false') {
// ── أول تسجيل ─────────────────────────────────────────
// نرسل البصمة المشفرة مع باقي البيانات
// السيرفر سيعمل hash لها ويخزنها في JWT payload
var payload = {
'id': box.read(BoxName.passengerID) ?? AK.newId,
'password': AK.passnpassenger,
'aud': '${AK.allowed}$dev',
'fingerPrint': fp,
};
// Log.print('payload: ${payload}');
var response0 = await http.post(
var response = await http.post(
Uri.parse(AppLink.loginFirstTime),
body: payload,
);
if (response0.statusCode == 200) {
final decodedResponse1 = jsonDecode(response0.body);
Log.print('AppLink.loginFirstTime: ${AppLink.loginFirstTime}');
final jwt = decodedResponse1['jwt'];
final refreshToken = decodedResponse1['refresh_token'];
Log.print('payload: ${payload}');
Log.print('response: ${response}');
if (response.statusCode == 200) {
final decoded = jsonDecode(response.body);
final String jwt = decoded['jwt'];
// نشفر الـ JWT بالتشفير الثلاثي قبل التخزين في GetStorage
box.write(BoxName.jwt, c(jwt));
// Sss.write(BoxName.jwt, jwt);
await storage.write(key: BoxName.refreshToken, value: refreshToken);
// await AppInitializer().getAIKey(Pasenger.keyOfApp);
// await AppInitializer().getAIKey(Pasenger.initializationVector);
// await Future.delayed(Duration.zero);
await EncryptionHelper.initialize();
await AppInitializer().getKey();
} else {}
}
} else {
// ── مستخدم موجود: تجديد التوكن ────────────────────────
await EncryptionHelper.initialize();
var payload = {
'id': box.read(BoxName.passengerID),
'password': box.read(BoxName.email),
'fingerPrint': fp,
'aud': '${AK.allowed}$dev',
};
// Log.print('payload: ${payload}');
var response1 = await http.post(
var response = await http.post(
Uri.parse(AppLink.loginJwtRider),
body: payload,
);
Log.print('req: ${response1.request}');
Log.print('response: ${response1.body}');
Log.print('AppLink.loginJwtRider: ${AppLink.loginJwtRider}');
Log.print('payload: ${payload}');
// Log.print('decodedResponse1: ${jsonDecode(response1.body)}');
Log.print('response: ${response.body}');
if (response.statusCode == 200) {
final decoded = jsonDecode(response.body);
final String jwt = decoded['jwt'];
if (response1.statusCode == 200) {
final decodedResponse1 = jsonDecode(response1.body);
// Log.print('decodedResponse1: ${decodedResponse1}');
final jwt = decodedResponse1['jwt'];
await box.write(BoxName.jwt, c(jwt));
await AppInitializer().getKey();
// final refreshToken = decodedResponse1['refresh_token'];
// await storage.write(key: BoxName.refreshToken, value: refreshToken);
box.write(BoxName.jwt, c(jwt));
}
}
}
// ─────────────────────────────────────────────────────────────
// getJwtWallet: الحصول على توكن لسيرفر المدفوعات
// ─────────────────────────────────────────────────────────────
// الفرق عن getJWT:
// • يستخدم endpoint مختلف (loginWallet)
// • يرجع hmac مع الـ jwt ويخزنه في GetStorage
// • الـ JWT لا يُشفَّر ثلاثياً (يُستخدم مباشرة في الـ header)
// ─────────────────────────────────────────────────────────────
Future<String?> getJwtWallet() async {
dev = Platform.isAndroid ? 'android' : 'ios';
// await DeviceHelper.initAndStore();
final String fp = box.read(BoxName.deviceFpEncrypted) ?? '';
var payload = {
'id': box.read(BoxName.passengerID),
'password': AK.passnpassenger,
'aud': '${AK.allowed}$dev',
'fingerPrint': fp,
};
var response = await http.post(
Uri.parse(AppLink.loginJwtWalletRider),
body: payload,
);
Log.print('AppLink.loginJwtWalletRider: ${AppLink.loginJwtWalletRider}');
// Log.print('payload: ${payload}');
Log.print('response wallet: ${response.body}');
if (response.statusCode == 200) {
final decoded = jsonDecode(response.body);
final String jwt = decoded['jwt'];
final String hmac = decoded['hmac'];
// نخزن الـ hmac للاستخدام في X-HMAC-Auth header
box.write(BoxName.hmac, hmac);
// wallet JWT يُرجَع مباشرة دون تشفير ثلاثي
return jwt;
}
return null;
}
Future<void> loginUsingCredentials(String passengerID, String email) async {
isloading = true;
update();

View File

@@ -297,20 +297,6 @@ class RegisterController extends GetxController {
);
if (res1 != 'failure') {
// if (AppLink.IntaleqAlexandriaServer != AppLink.IntaleqSyriaServer) {
// List<Future> signUp = [
// CRUD().post(
// link: '${AppLink.IntaleqAlexandriaServer}/auth/signup.php',
// payload: payload,
// ),
// CRUD().post(
// link: '${AppLink.IntaleqGizaServer}/auth/signup.php',
// payload: payload,
// )
// ];
// await Future.wait(signUp);
// }
box.write(BoxName.isVerified, '1');
box.write(BoxName.isFirstTime, '0');
box.write(BoxName.phone, (phoneController.text));

View File

@@ -69,7 +69,7 @@ class FirebaseMessagesController extends GetxController {
Future getToken() async {
fcmToken.getToken().then((token) {
Log.print('fcmToken: ${token}');
// Log.print('fcmToken: ${token}');
box.write(BoxName.tokenFCM, (token.toString()));
});
// 🔹 الاشتراك في topic

View File

@@ -68,6 +68,15 @@ class CRUD {
} catch (e) {}
}
// ─────────────────────────────────────────────────────────────
// دالة مساعدة خاصة: يجيب البصمة المشفرة من GetStorage
// هي نفس القيمة المرسلة في login وعُملها hash في JWT payload
// السيرفر يعمل: sha256(X-Device-FP + FP_PEPPER) == JWT.fingerPrint
// ─────────────────────────────────────────────────────────────
String _getFpHeader() {
return box.read(BoxName.deviceFpEncrypted)?.toString() ?? '';
}
/// Centralized private method to handle all API requests.
/// Includes retry logic, network checking, and standardized error handling.
Future<dynamic> _makeRequest({
@@ -75,13 +84,11 @@ class CRUD {
Map<String, dynamic>? payload,
required Map<String, String> headers,
}) async {
// timeouts أقصر
const connectTimeout = Duration(seconds: 6);
const receiveTimeout = Duration(seconds: 10);
Future<http.Response> doPost() {
final url = Uri.parse(link);
// استخدم _client بدل http.post
return _client
.post(url, body: payload, headers: headers)
.timeout(connectTimeout + receiveTimeout);
@@ -93,7 +100,6 @@ class CRUD {
try {
response = await doPost();
} on SocketException catch (_) {
// محاولة ثانية واحدة فقط
response = await doPost();
} on TimeoutException catch (_) {
response = await doPost();
@@ -102,36 +108,34 @@ class CRUD {
final sc = response.statusCode;
final body = response.body;
Log.print('request: ${response.request}');
Log.print('body: ${body}');
Log.print('body: $body');
// 2xx
if (sc >= 200 && sc < 300) {
try {
final jsonData = jsonDecode(body);
return jsonData; // لا تعيد 'success' فقط؛ أعِد الجسم كله
return jsonData;
} catch (e, st) {
// لا تسجّل كخطأ شبكي لكل حالة؛ فقط معلومات
addError('JSON Decode Error', 'Body: $body\n$st',
'CRUD._makeRequest $link');
return 'failure';
}
}
// 401 → دع الطبقة العليا تتعامل مع التجديد
// 401 → تجديد التوكن تلقائياً
if (sc == 401) {
await Get.put(LoginController()).getJWT();
// لا تستدع getJWT هنا كي لا نضاعف الرحلات
return 'token_expired';
}
// 5xx: لا تعِد المحاولة هنا (حاولنا مرة ثانية فوق)
// 5xx
if (sc >= 500) {
addError(
'Server 5xx', 'SC: $sc\nBody: $body', 'CRUD._makeRequest $link');
return 'failure';
}
// 4xx أخرى: أعد الخطأ بدون تسجيل مكرر
// 4xx أخرى
return 'failure';
} on SocketException {
_netGuard.notifyOnce((title, msg) => mySnackeBarError(msg));
@@ -145,21 +149,23 @@ class CRUD {
}
}
/// Performs a standard authenticated POST request.
/// Automatically handles token renewal.
// ═══════════════════════════════════════════════════════════════
// post — طلب POST عادي للراكب/السائق
// ───────────────────────────────────────────────────────────────
// التغيير: إضافة X-Device-FP header
// القيمة: fp_encrypted من GetStorage
// السيرفر يتحقق: sha256(fp_encrypted + FP_PEPPER) == JWT.fingerPrint
// ═══════════════════════════════════════════════════════════════
Future<dynamic> post({
required String link,
Map<String, dynamic>? payload,
}) async {
String token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
// if (JwtDecoder.isExpired(token)) {
// await Get.put(LoginController()).getJWT();
// token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
// }
final headers = {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization': 'Bearer $token'
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer $token',
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
};
return await _makeRequest(
@@ -169,58 +175,58 @@ class CRUD {
);
}
/// Performs a standard authenticated GET request (using POST method as per original code).
/// Automatically handles token renewal.
// ═══════════════════════════════════════════════════════════════
// get — طلب GET للراكب/السائق (يستخدم POST method)
// ───────────────────────────────────────────────────────────────
// التغيير: إضافة X-Device-FP header
// ═══════════════════════════════════════════════════════════════
Future<dynamic> get({
required String link,
Map<String, dynamic>? payload,
}) async {
var url = Uri.parse(
link,
);
var url = Uri.parse(link);
var response = await http.post(
url,
body: payload,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization':
'Bearer ${r(box.read(BoxName.jwt)).toString().split(Env.addd)[0]}'
'Bearer ${r(box.read(BoxName.jwt)).toString().split(Env.addd)[0]}',
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
},
);
Log.print('request: ${response.request}');
Log.print('body: ${response.body}');
Log.print('payload: ${payload}');
Log.print('payload: $payload');
if (response.statusCode == 200) {
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'success') {
return response.body;
}
return jsonData['status'];
} else if (response.statusCode == 401) {
// Specifically handle 401 Unauthorized
var jsonData = jsonDecode(response.body);
if (jsonData['error'] == 'Token expired') {
// Show snackbar prompting to re-login
await Get.put(LoginController()).getJWT();
// mySnackbarSuccess('please order now'.tr);
return 'token_expired'; // Return a specific value for token expiration
return 'token_expired';
} else {
// Other 401 errors
// addError('Unauthorized: ${jsonData['error']}', 'crud().post - 401',
// url.toString());
return 'failure';
}
} else {
addError('Non-200 response code: ${response.statusCode}',
'crud().post - Other', url.toString());
'crud().get - Other', url.toString());
return 'failure';
}
}
/// Performs an authenticated POST request to wallet endpoints.
// ═══════════════════════════════════════════════════════════════
// postWallet — طلب POST لسيرفر المدفوعات
// ───────────────────────────────────────────────────────────────
// التغيير: إضافة X-Device-FP header
// 3 headers معاً: JWT + HMAC + FP
// ═══════════════════════════════════════════════════════════════
Future<dynamic> postWallet({
required String link,
Map<String, dynamic>? payload,
@@ -229,9 +235,10 @@ class CRUD {
final hmac = box.read(BoxName.hmac);
final headers = {
"Content-Type": "application/x-www-form-urlencoded",
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer $jwt',
'X-HMAC-Auth': hmac.toString(),
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
};
return await _makeRequest(
@@ -241,63 +248,61 @@ class CRUD {
);
}
/// Performs an authenticated GET request to wallet endpoints (using POST).
// ═══════════════════════════════════════════════════════════════
// getWallet — طلب GET لسيرفر المدفوعات (يستخدم POST method)
// ───────────────────────────────────────────────────────────────
// التغيير: إضافة X-Device-FP header
// ═══════════════════════════════════════════════════════════════
Future<dynamic> getWallet({
required String link,
Map<String, dynamic>? payload,
}) async {
var s = await LoginController().getJwtWallet();
final hmac = box.read(BoxName.hmac);
var url = Uri.parse(
link,
);
var url = Uri.parse(link);
var response = await http.post(
url,
body: payload,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer $s',
'X-HMAC-Auth': hmac.toString(),
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
},
);
if (response.statusCode == 200) {
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'success') {
return response.body;
}
return jsonData['status'];
} else if (response.statusCode == 401) {
// Specifically handle 401 Unauthorized
var jsonData = jsonDecode(response.body);
if (jsonData['error'] == 'Token expired') {
// Show snackbar prompting to re-login
await Get.put(LoginController()).getJwtWallet();
return 'token_expired'; // Return a specific value for token expiration
return 'token_expired';
} else {
// Other 401 errors
addError('Unauthorized: ${jsonData['error']}', 'crud().post - 401',
addError('Unauthorized: ${jsonData['error']}', 'crud().getWallet - 401',
url.toString());
return 'failure';
}
} else {
addError('Non-200 response code: ${response.statusCode}',
'crud().post - Other', url.toString());
'crud().getWallet - Other', url.toString());
return 'failure';
}
}
// =======================================================================
// All other specialized methods remain below.
// They are kept separate because they interact with external third-party APIs
// and have unique authentication, body structures, or error handling logic
// that doesn't fit the standardized `_makeRequest` helper.
// All other specialized methods remain below unchanged.
// They interact with external third-party APIs and have unique
// authentication or body structures that don't need the FP header.
// =======================================================================
Future<dynamic> postWalletMtn(
{required String link, Map<String, dynamic>? payload}) async {
// This method has a very custom response-wrapping logic, so it's kept separate.
final s = await LoginController().getJwtWallet();
final hmac = box.read(BoxName.hmac);
final url = Uri.parse(link);
@@ -307,9 +312,10 @@ class CRUD {
url,
body: payload,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Bearer $s",
"X-HMAC-Auth": hmac.toString(),
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer $s',
'X-HMAC-Auth': hmac.toString(),
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
},
);
@@ -360,30 +366,6 @@ class CRUD {
}
}
// Future<dynamic> getTokenParent({
// required String link,
// Map<String, dynamic>? payload,
// }) async {
// // Uses Basic Auth, so it's a separate implementation.
// var url = Uri.parse(
// link,
// );
// var response = await http.post(
// url,
// body: payload,
// headers: {
// "Content-Type": "application/x-www-form-urlencoded",
// 'Authorization':
// 'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}',
// },
// );
// if (response.statusCode == 200) {
// return jsonDecode(response.body);
// }
// // Consider adding error handling here.
// return null;
// }
Future sendWhatsAppAuth(String to, String token) async {
var res = await CRUD()
.get(link: AppLink.getApiKey, payload: {'keyName': 'whatsapp_key'});
@@ -407,10 +389,7 @@ class CRUD {
{
"type": "body",
"parameters": [
{
"type": "text",
"text": token,
}
{"type": "text", "text": token}
]
}
]
@@ -422,18 +401,14 @@ class CRUD {
try {
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
String responseBody = await response.stream.bytesToString();
Get.defaultDialog(
title: 'You will receive a code in WhatsApp Messenger'.tr,
middleText: 'wait 1 minute to recive message'.tr,
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () {
Get.back();
},
onPressed: () => Get.back(),
),
);
} else {
@@ -447,15 +422,16 @@ class CRUD {
required String uid,
}) async {
var uid = box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver);
var res = await http.get(Uri.parse(
// 'https://repulsive-pig-rugby-shirt.cyclic.app/token?channelName=$channelName'),
'https://orca-app-b2i85.ondigitalocean.app/token?channelName=$channelName'),
headers: {'Authorization': 'Bearer ${AK.agoraAppCertificate}'});
var res = await http.get(
Uri.parse(
'https://orca-app-b2i85.ondigitalocean.app/token?channelName=$channelName'),
headers: {'Authorization': 'Bearer ${AK.agoraAppCertificate}'},
);
if (res.statusCode == 200) {
var response = jsonDecode(res.body);
return response['token'];
} else {}
}
}
Future<dynamic> getLlama({
@@ -463,18 +439,14 @@ class CRUD {
required String payload,
required String prompt,
}) async {
var url = Uri.parse(
link,
);
var url = Uri.parse(link);
var headers = {
'Content-Type': 'application/json',
'Authorization':
'Bearer LL-X5lJ0Px9CzKK0HTuVZ3u2u4v3tGWkImLTG7okGRk4t25zrsLqJ0qNoUzZ2x4ciPy'
// 'Authorization': 'Bearer ${Env.llamaKey}'
};
var data = json.encode({
"model": "Llama-3-70b-Inst-FW",
// "model": "llama-13b-chat",
"messages": [
{
"role": "user",
@@ -484,15 +456,8 @@ class CRUD {
],
"temperature": 0.9
});
var response = await http.post(
url,
body: data,
headers: headers,
);
if (response.statusCode == 200) {
return response.body;
}
var response = await http.post(url, body: data, headers: headers);
if (response.statusCode == 200) return response.body;
return response.statusCode;
}
@@ -501,7 +466,6 @@ class CRUD {
Future.delayed(const Duration(seconds: 2));
String extracted =
await arabicTextExtractByVisionAndAI(imagePath: imagePath);
// await AI().geminiAiExtraction(prompt, extracted);
}
Future<dynamic> arabicTextExtractByVisionAndAI({
@@ -512,17 +476,13 @@ class CRUD {
'Ocp-Apim-Subscription-Key': '21010e54b50f41a4904708c526e102df'
};
var url = Uri.parse(
'https://ocrhamza.cognitiveservices.azure.com/vision/v2.1/ocr?language=ar',
);
'https://ocrhamza.cognitiveservices.azure.com/vision/v2.1/ocr?language=ar');
String imagePathFull =
'${AppLink.server}card_image/$imagePath-${box.read(BoxName.driverID) ?? box.read(BoxName.passengerID)}.jpg';
var requestBody = {"url": imagePathFull};
var response = await http.post(
url,
body: jsonEncode(requestBody), // Encode the JSON object to a string
headers: headers,
);
var response =
await http.post(url, body: jsonEncode(requestBody), headers: headers);
if (response.statusCode == 200) {
var responseBody = jsonDecode(response.body);
@@ -535,9 +495,7 @@ class CRUD {
required String link,
required String payload,
}) async {
var url = Uri.parse(
link,
);
var url = Uri.parse(link);
var headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer ${Env.chatGPTkeySeferNew}'
@@ -553,15 +511,8 @@ class CRUD {
],
"temperature": 0.9
});
var response = await http.post(
url,
body: data,
headers: headers,
);
if (response.statusCode == 200) {
return response.body;
}
var response = await http.post(url, body: data, headers: headers);
if (response.statusCode == 200) return response.body;
return response.statusCode;
}
@@ -569,109 +520,57 @@ class CRUD {
required String link,
Map<String, dynamic>? payload,
}) async {
// String? secretKey = await storage.read(key: BoxName.secretKey);
var url = Uri.parse(
link,
);
var url = Uri.parse(link);
var response = await http.post(
url,
body: payload,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ${AK.secretKey}',
},
);
if (response.statusCode == 200) {
return response.body;
} else {}
if (response.statusCode == 200) return response.body;
}
// Future<dynamic> post({
// required String link,
// Map<String, dynamic>? payload,
// }) async {
// // String? basicAuthCredentials =
// // await storage.read(key: BoxName.basicAuthCredentials);
// var url = Uri.parse(
// link,
// );
// var response = await http.post(
// url,
// body: payload,
// headers: {
// "Content-Type": "application/x-www-form-urlencoded",
// 'Authorization':
// 'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
// },
// );
// var jsonData = jsonDecode(response.body);
// if (response.statusCode == 200) {
// if (jsonData['status'] == 'success') {
// return response.body;
// } else {
// return (jsonData['status']);
// }
// } else {
// return response.statusCode;
// }
// }
Future<dynamic> postPayMob({
required String link,
Map<String, dynamic>? payload,
}) async {
// String? basicAuthCredentials =
// await storage.read(key: BoxName.basicAuthCredentials);
var url = Uri.parse(
link,
);
var url = Uri.parse(link);
var response = await http.post(url,
body: payload, headers: {'Content-Type': 'application/json'});
var jsonData = jsonDecode(response.body);
if (response.statusCode == 200) {
if (jsonData['status'] == 'success') {
return response.body;
} else {
return (jsonData['status']);
}
if (jsonData['status'] == 'success') return response.body;
return jsonData['status'];
} else {
return response.statusCode;
}
}
sendEmail(
String link,
Map<String, String>? payload,
) async {
sendEmail(String link, Map<String, String>? payload) async {
var headers = {
"Content-Type": "application/x-www-form-urlencoded",
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
};
var request = http.Request('POST', Uri.parse(link));
request.bodyFields = payload!;
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
} else {}
}
Future<dynamic> postFromDialogue({
required String link,
Map<String, dynamic>? payload,
}) async {
// String? basicAuthCredentials =
// await storage.read(key: BoxName.basicAuthCredentials);
var url = Uri.parse(
link,
);
var url = Uri.parse(link);
var response = await http.post(
url,
body: payload,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
},
@@ -682,15 +581,10 @@ class CRUD {
if (response.statusCode == 200) {
if (jsonData['status'] == 'success') {
Get.back();
// Get.snackbar(
// jsonData['status'],
// jsonData['message'],
// );
return response.body;
}
}
return (jsonData['status']);
return jsonData['status'];
}
}
@@ -702,7 +596,6 @@ class CRUD {
final Uri verificationUri = Uri.parse(
'https://verify.twilio.com/v2/Services/$verifySid/Verifications');
// Send the verification request
final response = await http.post(
verificationUri,
headers: {
@@ -710,19 +603,11 @@ class CRUD {
'Basic ' + base64Encode(utf8.encode('$accountSid:$authToken')),
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
'To': phoneNumber,
'Channel': 'sms',
},
body: {'To': phoneNumber, 'Channel': 'sms'},
);
if (response.statusCode == 201) {
} else {}
final otpCode = "123456";
// Prompt the user to enter the OTP
final otpCode = "123456"; // Replace with user input
// Check the verification code
final checkUri = Uri.parse(
'https://verify.twilio.com/v2/Services/$verifySid/VerificationCheck');
@@ -733,79 +618,39 @@ class CRUD {
'Basic ' + base64Encode(utf8.encode('$accountSid:$authToken')),
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
'To': phoneNumber,
'Code': otpCode,
},
body: {'To': phoneNumber, 'Code': otpCode},
);
if (checkResponse.statusCode == 201) {
} else {}
}
Future<dynamic> getGoogleApi({
required String link,
Map<String, dynamic>? payload,
}) async {
var url = Uri.parse(
link,
);
var response = await http.post(
url,
body: payload,
);
var url = Uri.parse(link);
var response = await http.post(url, body: payload);
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'OK') {
return jsonData;
}
return (jsonData['status']);
if (jsonData['status'] == 'OK') return jsonData;
return jsonData['status'];
}
Future<dynamic> getHereMap({
required String link,
}) async {
Future<dynamic> getHereMap({required String link}) async {
var url = Uri.parse(link);
try {
var response = await http.get(url);
if (response.statusCode == 200) {
// Ensure the response body is decoded as UTF-8
var decodedBody = utf8.decode(response.bodyBytes);
var data = jsonDecode(decodedBody);
return data;
} else {
return null;
return jsonDecode(decodedBody);
}
return null;
} catch (e) {
return null;
}
}
// Future<dynamic> update({
// required String endpoint,
// required Map<String, dynamic> data,
// required String id,
// }) async {
// // String? basicAuthCredentials =
// // await storage.read(key: BoxName.basicAuthCredentials);
// var url = Uri.parse('$endpoint/$id');
// var response = await http.put(
// url,
// body: json.encode(data),
// headers: {
// 'Authorization':
// 'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
// },
// );
// return json.decode(response.body);
// }
Future<dynamic> delete({
required String endpoint,
required String id,
}) async {
// String? basicAuthCredentials =
// await storage.read(key: BoxName.basicAuthCredentials);
var url = Uri.parse('$endpoint/$id');
var response = await http.delete(
url,
@@ -816,7 +661,4 @@ class CRUD {
);
return json.decode(response.body);
}
// ... [Other methods like sendWhatsAppAuth, getAgoraToken, getLlama, etc., would remain here as they are] ...
// For brevity, I am omitting the rest of the third-party API methods as they would not change.
}

View File

@@ -338,16 +338,19 @@ class DeviceHelper {
: deviceData['identifierForVendor'] ?? 'unknown';
final String deviceModel = deviceData['model'] ?? 'unknown';
final String osVersion = Platform.isAndroid
? deviceData['version']['release'] ?? 'unknown'
: deviceData['systemVersion'] ?? 'unknown';
// final String osVersion = Platform.isAndroid
// ? deviceData['version']['release'] ?? 'unknown'
// : deviceData['systemVersion'] ?? 'unknown';
// Log the extracted information
// Generate and return the encrypted fingerprint
final String fingerprint = '${deviceId}_${deviceModel}_$osVersion';
final String fingerprint = '${deviceId}_$deviceModel';
final String encryptedFp =
EncryptionHelper.instance.encryptData(fingerprint);
box.write(BoxName.deviceFpEncrypted, encryptedFp);
// print(EncryptionHelper.instance.encryptData(fingerprint));
return EncryptionHelper.instance.encryptData(fingerprint);
return encryptedFp;
} catch (e) {
throw Exception('Failed to generate device fingerprint');
}

View File

@@ -4,7 +4,7 @@ class Log {
Log._();
static void print(String value, {StackTrace? stackTrace}) {
// developer.log(value, name: 'LOG', stackTrace: stackTrace);
developer.log(value, name: 'LOG', stackTrace: stackTrace);
}
static Object? inspect(Object? object) {