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

586 lines
18 KiB
Dart

import 'package:dio/dio.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ApiService {
// static const String baseUrl = 'http://localhost:3000/api';
static const String baseUrl = 'https://chitfund.deepteklabs.com/api';
static const String tokenKey = 'auth_token';
late Dio _dio;
// Singleton pattern
static final ApiService _instance = ApiService._internal();
factory ApiService() => _instance;
ApiService._internal() {
print('🏗️ [ApiService] Creating singleton instance');
_dio = Dio(BaseOptions(
baseUrl: baseUrl,
connectTimeout: const Duration(seconds: 30),
receiveTimeout: const Duration(seconds: 30),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
));
// Add interceptor to include auth token
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) async {
final token = await _getToken();
print('🔐 [ApiService] Token retrieved: ${token != null ? "YES (${token.substring(0, 20)}...)" : "NO - NULL!"}');
print('🔐 [ApiService] Request: ${options.method} ${options.path}');
if (token != null) {
options.headers['Authorization'] = 'Bearer $token';
print('🔐 [ApiService] Authorization header added');
} else {
print('⚠️ [ApiService] WARNING: No token found! Request will fail!');
}
handler.next(options);
},
onError: (error, handler) {
print('❌ [ApiService] Error: ${error.response?.statusCode} - ${error.message}');
if (error.response?.statusCode == 401) {
print('❌ [ApiService] 401 Unauthorized - Clearing token');
// Token expired or invalid
_clearToken();
}
handler.next(error);
},
));
}
// Token management
Future<String?> _getToken() async {
try {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString(tokenKey);
print('🔑 [ApiService._getToken] Looking for key: "$tokenKey"');
print('🔑 [ApiService._getToken] Token found: ${token != null}');
if (token != null) {
print('🔑 [ApiService._getToken] Token length: ${token.length}');
print('🔑 [ApiService._getToken] Token starts with: ${token.substring(0, token.length > 20 ? 20 : token.length)}...');
}
return token;
} catch (e) {
print('❌ [ApiService._getToken] Error: $e');
return null;
}
}
Future<void> _saveToken(String token) async {
try {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(tokenKey, token);
print('✅ [ApiService._saveToken] Token saved with key: "$tokenKey"');
print('✅ [ApiService._saveToken] Token length: ${token.length}');
} catch (e) {
print('❌ [ApiService._saveToken] Error: $e');
}
}
Future<void> _clearToken() async {
try {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(tokenKey);
print('🗑️ [ApiService._clearToken] Token cleared');
} catch (e) {
print('❌ [ApiService._clearToken] Error: $e');
}
}
// Authentication APIs
Future<Map<String, dynamic>> signup(String mobileNumber, String fullName, String password, {String? email, String? address, String? emergencyContact}) async {
try {
final data = {
'mobile_number': mobileNumber,
'full_name': fullName,
'password': password,
};
if (email != null && email.isNotEmpty) data['email'] = email;
if (address != null && address.isNotEmpty) data['address'] = address;
if (emergencyContact != null && emergencyContact.isNotEmpty) data['emergency_contact'] = emergencyContact;
final response = await _dio.post('/auth/signup', data: data);
if (response.data['success']) {
await _saveToken(response.data['data']['token']);
}
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> login(String mobileNumber, String password) async {
try {
final response = await _dio.post('/auth/login', data: {
'mobile_number': mobileNumber,
'password': password,
});
if (response.data['success']) {
await _saveToken(response.data['data']['token']);
}
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getProfile() async {
try {
final response = await _dio.get('/auth/profile');
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> updateProfile(Map<String, dynamic> data) async {
try {
final response = await _dio.put('/auth/profile', data: data);
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> changePassword(String currentPassword, String newPassword) async {
try {
final response = await _dio.put('/auth/change-password', data: {
'current_password': currentPassword,
'new_password': newPassword,
});
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> createMember(String mobileNumber, String fullName, {String? email, String? address, String? emergencyContact}) async {
try {
final data = {
'mobile_number': mobileNumber,
'full_name': fullName,
};
if (email != null && email.isNotEmpty) data['email'] = email;
if (address != null && address.isNotEmpty) data['address'] = address;
if (emergencyContact != null && emergencyContact.isNotEmpty) data['emergency_contact'] = emergencyContact;
final response = await _dio.post('/auth/create-member', data: data);
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
// Chit Group APIs
Future<Map<String, dynamic>> createChitGroup(Map<String, dynamic> data) async {
try {
final response = await _dio.post('/chit-groups', data: data);
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getManagerChitGroups({String? status, int page = 1, int limit = 10}) async {
try {
final response = await _dio.get('/chit-groups/manager', queryParameters: {
if (status != null) 'status': status,
'page': page,
'limit': limit,
});
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getMemberChitGroups({String? status, int page = 1, int limit = 10}) async {
try {
final response = await _dio.get('/chit-groups/member', queryParameters: {
if (status != null) 'status': status,
'page': page,
'limit': limit,
});
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getChitGroupDetails(String groupId) async {
try {
final response = await _dio.get('/chit-groups/$groupId');
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> updateChitGroup(String groupId, Map<String, dynamic> data) async {
try {
final response = await _dio.put('/chit-groups/$groupId', data: data);
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> deleteChitGroup(String groupId) async {
try {
final response = await _dio.delete('/chit-groups/$groupId');
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> startChitGroup(String groupId) async {
try {
final response = await _dio.post('/chit-groups/$groupId/start');
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getChitGroupStats(String groupId) async {
try {
final response = await _dio.get('/chit-groups/$groupId/stats');
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
// Member Management APIs
Future<Map<String, dynamic>> addMemberToGroup(String groupId, Map<String, dynamic> memberData) async {
try {
final response = await _dio.post('/members/$groupId/members', data: memberData);
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> removeMemberFromGroup(String groupId, String memberId) async {
try {
final response = await _dio.delete('/members/$groupId/members/$memberId');
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getAllUsers({String? search, int page = 1, int limit = 50}) async {
try {
final queryParams = <String, dynamic>{
'page': page,
'limit': limit,
};
if (search != null && search.isNotEmpty) {
queryParams['search'] = search;
}
final response = await _dio.get('/members/users', queryParameters: queryParams);
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getAvailableUsersForGroup(String groupId) async {
try {
final response = await _dio.get('/members/users/available/$groupId');
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getGroupMembers(String groupId, {String? status, int page = 1, int limit = 20}) async {
try {
final response = await _dio.get('/members/$groupId/members', queryParameters: {
if (status != null) 'status': status,
'page': page,
'limit': limit,
});
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getMemberDetails(String groupId, String memberId) async {
try {
final response = await _dio.get('/members/$groupId/members/$memberId');
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> updateMemberStatus(String groupId, String memberId, String status) async {
try {
final response = await _dio.put('/members/$groupId/members/$memberId/status', data: {
'status': status,
});
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getMemberPaymentSummary(String groupId, String memberId) async {
try {
final response = await _dio.get('/members/$groupId/members/$memberId/payment-summary');
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
// Payment Management APIs
Future<Map<String, dynamic>> recordPayment(Map<String, dynamic> data) async {
try {
final response = await _dio.post('/payments', data: data);
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getGroupPayments(String groupId, {String? status, int? month, int? year, int page = 1, int limit = 20}) async {
try {
final response = await _dio.get('/payments/group/$groupId', queryParameters: {
if (status != null) 'status': status,
if (month != null) 'month': month,
if (year != null) 'year': year,
'page': page,
'limit': limit,
});
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getMemberPayments(String groupId, String memberId, {String? status, int page = 1, int limit = 20}) async {
try {
final response = await _dio.get('/payments/group/$groupId/member/$memberId', queryParameters: {
if (status != null) 'status': status,
'page': page,
'limit': limit,
});
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> updatePaymentStatus(String paymentId, String status, {String? notes}) async {
try {
final response = await _dio.put('/payments/$paymentId/status', data: {
'status': status,
if (notes != null) 'notes': notes,
});
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getGroupPaymentSummary(String groupId) async {
try {
final response = await _dio.get('/payments/group/$groupId/summary');
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getPendingPayments(String groupId) async {
try {
final response = await _dio.get('/payments/group/$groupId/pending');
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
// Monthly Draw APIs
Future<Map<String, dynamic>> createMonthlyDraw(Map<String, dynamic> data) async {
try {
final response = await _dio.post('/monthly-draws', data: data);
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getGroupMonthlyDraws(String groupId, {String? status, int page = 1, int limit = 20}) async {
try {
final response = await _dio.get('/monthly-draws/group/$groupId', queryParameters: {
if (status != null) 'status': status,
'page': page,
'limit': limit,
});
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getMonthlyDrawDetails(String drawId) async {
try {
final response = await _dio.get('/monthly-draws/$drawId');
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> verifyDrawResult(String drawId, {String? clientSeed}) async {
try {
final response = await _dio.post('/monthly-draws/$drawId/verify', data: {
if (clientSeed != null) 'client_seed': clientSeed,
});
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getDrawStatistics(String groupId) async {
try {
final response = await _dio.get('/monthly-draws/group/$groupId/statistics');
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> updateMonthlyDraw(String drawId, Map<String, dynamic> data) async {
try {
final response = await _dio.put('/monthly-draws/$drawId', data: data);
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> deleteMonthlyDraw(String drawId) async {
try {
final response = await _dio.delete('/monthly-draws/$drawId');
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> getGroupFinancialData(String groupId) async {
try {
final response = await _dio.get('/chit-groups/$groupId/financial-data');
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> updateMemberDetails(String memberId, Map<String, dynamic> data) async {
try {
final response = await _dio.put('/auth/member/$memberId', data: data);
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
// Generic HTTP Methods
Future<Map<String, dynamic>> get(String path, {Map<String, dynamic>? queryParameters}) async {
try {
final response = await _dio.get(path, queryParameters: queryParameters);
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> post(String path, Map<String, dynamic> data) async {
try {
final response = await _dio.post(path, data: data);
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> put(String path, Map<String, dynamic> data) async {
try {
final response = await _dio.put(path, data: data);
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
Future<Map<String, dynamic>> delete(String path) async {
try {
final response = await _dio.delete(path);
return response.data;
} on DioException catch (e) {
return _handleError(e);
}
}
// Logout
Future<void> logout() async {
await _clearToken();
}
// Debug: Check if token exists
Future<bool> hasToken() async {
final token = await _getToken();
return token != null;
}
// Debug: Get current token (for debugging)
Future<String?> getCurrentToken() async {
return await _getToken();
}
// Error handling
Map<String, dynamic> _handleError(DioException e) {
if (e.response != null) {
final responseData = e.response?.data;
String message = 'Request failed';
if (responseData is Map<String, dynamic>) {
message = responseData['message']?.toString() ?? 'Request failed';
} else if (responseData is String) {
message = responseData;
}
return {
'success': false,
'message': message,
'statusCode': e.response?.statusCode,
};
} else {
return {
'success': false,
'message': 'Network error: ${e.message}',
'statusCode': 0,
};
}
}
}