From fdfea5582a77d839fb3c26cce781b0cc4a283106 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Fri, 13 Mar 2026 18:27:21 +0300 Subject: [PATCH] 2026-03-13-1 --- android/app/build.gradle | 4 +- ios/Flutter/AppFrameworkInfo.plist | 44 +-- ios/Runner/Info.plist | 207 ++++++----- lib/constant/box_name.dart | 1 + lib/constant/links.dart | 8 +- lib/controller/auth/login_controller.dart | 174 ++++----- lib/controller/auth/register_controller.dart | 14 - lib/controller/firebase/firbase_messge.dart | 2 +- lib/controller/functions/crud.dart | 366 ++++++------------- lib/controller/functions/package_info.dart | 13 +- lib/print.dart | 2 +- 11 files changed, 338 insertions(+), 497 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 2eeefe4..2b26425 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -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" diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 391a902..4aaa3e0 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -1,24 +1,26 @@ - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - - + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 16.1 + + \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 6786289..1ed3bac 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -1,107 +1,106 @@ - - NSSupportsLiveActivities - - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLName - intaleqapp.com - CFBundleURLSchemes - - - com.googleusercontent.apps.1086900987150-9jv4oa8l3t23d54lrf27c1d22tbt9i6d - intaleq - - - - FlutterDeepLinkingEnabled - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Intaleq - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - Intaleq - CFBundlePackageType - APPL - CFBundleShortVersionString - 31 - CFBundleSignature - ???? - CFBundleVersion - 1.1.31 - FirebaseAppDelegateProxyEnabled - NO - GMSApiKey - YOUR_API_KEY - LSApplicationQueriesSchemes - - googlechromes - comgooglemaps - - LSRequiresIPhoneOS - - NSCameraUsageDescription - 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. - NSContactsUsageDescription - This app requires contacts access to function properly. - NSFaceIDUsageDescription - Use Face ID to securely authenticate payment accounts. - NSLocationAlwaysAndWhenInUseUsageDescription - 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. - NSLocationAlwaysUsageDescription - This app needs access to location. - NSLocationWhenInUseUsageDescription - 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. - NSMicrophoneUsageDescription - 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. - NSPhotoLibraryUsageDescription - This app requires access to the photo library to upload pictures. - UIApplicationSupportsIndirectInputEvents - - UIBackgroundModes - - fetch - location - remote-notification - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - \ No newline at end of file + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Intaleq + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Intaleq + CFBundlePackageType + APPL + CFBundleShortVersionString + 32 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + intaleqapp.com + CFBundleURLSchemes + + com.googleusercontent.apps.1086900987150-9jv4oa8l3t23d54lrf27c1d22tbt9i6d + intaleq + + + + CFBundleVersion + 1.1.32 + FirebaseAppDelegateProxyEnabled + NO + FlutterDeepLinkingEnabled + + GMSApiKey + YOUR_API_KEY + LSApplicationQueriesSchemes + + googlechromes + comgooglemaps + + LSRequiresIPhoneOS + + NSCameraUsageDescription + 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. + NSContactsUsageDescription + This app requires contacts access to function properly. + NSFaceIDUsageDescription + Use Face ID to securely authenticate payment accounts. + NSLocationAlwaysAndWhenInUseUsageDescription + 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. + NSLocationAlwaysUsageDescription + This app needs access to location. + NSLocationWhenInUseUsageDescription + 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. + NSMicrophoneUsageDescription + 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. + NSPhotoLibraryUsageDescription + This app requires access to the photo library to upload pictures. + NSSupportsLiveActivities + + UIApplicationSupportsIndirectInputEvents + + UIBackgroundModes + + fetch + location + remote-notification + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/lib/constant/box_name.dart b/lib/constant/box_name.dart index e20bfa3..a2379f0 100644 --- a/lib/constant/box_name.dart +++ b/lib/constant/box_name.dart @@ -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"; diff --git a/lib/constant/links.dart b/lib/constant/links.dart index 7d25ee7..96aaf6d 100644 --- a/lib/constant/links.dart +++ b/lib/constant/links.dart @@ -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) للتعامل مع عمليات إلغاء الرحلات وتحديث حالة الرحلات وغيرها من العمليات المتعلقة بالرحلات. diff --git a/lib/controller/auth/login_controller.dart b/lib/controller/auth/login_controller.dart index 92b4e4f..25eb9c9 100644 --- a/lib/controller/auth/login_controller.dart +++ b/lib/controller/auth/login_controller.dart @@ -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 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 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 loginUsingCredentials(String passengerID, String email) async { isloading = true; update(); diff --git a/lib/controller/auth/register_controller.dart b/lib/controller/auth/register_controller.dart index f86eab1..8e4c8d7 100644 --- a/lib/controller/auth/register_controller.dart +++ b/lib/controller/auth/register_controller.dart @@ -297,20 +297,6 @@ class RegisterController extends GetxController { ); if (res1 != 'failure') { - // if (AppLink.IntaleqAlexandriaServer != AppLink.IntaleqSyriaServer) { - // List 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)); diff --git a/lib/controller/firebase/firbase_messge.dart b/lib/controller/firebase/firbase_messge.dart index abf673e..cd276fc 100644 --- a/lib/controller/firebase/firbase_messge.dart +++ b/lib/controller/firebase/firbase_messge.dart @@ -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 diff --git a/lib/controller/functions/crud.dart b/lib/controller/functions/crud.dart index 5dd615f..8d7d378 100644 --- a/lib/controller/functions/crud.dart +++ b/lib/controller/functions/crud.dart @@ -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 _makeRequest({ @@ -75,13 +84,11 @@ class CRUD { Map? payload, required Map headers, }) async { - // timeouts أقصر const connectTimeout = Duration(seconds: 6); const receiveTimeout = Duration(seconds: 10); Future 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 post({ required String link, Map? 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 get({ required String link, Map? 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 postWallet({ required String link, Map? 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 getWallet({ required String link, Map? 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 postWalletMtn( {required String link, Map? 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 getTokenParent({ - // required String link, - // Map? 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 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 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? 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 post({ - // required String link, - // Map? 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 postPayMob({ required String link, Map? 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? payload, - ) async { + sendEmail(String link, Map? 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 postFromDialogue({ required String link, Map? 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 getGoogleApi({ required String link, Map? 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 getHereMap({ - required String link, - }) async { + Future 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 update({ - // required String endpoint, - // required Map 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 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. } diff --git a/lib/controller/functions/package_info.dart b/lib/controller/functions/package_info.dart index 05c3ce8..ee00f41 100644 --- a/lib/controller/functions/package_info.dart +++ b/lib/controller/functions/package_info.dart @@ -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'); } diff --git a/lib/print.dart b/lib/print.dart index 63efb2d..a3d59f6 100644 --- a/lib/print.dart +++ b/lib/print.dart @@ -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) {