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 { try { isProcessing = true; notifyListeners(); // Step 1: Create payment order final result = await _paymentService.createOrder(amount); logSafe("๐Ÿงฉ Raw response in PaymentController: $result"); // Step 2: Validate result before accessing keys if (result == null) { _showError("Failed to create order. Server returned null response."); isProcessing = false; notifyListeners(); return; } // Step 2: Handle both wrapped and unwrapped formats final data = result['data'] ?? result; final orderId = data?['orderId']; final key = data?['key']; if (orderId == null || key == null) { _showError("Invalid response from server. Missing orderId or key."); logSafe("๐Ÿ’ฅ Invalid response structure: $result"); isProcessing = false; notifyListeners(); return; } // Step 3: Initialize Razorpay if needed _razorpay ??= Razorpay(); _razorpay!.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess); _razorpay!.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError); _razorpay!.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet); // Step 4: Open Razorpay checkout final options = { 'key': key, 'amount': (amount * 100).toInt(), // Razorpay expects amount in paise 'name': 'Marco', 'description': 'Subscription Payment', 'order_id': orderId, 'prefill': {'contact': '9999999999', 'email': 'test@marco.com'}, }; logSafe("๐ŸŸ  Opening Razorpay checkout with options: $options"); _razorpay!.open(options); } catch (e, s) { _showError("Payment initialization failed. Please try again."); logSafe("๐Ÿ’ฅ Exception in startPayment: $e\n$s"); } finally { isProcessing = false; notifyListeners(); } } /// ============================== /// 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, ); } }