diff --git a/lib/controller/payment/payment_controller.dart b/lib/controller/payment/payment_controller.dart index acb12b1..38cea44 100644 --- a/lib/controller/payment/payment_controller.dart +++ b/lib/controller/payment/payment_controller.dart @@ -2,13 +2,14 @@ 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; + //BuildContext? _context; /// ============================== /// START PAYMENT (Safe init) @@ -18,7 +19,7 @@ class PaymentController with ChangeNotifier { required String description, required BuildContext context, }) async { - _context = context; + //_context = context; isProcessing = true; notifyListeners(); @@ -39,7 +40,7 @@ class PaymentController with ChangeNotifier { } if (result == null) { - _showError(context, "Failed to connect to server or timeout."); + _showError("Failed to connect to server or timeout."); isProcessing = false; notifyListeners(); return false; @@ -48,7 +49,9 @@ class PaymentController with ChangeNotifier { final orderId = result['data']?['orderId']; final key = result['data']?['key']; if (orderId == null || key == null) { - _showError(context, "Invalid response from server."); + _showError("Invalid response from server."); + logSafe("Invalid response from server."); + isProcessing = false; notifyListeners(); return false; @@ -64,7 +67,7 @@ class PaymentController with ChangeNotifier { logSafe("โœ… Razorpay instance initialized successfully."); } catch (e) { logSafe("โŒ Razorpay init failed: $e", level: LogLevel.error); - _showError(context, "Payment system initialization failed."); + _showError("Payment system initialization failed."); isProcessing = false; notifyListeners(); return false; @@ -87,7 +90,7 @@ class PaymentController with ChangeNotifier { return true; } catch (e) { logSafe("โŒ Error opening Razorpay: $e", level: LogLevel.error); - _showError(context, "Error opening payment gateway."); + _showError("Error opening payment gateway."); _cleanup(); isProcessing = false; notifyListeners(); @@ -103,15 +106,17 @@ class PaymentController with ChangeNotifier { isProcessing = true; notifyListeners(); - Map? result; + Map? verificationResult; + try { - result = await _paymentService + logSafe("๐ŸŸข Verifying payment via backend..."); + verificationResult = await _paymentService .verifyPayment( paymentId: response.paymentId!, orderId: response.orderId!, signature: response.signature!, ) - .timeout(const Duration(seconds: 10)); + .timeout(const Duration(seconds: 15)); } catch (e) { logSafe("โฑ๏ธ Verification timeout/error: $e", level: LogLevel.error); } @@ -119,16 +124,32 @@ class PaymentController with ChangeNotifier { isProcessing = false; notifyListeners(); - if (result != null && result['verified'] == true) { - _showDialog( - title: "Payment Successful ๐ŸŽ‰", - message: "Your payment was verified successfully.", - success: true, - ); + // โœ… 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 could not be verified.", + message: "Payment completed but backend verification returned null.", success: false, ); } @@ -162,6 +183,7 @@ class PaymentController with ChangeNotifier { _razorpay?.clear(); } catch (_) {} _razorpay = null; + logSafe("๐Ÿงน Razorpay instance cleaned up."); } @override @@ -180,36 +202,37 @@ class PaymentController with ChangeNotifier { required String message, required bool success, }) { - if (_context == null) return; + if (Get.isDialogOpen == true) Get.back(); // Close any existing dialogs - showDialog( - context: _context!, - builder: (ctx) => AlertDialog( - title: Text(title), - content: Text(message), - actions: [ - TextButton( - child: const Text("OK"), - onPressed: () { - Navigator.of(ctx).pop(); - ScaffoldMessenger.of(_context!).showSnackBar( - SnackBar( - content: Text(success - ? "Payment verified successfully!" - : "Payment failed or could not be verified."), - backgroundColor: success ? Colors.green : Colors.red, - ), - ); - }, - ), - ], + 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(BuildContext context, String message) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(message), backgroundColor: Colors.red), + 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, ); } } diff --git a/lib/helpers/services/api_service.dart b/lib/helpers/services/api_service.dart index cf68692..9109d9f 100644 --- a/lib/helpers/services/api_service.dart +++ b/lib/helpers/services/api_service.dart @@ -93,16 +93,26 @@ class ApiService { static dynamic _parseResponse(http.Response response, {String label = ''}) { _log("$label Response: ${response.body}"); + try { - final json = jsonDecode(response.body); - if (response.statusCode == 200 && json['success'] == true) { - return json['data']; + final Map json = jsonDecode(response.body); + + // โœ… Treat 200โ€“299 range as success + final isHttpOk = response.statusCode >= 200 && response.statusCode < 300; + final isSuccess = json['success'] == true; + + if (isHttpOk && isSuccess) { + return json['data']; // return full data block for use in payment } - _log("API Error [$label]: ${json['message'] ?? 'Unknown error'}"); + + // โš ๏ธ Log any API-level error + _log( + "API Error [$label]: ${json['message'] ?? 'Unknown error'} (code ${response.statusCode})"); + return null; } catch (e) { _log("Response parsing error [$label]: $e"); + return null; } - return null; } static dynamic _parseResponseForAllData(http.Response response, diff --git a/lib/helpers/services/payment_service.dart b/lib/helpers/services/payment_service.dart index d438128..5bda4df 100644 --- a/lib/helpers/services/payment_service.dart +++ b/lib/helpers/services/payment_service.dart @@ -21,12 +21,13 @@ class PaymentService { required String signature, }) async { try { - logSafe("๐ŸŸข Calling verifyPayment API: orderId=$orderId, paymentId=$paymentId"); + logSafe("๐ŸŸข Calling verifyPayment API..."); final response = await ApiService.verifyPayment( orderId: orderId, paymentId: paymentId, signature: signature, ); + logSafe("โœ… VerifyPayment API response: $response"); return response; } catch (e) { logSafe("โŒ Error in verifyPayment: $e", level: LogLevel.error); diff --git a/lib/routes.dart b/lib/routes.dart index 6dc6306..3b23d46 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -27,22 +27,27 @@ import 'package:marco/view/subscriptions/subscriptions_screen.dart'; class AuthMiddleware extends GetMiddleware { @override RouteSettings? redirect(String? route) { - // Public routes (no auth required) + // โœ… Public routes that don't require authentication const publicRoutes = [ '/auth/login-option', '/auth/login', - '/subscription', // ๐Ÿ‘ˆ Allow this route without auth + '/subscription', '/payment', '/select-tenant', ]; - // Skip auth checks for public routes - if (publicRoutes.contains(route)) return null; + // โœ… Allow any route that starts with these public routes + if (route != null && + publicRoutes.any((public) => route.startsWith(public))) { + return null; + } + // โœ… Block others if not logged in if (!AuthService.isLoggedIn) { return const RouteSettings(name: '/auth/login-option'); } + // โœ… Ensure tenant is selected after login if (!TenantService.isTenantSelected) { return const RouteSettings(name: '/select-tenant'); } diff --git a/lib/view/layouts/user_profile_right_bar.dart b/lib/view/layouts/user_profile_right_bar.dart index e266643..7b19564 100644 --- a/lib/view/layouts/user_profile_right_bar.dart +++ b/lib/view/layouts/user_profile_right_bar.dart @@ -316,14 +316,14 @@ class _UserProfileBarState extends State onTap: _onProfileTap, ), SizedBox(height: spacingHeight), - // _menuItemRow( - // icon: LucideIcons.bell, - // label: 'Subscribe', - // onTap: _onSubscribeTap, - // iconColor: Colors.redAccent, - // textColor: Colors.redAccent, - // ), - // SizedBox(height: spacingHeight), + _menuItemRow( + icon: LucideIcons.bell, + label: 'Subscribe', + onTap: _onSubscribeTap, + iconColor: Colors.redAccent, + textColor: Colors.redAccent, + ), + SizedBox(height: spacingHeight), _menuItemRow( icon: LucideIcons.settings, label: 'Settings', @@ -393,9 +393,9 @@ class _UserProfileBarState extends State )); } - // void _onSubscribeTap() { - // Get.toNamed("/subscription"); - // } + void _onSubscribeTap() { + Get.toNamed("/subscription"); + } void _onMpinTap() {