feature/payment #77
@ -14,87 +14,64 @@ class PaymentController with ChangeNotifier {
|
|||||||
/// ==============================
|
/// ==============================
|
||||||
/// START PAYMENT (Safe init)
|
/// START PAYMENT (Safe init)
|
||||||
/// ==============================
|
/// ==============================
|
||||||
Future<bool> startPayment({
|
Future<void> startPayment({
|
||||||
required double amount,
|
required double amount,
|
||||||
required String description,
|
required String description,
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
}) async {
|
}) async {
|
||||||
//_context = context;
|
try {
|
||||||
isProcessing = true;
|
isProcessing = true;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
||||||
logSafe("🟢 Starting payment for ₹$amount - $description");
|
// Step 1: Create payment order
|
||||||
|
final result = await _paymentService.createOrder(amount);
|
||||||
// ✅ Clear any old instance (prevents freeze on re-init)
|
logSafe("🧩 Raw response in PaymentController: $result");
|
||||||
_cleanup();
|
|
||||||
|
|
||||||
// ✅ Create order first (no login dependency)
|
|
||||||
Map<String, dynamic>? 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Step 2: Validate result before accessing keys
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
_showError("Failed to connect to server or timeout.");
|
_showError("Failed to create order. Server returned null response.");
|
||||||
isProcessing = false;
|
isProcessing = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final orderId = result['data']?['orderId'];
|
// Step 2: Handle both wrapped and unwrapped formats
|
||||||
final key = result['data']?['key'];
|
final data = result['data'] ?? result;
|
||||||
|
final orderId = data?['orderId'];
|
||||||
|
final key = data?['key'];
|
||||||
|
|
||||||
if (orderId == null || key == null) {
|
if (orderId == null || key == null) {
|
||||||
_showError("Invalid response from server.");
|
_showError("Invalid response from server. Missing orderId or key.");
|
||||||
logSafe("Invalid response from server.");
|
logSafe("💥 Invalid response structure: $result");
|
||||||
|
|
||||||
isProcessing = false;
|
isProcessing = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Safe initialization of Razorpay (deferred)
|
// Step 3: Initialize Razorpay if needed
|
||||||
try {
|
_razorpay ??= Razorpay();
|
||||||
logSafe("🟡 Initializing Razorpay instance...");
|
_razorpay!.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess);
|
||||||
_razorpay = Razorpay();
|
_razorpay!.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError);
|
||||||
_razorpay?.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess);
|
_razorpay!.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet);
|
||||||
_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
|
// Step 4: Open Razorpay checkout
|
||||||
final options = {
|
final options = {
|
||||||
'key': key,
|
'key': key,
|
||||||
'amount': (amount * 100).toInt(),
|
'amount': (amount * 100).toInt(), // Razorpay expects amount in paise
|
||||||
'name': 'Your Company Name',
|
'name': 'Marco',
|
||||||
'description': description,
|
'description': 'Subscription Payment',
|
||||||
'order_id': orderId,
|
'order_id': orderId,
|
||||||
'theme': {'color': '#0D47A1'},
|
'prefill': {'contact': '9999999999', 'email': 'test@marco.com'},
|
||||||
'timeout': 120, // seconds
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
|
||||||
logSafe("🟠 Opening Razorpay checkout with options: $options");
|
logSafe("🟠 Opening Razorpay checkout with options: $options");
|
||||||
_razorpay!.open(options);
|
_razorpay!.open(options);
|
||||||
return true;
|
} catch (e, s) {
|
||||||
} catch (e) {
|
_showError("Payment initialization failed. Please try again.");
|
||||||
logSafe("❌ Error opening Razorpay: $e", level: LogLevel.error);
|
logSafe("💥 Exception in startPayment: $e\n$s");
|
||||||
_showError("Error opening payment gateway.");
|
} finally {
|
||||||
_cleanup();
|
|
||||||
isProcessing = false;
|
isProcessing = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,8 @@ class PaymentService {
|
|||||||
try {
|
try {
|
||||||
logSafe("🟢 Calling createPaymentOrder API with amount: ₹$amount");
|
logSafe("🟢 Calling createPaymentOrder API with amount: ₹$amount");
|
||||||
final response = await ApiService.createPaymentOrder(amount);
|
final response = await ApiService.createPaymentOrder(amount);
|
||||||
|
logSafe("🧩 Raw response in PaymentService: $response");
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logSafe("❌ Error in createOrder: $e", level: LogLevel.error);
|
logSafe("❌ Error in createOrder: $e", level: LogLevel.error);
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import 'package:marco/view/employees/employee_profile_screen.dart';
|
|||||||
import 'package:marco/helpers/services/tenant_service.dart';
|
import 'package:marco/helpers/services/tenant_service.dart';
|
||||||
import 'package:marco/view/tenant/tenant_selection_screen.dart';
|
import 'package:marco/view/tenant/tenant_selection_screen.dart';
|
||||||
import 'package:marco/controller/tenant/tenant_switch_controller.dart';
|
import 'package:marco/controller/tenant/tenant_switch_controller.dart';
|
||||||
import 'package:marco/view/appearance_screen.dart';
|
// import 'package:marco/view/appearance_screen.dart';
|
||||||
|
|
||||||
class UserProfileBar extends StatefulWidget {
|
class UserProfileBar extends StatefulWidget {
|
||||||
final bool isCondensed;
|
final bool isCondensed;
|
||||||
@ -314,9 +314,9 @@ class _UserProfileBarState extends State<UserProfileBar>
|
|||||||
_menuItemRow(
|
_menuItemRow(
|
||||||
icon: LucideIcons.settings,
|
icon: LucideIcons.settings,
|
||||||
label: 'Settings',
|
label: 'Settings',
|
||||||
onTap: () {
|
// onTap: () {
|
||||||
Get.to(() => const AppearancePage());
|
// Get.to(() => const AppearancePage());
|
||||||
},
|
// },
|
||||||
),
|
),
|
||||||
SizedBox(height: spacingHeight),
|
SizedBox(height: spacingHeight),
|
||||||
_menuItemRow(
|
_menuItemRow(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user