// لإضافة هذه الحزمة، قم بتشغيل الأمر التالي في الـ Terminal // flutter pub add intl import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; import 'package:intl/intl.dart'; import 'package:sefer_driver/constant/links.dart'; import 'package:sefer_driver/controller/functions/crud.dart'; import '../../../constant/box_name.dart'; import '../../../main.dart'; /// خدمة لإدارة عمليات الدفع المتعلقة بنظام الدفع عبر الرسائل القصيرة class PaymentService { final String _baseUrl = "${AppLink.seferPaymentServer}/sms_webhook"; Future createInvoice({ required String userPhone, required double amount, }) async { final url = "$_baseUrl/create_invoice.php"; try { final response = await CRUD().postWallet( link: url, payload: { 'user_phone': userPhone.toString(), 'driverID': box.read(BoxName.driverID), 'amount': amount.toString(), }, ).timeout(const Duration(seconds: 15)); // إضافة مهلة للطلب if (response != 'failure') { final data = (response); if (data['status'] == 'success' && data['invoice_number'] != null) { debugPrint( "تم إنشاء الفاتورة بنجاح. الرقم: ${data['invoice_number']}"); return data['invoice_number'].toString(); } else { debugPrint("فشل في إنشاء الفاتورة من السيرفر: ${data['message']}"); return null; } } else { debugPrint("خطأ في السيرفر عند إنشاء الفاتورة: ${response.statusCode}"); return null; } } catch (e) { debugPrint("حدث استثناء عند إنشاء الفاتورة: $e"); return null; } } /// دالة للتحقق من حالة فاتورة واحدة Future checkInvoiceStatus(String invoiceNumber) async { final url = "$_baseUrl/check_invoice_status.php"; try { final response = await CRUD().postWallet(link: url, payload: { 'invoice_number': invoiceNumber, }).timeout(const Duration(seconds: 10)); // مهلة للشبكة if (response != 'failure') { final data = (response); return data['status'] == 'success' && data['invoice_status'] == 'completed'; } return false; } catch (e) { debugPrint("خطأ أثناء التحقق من الفاتورة: $e"); return false; } } } enum PaymentStatus { creatingInvoice, waitingForPayment, paymentSuccess, paymentTimeout, paymentError } class PaymentScreenSmsProvider extends StatefulWidget { final double amount; final String providerName; final String providerLogo; final String paymentPhoneNumber; const PaymentScreenSmsProvider({ super.key, required this.amount, this.providerName = 'شام كاش', this.providerLogo = 'assets/images/shamCash.png', this.paymentPhoneNumber = '963942542053', }); @override _PaymentScreenSmsProviderState createState() => _PaymentScreenSmsProviderState(); } class _PaymentScreenSmsProviderState extends State { final PaymentService _paymentService = PaymentService(); Timer? _pollingTimer; PaymentStatus _status = PaymentStatus.creatingInvoice; String? _invoiceNumber; final String phone = box.read(BoxName.phoneWallet); @override void initState() { super.initState(); _createAndPollInvoice(); } @override void dispose() { _pollingTimer?.cancel(); // مهم جداً: إلغاء المؤقت عند الخروج من الشاشة super.dispose(); } void _createAndPollInvoice() async { setState(() => _status = PaymentStatus.creatingInvoice); final invoiceNumber = await _paymentService.createInvoice( userPhone: phone, amount: widget.amount, ); if (invoiceNumber != null && mounted) { setState(() { _invoiceNumber = invoiceNumber; _status = PaymentStatus.waitingForPayment; }); _startPolling(invoiceNumber); } else if (mounted) { setState(() => _status = PaymentStatus.paymentError); } } void _startPolling(String invoiceNumber) { const timeoutDuration = Duration(minutes: 3); var elapsed = Duration.zero; _pollingTimer = Timer.periodic(const Duration(seconds: 5), (timer) async { elapsed += const Duration(seconds: 5); if (elapsed >= timeoutDuration) { timer.cancel(); if (mounted) setState(() => _status = PaymentStatus.paymentTimeout); return; } debugPrint("Polling... Checking invoice status for: $invoiceNumber"); final isCompleted = await _paymentService.checkInvoiceStatus(invoiceNumber); if (isCompleted && mounted) { timer.cancel(); setState(() => _status = PaymentStatus.paymentSuccess); // TODO: تحديث رصيد المستخدم أو تنفيذ الإجراءات اللازمة } }); } /// دالة جديدة لمعالجة محاولة الرجوع للخلف void _onPopInvoked(bool didPop) async { // إذا كان الرجوع قد تم بالفعل (مثلاً من خلال Navigator.pop)، لا تفعل شيئاً if (didPop) return; // إذا كان المستخدم ينتظر الدفع، أظهر له حوار التأكيد if (_status == PaymentStatus.waitingForPayment) { final shouldPop = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('هل أنت متأكد؟'), content: const Text('إذا خرجت الآن، سيتم إلغاء عملية الدفع الحالية.'), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: const Text('البقاء'), ), TextButton( onPressed: () => Navigator.of(context).pop(true), child: const Text('الخروج'), ), ], ), ); // إذا وافق المستخدم على الخروج، قم بإغلاق الشاشة if (shouldPop ?? false) { Navigator.of(context).pop(); } } } @override Widget build(BuildContext context) { // استخدام PopScope بدلاً من WillPopScope return PopScope( // منع الرجوع التلقائي فقط في حالة انتظار الدفع canPop: _status != PaymentStatus.waitingForPayment, // استدعاء دالة التحقق عند محاولة الرجوع onPopInvoked: _onPopInvoked, child: Scaffold( appBar: AppBar(title: Text("الدفع عبر ${widget.providerName}")), body: Padding( padding: const EdgeInsets.all(16.0), child: Center( child: _buildContentByStatus(), ), ), ), ); } Widget _buildContentByStatus() { switch (_status) { case PaymentStatus.creatingInvoice: return const Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 20), Text("جاري إنشاء فاتورة الدفع...", style: TextStyle(fontSize: 16)), ], ); case PaymentStatus.waitingForPayment: return _buildWaitingForPaymentUI(); case PaymentStatus.paymentSuccess: return _buildSuccessUI(); case PaymentStatus.paymentTimeout: case PaymentStatus.paymentError: return _buildErrorUI(); } } Widget _buildWaitingForPaymentUI() { final currencyFormat = NumberFormat.decimalPattern('ar_SY'); final invoiceText = _invoiceNumber ?? '------'; return SingleChildScrollView( child: Column( children: [ Image.asset(widget.providerLogo, width: 96), const SizedBox(height: 16), Text("تعليمات الدفع", style: Theme.of(context).textTheme.titleLarge), const SizedBox(height: 12), Card( elevation: 1.5, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ _StepTile(number: 1, text: "افتح تطبيق محفظتك الإلكترونية."), _StepTile(number: 2, text: "اختر خدمة تحويل الأموال."), _StepTile( number: 3, text: "أدخل المبلغ المطلوب: ${currencyFormat.format(widget.amount)} ل.س"), _StepTile(number: 4, text: "حوّل إلى الرقم التالي:"), // --- التعديل هنا --- ListTile( contentPadding: EdgeInsets.zero, title: Text( widget.paymentPhoneNumber, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, letterSpacing: 1.2), ), trailing: OutlinedButton.icon( onPressed: () async { await Clipboard.setData( ClipboardData(text: widget.paymentPhoneNumber)); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("تم نسخ رقم الهاتف"))); } }, icon: const Icon(Icons.copy, size: 18), label: const Text("نسخ"), ), ), // --- نهاية التعديل --- const SizedBox(height: 8), _StepTile( number: 5, text: "هام: انسخ رقم القسيمة والصقه في خانة \"البيان\"."), ListTile( contentPadding: EdgeInsets.zero, title: Text(invoiceText, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, letterSpacing: 1.5)), trailing: OutlinedButton.icon( onPressed: _invoiceNumber == null ? null : () async { await Clipboard.setData( ClipboardData(text: invoiceText)); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("تم نسخ رقم القسيمة"))); } }, icon: const Icon(Icons.copy, size: 18), label: const Text("نسخ"), ), ), ], ), ), ), const SizedBox(height: 20), const LinearProgressIndicator(minHeight: 2), const SizedBox(height: 12), Text("بانتظار تأكيد الدفع...", style: TextStyle(color: Colors.grey.shade700)), const SizedBox(height: 4), const Text("هذه الشاشة ستتحدث تلقائيًا", style: TextStyle(color: Colors.grey)), ], ), ); } Widget _buildSuccessUI() { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.check_circle, color: Colors.green, size: 80), const SizedBox(height: 20), const Text("تم الدفع بنجاح!", style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)), const SizedBox(height: 20), ElevatedButton( onPressed: () => Navigator.of(context).pop(), child: const Text("العودة"), ), ], ); } Widget _buildErrorUI() { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error, color: Colors.red, size: 80), const SizedBox(height: 20), Text( _status == PaymentStatus.paymentTimeout ? "انتهى الوقت المحدد للدفع" : "حدث خطأ ما", style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold), textAlign: TextAlign.center, ), const SizedBox(height: 8), const Text("يرجى المحاولة مرة أخرى.", style: TextStyle(fontSize: 16)), const SizedBox(height: 20), ElevatedButton( onPressed: _createAndPollInvoice, child: const Text("المحاولة مرة أخرى"), ), ], ); } } // ويدجت مساعد لعرض خطوات التعليمات بشكل أنيق class _StepTile extends StatelessWidget { final int number; final String text; const _StepTile({required this.number, required this.text}); @override Widget build(BuildContext context) { return ListTile( contentPadding: EdgeInsets.zero, leading: CircleAvatar( radius: 12, backgroundColor: Theme.of(context).primaryColor, child: Text("$number", style: const TextStyle( fontSize: 12, color: Colors.white, fontWeight: FontWeight.bold)), ), title: Text(text), ); } }