chitfund/luckychit/lib/core/services/phonepe_service.dart

254 lines
6.9 KiB
Dart

import 'package:get/get.dart';
import 'package:flutter/services.dart';
import 'api_service.dart';
/// PhonePe Payment Gateway Integration Service
/// Handles PhonePe payments for chit fund installments
class PhonePeService extends GetxController {
static PhonePeService get to => Get.find();
final ApiService _apiService = ApiService();
// Observable state
final RxBool _isProcessing = false.obs;
final RxString _error = ''.obs;
final RxString _currentTransactionId = ''.obs;
// Getters
bool get isProcessing => _isProcessing.value;
String get error => _error.value;
String get currentTransactionId => _currentTransactionId.value;
/// Initiate a payment for monthly installment
Future<Map<String, dynamic>?> initiatePayment({
required String groupId,
required String userId,
required int month,
required int year,
required double amount,
required String userName,
required String userMobile,
}) async {
try {
_isProcessing.value = true;
_error.value = '';
// Generate unique transaction ID
final transactionId = 'TXN_${DateTime.now().millisecondsSinceEpoch}';
_currentTransactionId.value = transactionId;
// Create payment request on backend
final response = await _apiService.post(
'/payments/phonepe/initiate',
{
'group_id': groupId,
'user_id': userId,
'month': month,
'year': year,
'amount': amount,
'transaction_id': transactionId,
'user_name': userName,
'user_mobile': userMobile,
},
);
if (response['success'] == true) {
final paymentData = response['data'];
// Launch PhonePe payment
final result = await _launchPhonePePayment(paymentData);
if (result != null && result['success'] == true) {
// Verify payment on backend
return await verifyPayment(transactionId);
} else {
_error.value = result?['message'] ?? 'Payment failed';
return null;
}
} else {
_error.value = response['message'] ?? 'Failed to initiate payment';
return null;
}
} catch (e) {
print('PhonePe payment error: $e');
_error.value = 'Payment failed: $e';
return null;
} finally {
_isProcessing.value = false;
}
}
/// Launch PhonePe payment flow
Future<Map<String, dynamic>?> _launchPhonePePayment(
Map<String, dynamic> paymentData,
) async {
try {
// Use PhonePe SDK or deep link
final String paymentUrl = paymentData['payment_url'];
final String checksum = paymentData['checksum'];
// Method 1: Using platform channel (if PhonePe SDK is integrated)
try {
final result = await _invokePhonePeSDK(
paymentUrl: paymentUrl,
checksum: checksum,
);
return result;
} catch (e) {
print('PhonePe SDK not available: $e');
// Method 2: Using URL launcher as fallback
return await _launchPhonePeUrl(paymentUrl);
}
} catch (e) {
print('Error launching PhonePe: $e');
return {
'success': false,
'message': 'Failed to launch PhonePe: $e',
};
}
}
/// Invoke PhonePe SDK via platform channel
Future<Map<String, dynamic>> _invokePhonePeSDK({
required String paymentUrl,
required String checksum,
}) async {
const platform = MethodChannel('com.luckychit.phonepe');
try {
final result = await platform.invokeMethod('startPayment', {
'paymentUrl': paymentUrl,
'checksum': checksum,
'callbackUrl': 'luckychit://payment/callback',
});
return {
'success': true,
'data': result,
};
} on PlatformException catch (e) {
return {
'success': false,
'message': e.message ?? 'Payment failed',
};
}
}
/// Launch PhonePe using URL (fallback method)
Future<Map<String, dynamic>> _launchPhonePeUrl(String paymentUrl) async {
// This would use url_launcher in a real implementation
// For now, return a simulated response
return {
'success': true,
'message': 'Payment URL opened',
};
}
/// Verify payment status on backend
Future<Map<String, dynamic>?> verifyPayment(String transactionId) async {
try {
final response = await _apiService.post(
'/payments/phonepe/verify',
{'transaction_id': transactionId},
);
if (response['success'] == true) {
return response['data'];
} else {
_error.value = response['message'] ?? 'Payment verification failed';
return null;
}
} catch (e) {
print('Payment verification error: $e');
_error.value = 'Verification failed: $e';
return null;
}
}
/// Check payment status
Future<String> getPaymentStatus(String transactionId) async {
try {
final response = await _apiService.get(
'/payments/phonepe/status/$transactionId',
);
if (response['success'] == true) {
return response['data']['status'] ?? 'pending';
} else {
return 'failed';
}
} catch (e) {
print('Error checking payment status: $e');
return 'error';
}
}
/// Handle payment callback (for deep links)
Future<void> handlePaymentCallback(Map<String, dynamic> callbackData) async {
try {
final transactionId = callbackData['transactionId'];
final status = callbackData['status'];
if (status == 'SUCCESS') {
// Verify payment
await verifyPayment(transactionId);
} else {
_error.value = 'Payment ${status.toLowerCase()}';
}
} catch (e) {
print('Error handling callback: $e');
_error.value = 'Failed to process payment callback';
}
}
/// Initiate refund (for cancelled payments)
Future<bool> initiateRefund({
required String transactionId,
required double amount,
String? reason,
}) async {
try {
final response = await _apiService.post(
'/payments/phonepe/refund',
{
'transaction_id': transactionId,
'amount': amount,
'reason': reason,
},
);
return response['success'] == true;
} catch (e) {
print('Refund error: $e');
_error.value = 'Refund failed: $e';
return false;
}
}
/// Get transaction details
Future<Map<String, dynamic>?> getTransactionDetails(
String transactionId,
) async {
try {
final response = await _apiService.get(
'/payments/phonepe/transaction/$transactionId',
);
if (response['success'] == true) {
return response['data'];
}
return null;
} catch (e) {
print('Error fetching transaction: $e');
return null;
}
}
/// Clear error state
void clearError() {
_error.value = '';
}
}