From 67cc322f5eb05d430dff4c91a3c7f535e8e94848 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Fri, 8 May 2026 23:18:10 +0300 Subject: [PATCH] Update: 2026-05-08 23:18:10 --- app/modules_app/voice/transcribe.php | 2 +- musadaq-app/android/app/build.gradle.kts | 6 ++ musadaq-app/android/gradle.properties | 1 + musadaq-app/ios/Podfile.lock | 6 ++ .../services/device_security_service.dart | 9 +++ .../core/services/home_widget_service.dart | 4 +- .../ai_usage/views/ai_usage_view.dart | 10 +-- .../controllers/subscription_controller.dart | 18 ++++++ .../subscription/views/subscription_view.dart | 64 +++++++++++++++---- musadaq-app/pubspec.yaml | 2 +- 10 files changed, 99 insertions(+), 23 deletions(-) diff --git a/app/modules_app/voice/transcribe.php b/app/modules_app/voice/transcribe.php index 87a9d62..67b5a30 100644 --- a/app/modules_app/voice/transcribe.php +++ b/app/modules_app/voice/transcribe.php @@ -98,7 +98,7 @@ function detectAudioMimeType(string $path, string $fallback, string $fileName = function extractIntentFromAudio(string $base64Audio, string $mimeType, string $apiKey): array { - $model = env('GEMINI_VOICE_MODEL', 'gemini-1.5-flash'); + $model = env('GEMINI_MODEL', 'gemini-flash-lite-latest'); $systemPrompt = << 3.30910.0) - PromisesObjC (~> 2.4) @@ -147,6 +149,7 @@ DEPENDENCIES: - Flutter (from `Flutter`) - flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`) - flutter_secure_storage_darwin (from `.symlinks/plugins/flutter_secure_storage_darwin/darwin`) + - freerasp (from `.symlinks/plugins/freerasp/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`) - objectbox_flutter_libs (from `.symlinks/plugins/objectbox_flutter_libs/ios`) @@ -198,6 +201,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_image_compress_common/ios" flutter_secure_storage_darwin: :path: ".symlinks/plugins/flutter_secure_storage_darwin/darwin" + freerasp: + :path: ".symlinks/plugins/freerasp/ios" image_picker_ios: :path: ".symlinks/plugins/image_picker_ios/ios" local_auth_darwin: @@ -240,6 +245,7 @@ SPEC CHECKSUMS: Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1 flutter_secure_storage_darwin: acdb3f316ed05a3e68f856e0353b133eec373a23 + freerasp: d77275f774facb901f52e9608e5bd34768728363 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a diff --git a/musadaq-app/lib/core/services/device_security_service.dart b/musadaq-app/lib/core/services/device_security_service.dart index 32f2637..3449da6 100644 --- a/musadaq-app/lib/core/services/device_security_service.dart +++ b/musadaq-app/lib/core/services/device_security_service.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:freerasp/freerasp.dart'; import 'package:get/get.dart'; +import 'package:flutter/foundation.dart'; /// Device Security Service /// Enterprise-grade jailbreak/root/tamper detection for Musadaq using freeRASP. @@ -71,6 +72,14 @@ class DeviceSecurityService extends GetxService { /// Handle threat detection void _onThreatDetected(String threat) { debugPrint('[Security] ⚠️ THREAT: $threat'); + + // RELAXED SECURITY FOR DEBUGGING + // Only block the app in release mode or if explicitly requested. + if (kDebugMode) { + debugPrint('[Security] Skipping block due to kDebugMode'); + return; + } + threatDetails.add(threat); isCompromised.value = true; diff --git a/musadaq-app/lib/core/services/home_widget_service.dart b/musadaq-app/lib/core/services/home_widget_service.dart index 597527e..9d2a93b 100644 --- a/musadaq-app/lib/core/services/home_widget_service.dart +++ b/musadaq-app/lib/core/services/home_widget_service.dart @@ -32,14 +32,14 @@ class HomeWidgetService extends GetxService { /// Refresh widget data from API Future refreshWidgetData() async { try { - final res = await DioClient().client.get('/v1/dashboard/stats'); + final res = await DioClient().client.get('dashboard/stats'); if (res.data['success'] == true) { final data = res.data['data']; totalInvoices.value = data['invoices']?['total'] ?? 0; pendingInvoices.value = data['invoices']?['pending'] ?? 0; } - final subRes = await DioClient().client.get('/v1/subscriptions/current'); + final subRes = await DioClient().client.get('subscriptions/current'); if (subRes.data['success'] == true) { final sub = subRes.data['data']; quotaUsed.value = sub['invoices']?['used'] ?? 0; diff --git a/musadaq-app/lib/features/ai_usage/views/ai_usage_view.dart b/musadaq-app/lib/features/ai_usage/views/ai_usage_view.dart index ef39d03..31403f4 100644 --- a/musadaq-app/lib/features/ai_usage/views/ai_usage_view.dart +++ b/musadaq-app/lib/features/ai_usage/views/ai_usage_view.dart @@ -72,7 +72,7 @@ class AiUsageView extends StatelessWidget { Widget _buildHeroCard(Map overall, bool isDark) { final totalRequests = overall['total_requests'] ?? 0; final totalTokens = overall['total_tokens'] ?? 0; - final totalCostJod = (overall['total_cost_jod'] ?? 0).toDouble(); + final totalCostJod = double.tryParse(overall['total_cost_jod']?.toString() ?? '0') ?? 0.0; return Container( padding: const EdgeInsets.all(24), @@ -122,7 +122,7 @@ class AiUsageView extends StatelessWidget { Widget _buildPeriodCard(String title, Map data, IconData icon, Color color, bool isDark) { final requests = data['requests'] ?? 0; final tokens = data['tokens'] ?? 0; - final costJod = (data['cost_jod'] ?? 0).toDouble(); + final costJod = double.tryParse(data['cost_jod']?.toString() ?? '0') ?? 0.0; return Container( padding: const EdgeInsets.all(16), @@ -153,8 +153,8 @@ class AiUsageView extends StatelessWidget { } Widget _buildCostPerInvoiceCard(Map overall, bool isDark) { - final avgCost = (overall['avg_cost_per_invoice_jod'] ?? 0).toDouble(); - final avgTokens = (overall['avg_tokens_per_invoice'] ?? 0).toDouble(); + final avgCost = double.tryParse(overall['avg_cost_per_invoice_jod']?.toString() ?? '0') ?? 0.0; + final avgTokens = double.tryParse(overall['avg_tokens_per_invoice']?.toString() ?? '0') ?? 0.0; return Container( padding: const EdgeInsets.all(20), @@ -238,7 +238,7 @@ class AiUsageView extends StatelessWidget { final date = d['date'] ?? ''; final requests = d['requests'] ?? 0; final tokens = d['tokens'] ?? 0; - final costJod = (d['cost_jod'] ?? 0).toDouble(); + final costJod = double.tryParse(d['cost_jod']?.toString() ?? '0') ?? 0.0; return Container( margin: const EdgeInsets.only(bottom: 8), diff --git a/musadaq-app/lib/features/subscription/controllers/subscription_controller.dart b/musadaq-app/lib/features/subscription/controllers/subscription_controller.dart index d5379dc..bc19638 100644 --- a/musadaq-app/lib/features/subscription/controllers/subscription_controller.dart +++ b/musadaq-app/lib/features/subscription/controllers/subscription_controller.dart @@ -98,4 +98,22 @@ class SubscriptionController extends GetxController { } return null; } + + Future cancelPaymentRequest(String paymentId) async { + try { + isLoading.value = true; + final res = await DioClient().client.post('payments/cancel', data: {'payment_id': paymentId}); + if (res.data['success'] == true) { + AppSnackbar.showSuccess('تم الإلغاء', 'تم إلغاء طلب الدفع بنجاح'); + await loadAll(); + return true; + } + } catch (e) { + AppLogger.error('Failed to cancel payment', e); + AppSnackbar.showError('خطأ', 'فشل إلغاء طلب الدفع'); + } finally { + isLoading.value = false; + } + return false; + } } diff --git a/musadaq-app/lib/features/subscription/views/subscription_view.dart b/musadaq-app/lib/features/subscription/views/subscription_view.dart index 3113818..5cc4b15 100644 --- a/musadaq-app/lib/features/subscription/views/subscription_view.dart +++ b/musadaq-app/lib/features/subscription/views/subscription_view.dart @@ -169,21 +169,57 @@ class SubscriptionView extends StatelessWidget { Text('رقم المرجع: ${payment['reference_number']}', style: const TextStyle(fontFamily: 'monospace', fontSize: 14, fontWeight: FontWeight.bold)), Text('المبلغ: ${payment['amount_jod']} JOD', style: const TextStyle(fontSize: 13)), const SizedBox(height: 8), - SizedBox( - width: double.infinity, - child: ElevatedButton.icon( - icon: const Icon(Icons.upload_file, size: 18), - label: const Text('رفع وصل الدفع'), - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFF59E0B), - foregroundColor: Colors.white, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + const SizedBox(height: 12), + Row( + children: [ + Expanded( + child: ElevatedButton.icon( + icon: const Icon(Icons.upload_file, size: 18), + label: const Text('رفع الوصل'), + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFF59E0B), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 10), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + ), + onPressed: () { + Get.toNamed('/payment-receipt', arguments: payment); + }, + ), ), - onPressed: () { - // Navigate to receipt upload screen - Get.toNamed('/payment-receipt', arguments: payment); - }, - ), + const SizedBox(width: 8), + Expanded( + child: OutlinedButton.icon( + icon: const Icon(Icons.close, size: 18), + label: const Text('إلغاء الطلب'), + style: OutlinedButton.styleFrom( + foregroundColor: Colors.red, + side: const BorderSide(color: Colors.red), + padding: const EdgeInsets.symmetric(vertical: 10), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + ), + onPressed: () { + Get.dialog( + AlertDialog( + title: const Text('إلغاء الطلب'), + content: const Text('هل أنت متأكد من إلغاء طلب الدفع هذا؟'), + actions: [ + TextButton(onPressed: () => Get.back(), child: const Text('تراجع')), + TextButton( + onPressed: () { + Get.back(); + final ctrl = Get.find(); + ctrl.cancelPaymentRequest(payment['id'].toString()); + }, + child: const Text('نعم، إلغاء', style: TextStyle(color: Colors.red)), + ), + ], + ), + ); + }, + ), + ), + ], ), ], ), diff --git a/musadaq-app/pubspec.yaml b/musadaq-app/pubspec.yaml index 627d095..598ae07 100644 --- a/musadaq-app/pubspec.yaml +++ b/musadaq-app/pubspec.yaml @@ -1,7 +1,7 @@ name: musadaq_app description: Jordanian E-Invoicing Automation SaaS publish_to: 'none' -version: 1.0.0+1 +version: 1.0.2+2 environment: sdk: '>=3.2.0 <4.0.0'