import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import '../../../constant/box_name.dart'; import '../../../constant/links.dart'; import '../../../controller/functions/crud.dart'; import '../../../main.dart'; // خدمة الدفع للراكب (تم تحديث المسارات) class PaymentService { // المسار الجديد لمجلد الركاب final String _baseUrl = "${AppLink.paymentServer}/ride/shamcash/passenger"; Future createInvoice({required double amount}) async { final url = "$_baseUrl/create_invoice.php"; try { final response = await CRUD().postWallet( link: url, payload: { 'passengerID': box.read(BoxName.passengerID), // استخدام passengerID 'amount': amount.toString(), }, ).timeout(const Duration(seconds: 15)); if (response != 'failure') { final data = response; if (data['status'] == 'success' && data['invoice_number'] != null) { return data['invoice_number'].toString(); } } return null; } catch (e) { debugPrint("Create Invoice Error: $e"); return null; } } Future checkInvoiceStatus(String invoiceNumber) async { final url = "$_baseUrl/check_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) { return false; } } } enum PaymentStatus { creatingInvoice, waitingForPayment, paymentSuccess, paymentTimeout, paymentError } class PaymentScreenSmsProvider extends StatefulWidget { final double amount; final String providerName; final String providerLogo; final String qrImagePath; const PaymentScreenSmsProvider({ super.key, required this.amount, this.providerName = 'شام كاش', this.providerLogo = 'assets/images/shamCash.png', this.qrImagePath = 'assets/images/shamcashsend.png', }); @override _PaymentScreenSmsProviderState createState() => _PaymentScreenSmsProviderState(); } class _PaymentScreenSmsProviderState extends State { final PaymentService _paymentService = PaymentService(); Timer? _pollingTimer; PaymentStatus _status = PaymentStatus.creatingInvoice; String? _invoiceNumber; @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(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: 5); 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; } final isCompleted = await _paymentService.checkInvoiceStatus(invoiceNumber); if (isCompleted && mounted) { timer.cancel(); setState(() => _status = PaymentStatus.paymentSuccess); } }); } Future _onPopInvoked() async { if (_status == PaymentStatus.waitingForPayment) { final shouldPop = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('إلغاء العملية؟', textAlign: TextAlign.right), content: const Text('الخروج الآن سيؤدي لإلغاء متابعة عملية الدفع.', textAlign: TextAlign.right), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: const Text('البقاء')), TextButton( onPressed: () => Navigator.of(context).pop(true), child: const Text('خروج', style: TextStyle(color: Colors.red))), ], ), ); return shouldPop ?? false; } return true; } @override Widget build(BuildContext context) { return WillPopScope( onWillPop: _onPopInvoked, child: Scaffold( backgroundColor: Colors.grey[50], appBar: AppBar( title: Text("دفع عبر ${widget.providerName}"), centerTitle: true, elevation: 0, backgroundColor: Colors.white, foregroundColor: Colors.black, ), body: SafeArea( child: Padding( padding: const EdgeInsets.all(20.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( physics: const BouncingScrollPhysics(), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // 1. المبلغ Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 15), decoration: BoxDecoration( gradient: LinearGradient( colors: [Colors.blue.shade800, Colors.blue.shade600]), borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.blue.withOpacity(0.25), blurRadius: 15, offset: const Offset(0, 8)) ], ), child: Column( children: [ const Text("المبلغ المطلوب", style: TextStyle(color: Colors.white70, fontSize: 14)), const SizedBox(height: 8), Text("${currencyFormat.format(widget.amount)} ل.س", style: const TextStyle( color: Colors.white, fontSize: 32, fontWeight: FontWeight.bold)), ], ), ), const SizedBox(height: 30), // 2. التعليمات والنسخ (للراكب) Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.grey.shade200), boxShadow: [ BoxShadow( color: Colors.grey.shade100, blurRadius: 10, offset: const Offset(0, 4)) ], ), child: Column( children: [ Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.orange.shade50, shape: BoxShape.circle), child: Icon(Icons.priority_high_rounded, color: Colors.orange.shade800, size: 20), ), const SizedBox(width: 12), const Expanded( child: Text( "انسخ الرقم أدناه وضعه في خانة (الملاحظات) عند الدفع.", style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600)), ), ], ), const SizedBox(height: 20), InkWell( onTap: () { Clipboard.setData(ClipboardData(text: invoiceText)); ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: const Text("تم نسخ رقم البيان ✅", textAlign: TextAlign.center), backgroundColor: Colors.green.shade600)); }, borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.symmetric( horizontal: 15, vertical: 12), decoration: BoxDecoration( color: Colors.grey.shade50, borderRadius: BorderRadius.circular(12), border: Border.all( color: Colors.blue.shade200, width: 1.5)), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text("رقم البيان (Invoice ID)", style: TextStyle( fontSize: 12, color: Colors.grey)), Text(invoiceText, style: const TextStyle( fontSize: 22, fontWeight: FontWeight.bold, letterSpacing: 1.5)), ], ), const Icon(Icons.copy_rounded, color: Colors.blue, size: 24), ], ), ), ), ], ), ), const SizedBox(height: 30), // 3. QR Code const Text("امسح الرمز للدفع", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), const SizedBox(height: 15), GestureDetector( onTap: () { showDialog( context: context, builder: (ctx) => Dialog( backgroundColor: Colors.transparent, child: InteractiveViewer( child: Image.asset(widget.qrImagePath)))); }, child: Container( padding: const EdgeInsets.all(15), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.grey.shade300)), child: Column( children: [ Image.asset(widget.qrImagePath, width: 180, height: 180, fit: BoxFit.contain, errorBuilder: (c, o, s) => const Icon(Icons.qr_code_2, size: 100, color: Colors.grey)), const SizedBox(height: 8), const Text("اضغط للتكبير", style: TextStyle(fontSize: 10, color: Colors.grey)), ], ), ), ), const SizedBox(height: 40), const LinearProgressIndicator(backgroundColor: Colors.white), const SizedBox(height: 10), const Text("ننتظر إشعار الدفع تلقائياً...", style: TextStyle(color: Colors.grey, fontSize: 12)), const SizedBox(height: 20), ], ), ); } Widget _buildSuccessUI() { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.verified_rounded, color: Colors.green, size: 100), const SizedBox(height: 20), const Text("تم الدفع بنجاح!", style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)), const SizedBox(height: 40), SizedBox( width: double.infinity, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.green, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16)), onPressed: () => Navigator.of(context).pop(), child: const Text("متابعة", style: TextStyle(fontSize: 18)), ), ), ], ); } Widget _buildErrorUI() { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error_outline_rounded, color: Colors.red.shade400, size: 80), const SizedBox(height: 20), Text( _status == PaymentStatus.paymentTimeout ? "انتهى الوقت" : "لم يتم التحقق", style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold)), const SizedBox(height: 15), const Padding( padding: EdgeInsets.symmetric(horizontal: 30), child: Text("لم يصلنا إشعار الدفع.", textAlign: TextAlign.center, style: TextStyle(color: Colors.grey))), const SizedBox(height: 40), SizedBox( width: double.infinity, child: ElevatedButton.icon( style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 15)), onPressed: _createAndPollInvoice, icon: const Icon(Icons.refresh), label: const Text("حاول مرة أخرى"), ), ), const SizedBox(height: 15), TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text("إلغاء", style: TextStyle(color: Colors.grey))) ], ); } }