import 'package:flutter/material.dart'; import 'package:razorpay_flutter/razorpay_flutter.dart'; import 'package:marco/helpers/services/payment_service.dart'; import 'package:marco/helpers/services/app_logger.dart'; import 'package:get/get.dart'; class PaymentController with ChangeNotifier { Razorpay? _razorpay; final PaymentService _paymentService = PaymentService(); bool isProcessing = false; //BuildContext? _context; /// ============================== /// START PAYMENT (Safe init) /// ============================== Future startPayment({ required double amount, required String description, required BuildContext context, }) async { //_context = context; isProcessing = true; notifyListeners(); logSafe("๐ŸŸข Starting payment for โ‚น$amount - $description"); // โœ… Clear any old instance (prevents freeze on re-init) _cleanup(); // โœ… Create order first (no login dependency) Map? result; try { result = await _paymentService .createOrder(amount) .timeout(const Duration(seconds: 10)); } catch (e) { logSafe("โฑ๏ธ API Timeout or Exception while creating order: $e", level: LogLevel.error); } if (result == null) { _showError("Failed to connect to server or timeout."); isProcessing = false; notifyListeners(); return false; } final orderId = result['data']?['orderId']; final key = result['data']?['key']; if (orderId == null || key == null) { _showError("Invalid response from server."); logSafe("Invalid response from server."); isProcessing = false; notifyListeners(); return false; } // โœ… Safe initialization of Razorpay (deferred) try { logSafe("๐ŸŸก Initializing Razorpay instance..."); _razorpay = Razorpay(); _razorpay?.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess); _razorpay?.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError); _razorpay?.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet); logSafe("โœ… Razorpay instance initialized successfully."); } catch (e) { logSafe("โŒ Razorpay init failed: $e", level: LogLevel.error); _showError("Payment system initialization failed."); isProcessing = false; notifyListeners(); return false; } // โœ… Prepare payment options final options = { 'key': key, 'amount': (amount * 100).toInt(), 'name': 'Your Company Name', 'description': description, 'order_id': orderId, 'theme': {'color': '#0D47A1'}, 'timeout': 120, // seconds }; try { logSafe("๐ŸŸ  Opening Razorpay checkout with options: $options"); _razorpay!.open(options); return true; } catch (e) { logSafe("โŒ Error opening Razorpay: $e", level: LogLevel.error); _showError("Error opening payment gateway."); _cleanup(); isProcessing = false; notifyListeners(); return false; } } /// ============================== /// EVENT HANDLERS /// ============================== void _handlePaymentSuccess(PaymentSuccessResponse response) async { logSafe("โœ… Payment Success: ${response.paymentId}"); isProcessing = true; notifyListeners(); Map? verificationResult; try { logSafe("๐ŸŸข Verifying payment via backend..."); verificationResult = await _paymentService .verifyPayment( paymentId: response.paymentId!, orderId: response.orderId!, signature: response.signature!, ) .timeout(const Duration(seconds: 15)); } catch (e) { logSafe("โฑ๏ธ Verification timeout/error: $e", level: LogLevel.error); } isProcessing = false; notifyListeners(); // โœ… Handle backend verification response properly if (verificationResult != null) { // Example backend response: { "verified": true, "message": "Payment verified" } final isVerified = verificationResult['verified'] == true; final msg = verificationResult['message'] ?? ''; if (isVerified) { _showDialog( title: "Payment Successful ๐ŸŽ‰", message: msg.isNotEmpty ? msg : "Your payment was verified successfully.", success: true, ); } else { _showDialog( title: "Verification Failed โŒ", message: msg.isNotEmpty ? msg : "Payment completed but verification failed.", success: false, ); } } else { _showDialog( title: "Verification Failed โŒ", message: "Payment completed but backend verification returned null.", success: false, ); } _cleanup(); } void _handlePaymentError(PaymentFailureResponse response) { logSafe("โŒ Payment Failed: ${response.message}"); isProcessing = false; notifyListeners(); _showDialog( title: "Payment Failed โŒ", message: "Reason: ${response.message ?? 'Unknown error'}", success: false, ); _cleanup(); } void _handleExternalWallet(ExternalWalletResponse response) { logSafe("โ„น๏ธ External Wallet Used: ${response.walletName}"); } /// ============================== /// CLEANUP / DISPOSE /// ============================== void _cleanup() { try { _razorpay?.clear(); } catch (_) {} _razorpay = null; logSafe("๐Ÿงน Razorpay instance cleaned up."); } @override void dispose() { _cleanup(); super.dispose(); } void disposeController() => _cleanup(); /// ============================== /// HELPER UI FUNCTIONS /// ============================== void _showDialog({ required String title, required String message, required bool success, }) { if (Get.isDialogOpen == true) Get.back(); // Close any existing dialogs Get.defaultDialog( title: title, middleText: message, confirm: ElevatedButton( onPressed: () { Get.back(); // close dialog Get.snackbar( success ? "Payment Successful ๐ŸŽ‰" : "Payment Failed โŒ", success ? "Payment verified successfully!" : "Payment failed or could not be verified.", backgroundColor: success ? Colors.green : Colors.red, colorText: Colors.white, snackPosition: SnackPosition.BOTTOM, ); }, child: const Text("OK"), ), ); } void _showError(String message) { if (Get.isDialogOpen == true) Get.back(); // Close any open dialog Get.snackbar( "Payment Error", message, backgroundColor: Colors.red, colorText: Colors.white, snackPosition: SnackPosition.BOTTOM, ); } }