164 lines
4.4 KiB
Dart
164 lines
4.4 KiB
Dart
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';
|
||
|
||
class PaymentController with ChangeNotifier {
|
||
final Razorpay _razorpay = Razorpay();
|
||
final PaymentService _paymentService = PaymentService();
|
||
|
||
bool isProcessing = false;
|
||
BuildContext? _context; // For showing dialogs/snackbars
|
||
|
||
PaymentController() {
|
||
// Razorpay event listeners
|
||
_razorpay.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess);
|
||
_razorpay.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError);
|
||
_razorpay.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet);
|
||
}
|
||
|
||
void disposeController() {
|
||
_razorpay.clear();
|
||
}
|
||
|
||
/// ==============================
|
||
/// START PAYMENT
|
||
/// ==============================
|
||
Future<void> startPayment({
|
||
required double amount,
|
||
required String description,
|
||
required BuildContext context,
|
||
}) async {
|
||
_context = context;
|
||
isProcessing = true;
|
||
notifyListeners();
|
||
|
||
logSafe("🟢 Starting payment for ₹$amount - $description");
|
||
|
||
// Call backend to create Razorpay order
|
||
final result = await _paymentService.createOrder(amount);
|
||
|
||
if (result == null) {
|
||
_showError(context, "Failed to connect to server.");
|
||
isProcessing = false;
|
||
notifyListeners();
|
||
return;
|
||
}
|
||
|
||
final orderId = result['orderId'];
|
||
final key = result['key'];
|
||
|
||
if (orderId == null || key == null) {
|
||
_showError(context, "Invalid response from server.");
|
||
isProcessing = false;
|
||
notifyListeners();
|
||
return;
|
||
}
|
||
|
||
var options = {
|
||
'key': key,
|
||
'amount': (amount * 100).toInt(), // Razorpay takes amount in paise
|
||
'name': 'Your Company Name',
|
||
'description': description,
|
||
'order_id': orderId,
|
||
'theme': {'color': '#0D47A1'},
|
||
'timeout': 120, // 2 minutes timeout
|
||
};
|
||
|
||
try {
|
||
logSafe("🟠 Opening Razorpay with options: $options");
|
||
_razorpay.open(options);
|
||
} catch (e) {
|
||
logSafe("❌ Error opening Razorpay: $e", level: LogLevel.error);
|
||
_showError(context, "Error opening payment gateway.");
|
||
isProcessing = false;
|
||
notifyListeners();
|
||
}
|
||
}
|
||
|
||
/// ==============================
|
||
/// EVENT HANDLERS
|
||
/// ==============================
|
||
void _handlePaymentSuccess(PaymentSuccessResponse response) async {
|
||
logSafe("✅ Payment Success: ${response.paymentId}");
|
||
|
||
isProcessing = true;
|
||
notifyListeners();
|
||
|
||
final result = await _paymentService.verifyPayment(
|
||
paymentId: response.paymentId!,
|
||
orderId: response.orderId!,
|
||
signature: response.signature!,
|
||
);
|
||
|
||
isProcessing = false;
|
||
notifyListeners();
|
||
|
||
if (result != null && result['verified'] == true) {
|
||
_showDialog(
|
||
title: "Payment Successful 🎉",
|
||
message: "Your payment was verified successfully.",
|
||
success: true,
|
||
);
|
||
} else {
|
||
_showDialog(
|
||
title: "Verification Failed ❌",
|
||
message: "Payment completed but could not be verified.",
|
||
success: false,
|
||
);
|
||
}
|
||
}
|
||
|
||
void _handlePaymentError(PaymentFailureResponse response) {
|
||
logSafe("❌ Payment Failed: ${response.message}");
|
||
isProcessing = false;
|
||
notifyListeners();
|
||
|
||
_showDialog(
|
||
title: "Payment Failed ❌",
|
||
message: "Reason: ${response.message ?? 'Unknown error'}",
|
||
success: false,
|
||
);
|
||
}
|
||
|
||
void _handleExternalWallet(ExternalWalletResponse response) {
|
||
logSafe("ℹ️ External Wallet Used: ${response.walletName}");
|
||
}
|
||
|
||
/// ==============================
|
||
/// HELPER DIALOGS / SNACKBARS
|
||
/// ==============================
|
||
void _showDialog({
|
||
required String title,
|
||
required String message,
|
||
required bool success,
|
||
}) {
|
||
if (_context == null) return;
|
||
|
||
showDialog(
|
||
context: _context!,
|
||
builder: (ctx) => AlertDialog(
|
||
title: Text(title),
|
||
content: Text(message),
|
||
actions: [
|
||
TextButton(
|
||
child: const Text("OK"),
|
||
onPressed: () {
|
||
Navigator.of(ctx).pop();
|
||
if (success) {
|
||
Navigator.of(_context!).pop(true); // Return success
|
||
}
|
||
},
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
void _showError(BuildContext context, String message) {
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
SnackBar(content: Text(message), backgroundColor: Colors.red),
|
||
);
|
||
}
|
||
}
|