marco.pms.mobileapp/lib/controller/payment/payment_controller.dart

216 lines
5.9 KiB
Dart
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 {
Razorpay? _razorpay;
final PaymentService _paymentService = PaymentService();
bool isProcessing = false;
BuildContext? _context;
/// ==============================
/// START PAYMENT (Safe init)
/// ==============================
Future<bool> 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<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);
}
if (result == null) {
_showError(context, "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(context, "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(context, "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(context, "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<String, dynamic>? result;
try {
result = await _paymentService
.verifyPayment(
paymentId: response.paymentId!,
orderId: response.orderId!,
signature: response.signature!,
)
.timeout(const Duration(seconds: 10));
} catch (e) {
logSafe("⏱️ Verification timeout/error: $e", level: LogLevel.error);
}
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,
);
}
_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;
}
@override
void dispose() {
_cleanup();
super.dispose();
}
void disposeController() => _cleanup();
/// ==============================
/// HELPER UI FUNCTIONS
/// ==============================
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();
ScaffoldMessenger.of(_context!).showSnackBar(
SnackBar(
content: Text(success
? "Payment verified successfully!"
: "Payment failed or could not be verified."),
backgroundColor: success ? Colors.green : Colors.red,
),
);
},
),
],
),
);
}
void _showError(BuildContext context, String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message), backgroundColor: Colors.red),
);
}
}