539 lines
16 KiB
JavaScript
539 lines
16 KiB
JavaScript
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||
import { API_CONFIG } from '../constants/api';
|
||
|
||
class AuthService {
|
||
async getStoredToken() {
|
||
try {
|
||
return await AsyncStorage.getItem('auth_token');
|
||
} catch (error) {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
async makeRequest(endpoint, data, requiresAuth = false, method = 'POST') {
|
||
const fullUrl = `${API_CONFIG.BASE_URL}${endpoint}`;
|
||
const requestId = Math.random().toString(36).substr(2, 9);
|
||
|
||
try {
|
||
const headers = { ...API_CONFIG.HEADERS };
|
||
|
||
// Determine body and adjust headers for FormData
|
||
let bodyToSend;
|
||
if (data instanceof FormData) {
|
||
bodyToSend = data;
|
||
// Let fetch set correct Content-Type with boundary
|
||
if (headers['Content-Type']) delete headers['Content-Type'];
|
||
} else {
|
||
bodyToSend = data ? JSON.stringify(data) : undefined;
|
||
}
|
||
|
||
// Auto-include token for authenticated requests
|
||
if (requiresAuth) {
|
||
const token = await this.getStoredToken();
|
||
if (token) {
|
||
headers.Authorization = `Bearer ${token}`;
|
||
}
|
||
}
|
||
|
||
// Log request details
|
||
console.log(`\n🚀 [${requestId}] API REQUEST`);
|
||
console.log(`📍 URL: ${method} ${fullUrl}`);
|
||
console.log(`🔐 Auth Required: ${requiresAuth}`);
|
||
console.log(`📝 Headers:`, {
|
||
...headers,
|
||
Authorization: headers.Authorization ? `Bearer ${headers.Authorization.slice(-10)}...` : 'None'
|
||
});
|
||
if (data) {
|
||
console.log(`📦 Body:`, data);
|
||
}
|
||
console.log(`⏰ Time: ${new Date().toISOString()}`);
|
||
|
||
const startTime = Date.now();
|
||
|
||
const response = await fetch(fullUrl, {
|
||
method,
|
||
headers,
|
||
body: bodyToSend,
|
||
});
|
||
|
||
const endTime = Date.now();
|
||
const duration = endTime - startTime;
|
||
|
||
const result = await response.json();
|
||
|
||
// Log response details
|
||
console.log(`\n✅ [${requestId}] API RESPONSE`);
|
||
console.log(`📊 Status: ${response.status} ${response.statusText}`);
|
||
console.log(`⏱️ Duration: ${duration}ms`);
|
||
console.log(`📄 Response:`, result);
|
||
|
||
if (!response.ok) {
|
||
console.log(`❌ [${requestId}] API ERROR`);
|
||
console.log(`🔴 Status: ${response.status}`);
|
||
console.log(`💬 Error Message:`, result.message || 'Unknown error');
|
||
|
||
// Handle token expiration
|
||
if (response.status === 401 && requiresAuth) {
|
||
console.log(`🔓 [${requestId}] TOKEN EXPIRED - Clearing storage`);
|
||
await AsyncStorage.removeItem('auth_token');
|
||
await AsyncStorage.removeItem('user_data');
|
||
throw new Error('Session expired. Please login again.');
|
||
}
|
||
throw new Error(result.message || 'An error occurred');
|
||
}
|
||
|
||
console.log(`✨ [${requestId}] REQUEST COMPLETED SUCCESSFULLY\n`);
|
||
return result;
|
||
} catch (error) {
|
||
console.log(`\n💥 [${requestId}] API REQUEST FAILED`);
|
||
console.log(`📍 URL: ${method} ${fullUrl}`);
|
||
console.log(`🔴 Error:`, error.message);
|
||
console.log(`📅 Time: ${new Date().toISOString()}\n`);
|
||
|
||
throw new Error(error.message || 'Network error');
|
||
}
|
||
}
|
||
|
||
async login(phone, password) {
|
||
return this.makeRequest(API_CONFIG.ENDPOINTS.AUTH.LOGIN, {
|
||
phone: parseInt(phone),
|
||
password,
|
||
});
|
||
}
|
||
|
||
async register(phone, name, password) {
|
||
return this.makeRequest(API_CONFIG.ENDPOINTS.AUTH.REGISTER, {
|
||
phone: parseInt(phone),
|
||
name,
|
||
password,
|
||
});
|
||
}
|
||
|
||
async verify(phone, code) {
|
||
return this.makeRequest(API_CONFIG.ENDPOINTS.AUTH.VERIFY, {
|
||
phone: parseInt(phone),
|
||
code: parseInt(code),
|
||
});
|
||
}
|
||
|
||
// Authenticated API methods
|
||
async getProfile() {
|
||
return this.makeRequest('/profile', null, true, 'GET');
|
||
}
|
||
|
||
async updateProfile(data) {
|
||
return this.makeRequest('/profile', data, true, 'POST');
|
||
}
|
||
|
||
async getTransactions(page = 1, limit = 20) {
|
||
return this.makeRequest(`/user/transactions?page=${page}&limit=${limit}`, null, true, 'GET');
|
||
}
|
||
|
||
async getBalance() {
|
||
return this.makeRequest('/user/balance', null, true, 'GET');
|
||
}
|
||
|
||
// NEW: Fetch metrics for dashboard
|
||
async getMetrics() {
|
||
return this.makeRequest('/metrics', null, true, 'GET');
|
||
}
|
||
|
||
async transferMoney(data) {
|
||
return this.makeRequest('/user/transfer', data, true);
|
||
}
|
||
|
||
async payBill(data) {
|
||
return this.makeRequest('/user/pay-bill', data, true);
|
||
}
|
||
|
||
async getCards() {
|
||
return this.makeRequest('/user/cards', null, true, 'GET');
|
||
}
|
||
|
||
async addCard(data) {
|
||
return this.makeRequest('/user/cards', data, true);
|
||
}
|
||
|
||
async blockCard(cardId) {
|
||
return this.makeRequest(`/user/cards/${cardId}/block`, null, true);
|
||
}
|
||
|
||
async unblockCard(cardId) {
|
||
return this.makeRequest(`/user/cards/${cardId}/unblock`, null, true);
|
||
}
|
||
|
||
async changePassword(currentPassword, newPassword) {
|
||
return this.makeRequest('/user/change-password', {
|
||
current_password: currentPassword,
|
||
new_password: newPassword,
|
||
}, true);
|
||
}
|
||
|
||
async deleteAccount() {
|
||
return this.makeRequest('/user/delete-account', null, true, 'DELETE');
|
||
}
|
||
|
||
// ================================
|
||
// Loan Remaining Order (Karzyň galyndysy)
|
||
// ================================
|
||
|
||
// Helper to read cached user data (for passport details)
|
||
async getStoredUser() {
|
||
try {
|
||
const raw = await AsyncStorage.getItem('user_data');
|
||
return raw ? JSON.parse(raw) : null;
|
||
} catch (e) {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// LIST orders
|
||
async getLoanRemainingOrders() {
|
||
return this.makeRequest('/loan-remaining-order', null, true, 'GET');
|
||
}
|
||
|
||
// CREATE order (passport can be supplied or fetched from profile)
|
||
async createLoanRemainingOrder(accountNumber, passportSerie = null, passportId = null) {
|
||
let serie = passportSerie;
|
||
let pid = passportId;
|
||
if (!serie || !pid) {
|
||
const user = await this.getStoredUser();
|
||
serie = serie || user?.passport_serie;
|
||
pid = pid || user?.passport_id;
|
||
}
|
||
if (!serie || !pid) {
|
||
throw new Error('Passport details are missing');
|
||
}
|
||
const payload = {
|
||
passport_serie: serie,
|
||
passport_id: pid,
|
||
account_number: accountNumber,
|
||
};
|
||
return this.makeRequest('/loan-remaining-order', payload, true, 'POST');
|
||
}
|
||
|
||
// SHOW order details
|
||
async getLoanRemainingOrder(orderId) {
|
||
return this.makeRequest(`/loan-remaining-order/${orderId}`, null, true, 'GET');
|
||
}
|
||
|
||
// UPDATE order (only account number can change; passport details stay the same)
|
||
async updateLoanRemainingOrder(orderId, accountNumber, passportSerie = null, passportId = null) {
|
||
let serie = passportSerie;
|
||
let pid = passportId;
|
||
if (!serie || !pid) {
|
||
const user = await this.getStoredUser();
|
||
serie = serie || user?.passport_serie;
|
||
pid = pid || user?.passport_id;
|
||
}
|
||
if (!serie || !pid) {
|
||
throw new Error('Passport details are missing');
|
||
}
|
||
const payload = {
|
||
passport_serie: serie,
|
||
passport_id: pid,
|
||
account_number: accountNumber,
|
||
};
|
||
return this.makeRequest(`/loan-remaining-order/${orderId}`, payload, true, 'POST');
|
||
}
|
||
|
||
// DELETE order
|
||
async deleteLoanRemainingOrder(orderId) {
|
||
return this.makeRequest(`/loan-remaining-order/${orderId}`, null, true, 'DELETE');
|
||
}
|
||
|
||
// ================================
|
||
// Loan remaining quick check (returns remaining balance info)
|
||
// ================================
|
||
|
||
async getLoanRemainingBalance(accountNumber, passportSerie = null, passportId = null) {
|
||
let serie = passportSerie;
|
||
let pid = passportId;
|
||
|
||
if (!serie || !pid) {
|
||
const user = await this.getStoredUser();
|
||
serie = user?.passport_serie;
|
||
pid = user?.passport_id;
|
||
}
|
||
|
||
if (!serie || !pid) {
|
||
throw new Error('Passport details are missing');
|
||
}
|
||
|
||
const query = `passport_serie=${encodeURIComponent(serie)}&passport_id=${encodeURIComponent(pid)}&account_number=${encodeURIComponent(accountNumber)}`;
|
||
return this.makeRequest(`/loan-remaining?${query}`, null, true, 'GET');
|
||
}
|
||
|
||
// ================================
|
||
// Loan Paid-Off Letter Orders
|
||
// ================================
|
||
|
||
// LIST
|
||
async getLoanPaidOffLetterOrders() {
|
||
return this.makeRequest('/loan-paid-off-letter-orders', null, true, 'GET');
|
||
}
|
||
|
||
// CREATE
|
||
async createLoanPaidOffLetterOrder(data) {
|
||
return this.makeRequest('/loan-paid-off-letter-orders', data, true, 'POST');
|
||
}
|
||
|
||
// SHOW
|
||
async getLoanPaidOffLetterOrder(orderId) {
|
||
return this.makeRequest(`/loan-paid-off-letter-orders/${orderId}`, null, true, 'GET');
|
||
}
|
||
|
||
// UPDATE
|
||
async updateLoanPaidOffLetterOrder(orderId, data) {
|
||
return this.makeRequest(`/loan-paid-off-letter-orders/${orderId}`, data, true, 'POST');
|
||
}
|
||
|
||
// DELETE
|
||
async deleteLoanPaidOffLetterOrder(orderId) {
|
||
return this.makeRequest(`/loan-paid-off-letter-orders/${orderId}`, null, true, 'DELETE');
|
||
}
|
||
|
||
// ================================
|
||
// Loan Orders (Karz sargytlary)
|
||
// ================================
|
||
|
||
// LIST
|
||
async getLoanOrders() {
|
||
return this.makeRequest('/loan-order', null, true, 'GET');
|
||
}
|
||
|
||
// CREATE
|
||
async createLoanOrder(data) {
|
||
return this.makeRequest('/loan-order', data, true, 'POST');
|
||
}
|
||
|
||
// SHOW
|
||
async getLoanOrder(orderId) {
|
||
return this.makeRequest(`/loan-order/${orderId}`, null, true, 'GET');
|
||
}
|
||
|
||
// UPDATE
|
||
async updateLoanOrder(orderId, data) {
|
||
return this.makeRequest(`/loan-order/${orderId}`, data, true, 'POST');
|
||
}
|
||
|
||
// DELETE
|
||
async deleteLoanOrder(orderId) {
|
||
return this.makeRequest(`/loan-order/${orderId}`, null, true, 'DELETE');
|
||
}
|
||
|
||
// ================================
|
||
// Card Transactions Orders (Kart hereketleri)
|
||
// ================================
|
||
|
||
// LIST
|
||
async getCardTransactionOrders() {
|
||
return this.makeRequest('/card-transactions', null, true, 'GET');
|
||
}
|
||
|
||
// CREATE order (auto-fills passport data from stored profile)
|
||
async createCardTransactionOrder(cardNumber, cardMonth, cardYear, passportSerie = null, passportId = null) {
|
||
let serie = passportSerie;
|
||
let pid = passportId;
|
||
if (!serie || !pid) {
|
||
const user = await this.getStoredUser();
|
||
serie = serie || user?.passport_serie;
|
||
pid = pid || user?.passport_id;
|
||
}
|
||
if (!serie || !pid) {
|
||
throw new Error('Passport details are missing');
|
||
}
|
||
const payload = {
|
||
passport_serie: serie,
|
||
passport_id: pid,
|
||
card_number: cardNumber,
|
||
card_month: cardMonth,
|
||
card_year: cardYear,
|
||
};
|
||
return this.makeRequest('/card-transactions', payload, true, 'POST');
|
||
}
|
||
|
||
// SHOW
|
||
async getCardTransactionOrder(orderId) {
|
||
return this.makeRequest(`/card-transactions/${orderId}`, null, true, 'GET');
|
||
}
|
||
|
||
// UPDATE
|
||
async updateCardTransactionOrder(orderId, cardNumber, cardMonth, cardYear, passportSerie = null, passportId = null) {
|
||
let serie = passportSerie;
|
||
let pid = passportId;
|
||
if (!serie || !pid) {
|
||
const user = await this.getStoredUser();
|
||
serie = serie || user?.passport_serie;
|
||
pid = pid || user?.passport_id;
|
||
}
|
||
if (!serie || !pid) {
|
||
throw new Error('Passport details are missing');
|
||
}
|
||
const payload = {
|
||
passport_serie: serie,
|
||
passport_id: pid,
|
||
card_number: cardNumber,
|
||
card_month: cardMonth,
|
||
card_year: cardYear,
|
||
};
|
||
return this.makeRequest(`/card-transactions/${orderId}`, payload, true, 'POST');
|
||
}
|
||
|
||
// DELETE
|
||
async deleteCardTransactionOrder(orderId) {
|
||
return this.makeRequest(`/card-transactions/${orderId}`, null, true, 'DELETE');
|
||
}
|
||
|
||
// DOWNLOAD (returns object with url)
|
||
async downloadCardTransactions(orderId, startDate, endDate) {
|
||
const query = `start_date=${encodeURIComponent(startDate)}&end_date=${encodeURIComponent(endDate)}`;
|
||
return this.makeRequest(`/card-transactions-download/${orderId}?${query}`, null, true, 'GET');
|
||
}
|
||
|
||
// ================================
|
||
// Card balance quick check (Kart galyndysy)
|
||
// ================================
|
||
|
||
async getCardBalanceQuickCheck(cardNumber = null, cardMonth = null, cardYear = null, passportSerie = null, passportId = null) {
|
||
// Fallback to stored user profile for missing fields
|
||
let serie = passportSerie;
|
||
let pid = passportId;
|
||
let cNumber = cardNumber;
|
||
let cMonth = cardMonth;
|
||
let cYear = cardYear;
|
||
|
||
if (!serie || !pid || !cNumber || !cMonth || !cYear) {
|
||
const user = await this.getStoredUser();
|
||
serie = serie || user?.passport_serie;
|
||
pid = pid || user?.passport_id;
|
||
cNumber = cNumber || user?.card_number;
|
||
cMonth = cMonth || user?.card_month;
|
||
cYear = cYear || user?.card_year;
|
||
}
|
||
|
||
if (!serie || !pid || !cNumber || !cMonth || !cYear) {
|
||
throw new Error('Card or passport details are missing');
|
||
}
|
||
|
||
// Ensure values are properly formatted
|
||
const plainCardNumber = String(cNumber).replace(/[^0-9]/g, '').slice(0, 16);
|
||
const monthStr = String(cMonth).padStart(2, '0');
|
||
|
||
const payload = {
|
||
passport_serie: serie,
|
||
passport_id: pid,
|
||
card_number: plainCardNumber,
|
||
card_month: monthStr,
|
||
card_year: String(cYear),
|
||
};
|
||
|
||
// POST request – authenticated (token header will be included if available)
|
||
return this.makeRequest('/card-balance-quick-check', payload, true, 'POST');
|
||
}
|
||
|
||
// ================================
|
||
// Card Balance Orders (Kart galyndylary)
|
||
// ================================
|
||
|
||
// LIST
|
||
async getCardBalanceOrders() {
|
||
return this.makeRequest('/card-balances', null, true, 'GET');
|
||
}
|
||
|
||
// CREATE
|
||
async createCardBalanceOrder(cardNumber, cardMonth, cardYear, passportSerie = null, passportId = null) {
|
||
let serie = passportSerie;
|
||
let pid = passportId;
|
||
if (!serie || !pid) {
|
||
const user = await this.getStoredUser();
|
||
serie = serie || user?.passport_serie;
|
||
pid = pid || user?.passport_id;
|
||
}
|
||
if (!serie || !pid) {
|
||
throw new Error('Passport details are missing');
|
||
}
|
||
const payload = {
|
||
passport_serie: serie,
|
||
passport_id: pid,
|
||
card_number: cardNumber,
|
||
card_month: cardMonth,
|
||
card_year: cardYear,
|
||
};
|
||
return this.makeRequest('/card-balances', payload, true, 'POST');
|
||
}
|
||
|
||
// SHOW
|
||
async getCardBalanceOrder(orderId) {
|
||
return this.makeRequest(`/card-balances/${orderId}`, null, true, 'GET');
|
||
}
|
||
|
||
// UPDATE
|
||
async updateCardBalanceOrder(orderId, cardNumber, cardMonth, cardYear, passportSerie = null, passportId = null) {
|
||
let serie = passportSerie;
|
||
let pid = passportId;
|
||
if (!serie || !pid) {
|
||
const user = await this.getStoredUser();
|
||
serie = serie || user?.passport_serie;
|
||
pid = pid || user?.passport_id;
|
||
}
|
||
if (!serie || !pid) {
|
||
throw new Error('Passport details are missing');
|
||
}
|
||
const payload = {
|
||
passport_serie: serie,
|
||
passport_id: pid,
|
||
card_number: cardNumber,
|
||
card_month: cardMonth,
|
||
card_year: cardYear,
|
||
};
|
||
return this.makeRequest(`/card-balances/${orderId}`, payload, true, 'POST');
|
||
}
|
||
|
||
// DELETE
|
||
async deleteCardBalanceOrder(orderId) {
|
||
return this.makeRequest(`/card-balances/${orderId}`, null, true, 'DELETE');
|
||
}
|
||
|
||
// DOWNLOAD (returns object with card details)
|
||
async downloadCardBalances(orderId) {
|
||
return this.makeRequest(`/card-balances-download/${orderId}`, null, true, 'GET');
|
||
}
|
||
|
||
// ================================
|
||
// Card Requisites Orders (Kart rekwizitler)
|
||
// ================================
|
||
|
||
// LIST
|
||
async getCardRequisiteOrders() {
|
||
return this.makeRequest('/card-requisites', null, true, 'GET');
|
||
}
|
||
|
||
// CREATE (passport + card info)
|
||
async createCardRequisiteOrder(payload) {
|
||
/* payload expected keys: passport_serie, passport_id, card_number, card_month, card_year, maybe card_name */
|
||
return this.makeRequest('/card-requisites', payload, true, 'POST');
|
||
}
|
||
|
||
// SHOW
|
||
async getCardRequisiteOrder(orderId) {
|
||
return this.makeRequest(`/card-requisites/${orderId}`, null, true, 'GET');
|
||
}
|
||
|
||
// UPDATE
|
||
async updateCardRequisiteOrder(orderId, payload) {
|
||
return this.makeRequest(`/card-requisites/${orderId}`, payload, true, 'POST');
|
||
}
|
||
|
||
// DELETE
|
||
async deleteCardRequisiteOrder(orderId) {
|
||
return this.makeRequest(`/card-requisites/${orderId}`, null, true, 'DELETE');
|
||
}
|
||
|
||
// DOWNLOAD
|
||
async downloadCardRequisites(orderId) {
|
||
return this.makeRequest(`/card-requisites-download/${orderId}`, null, true, 'GET');
|
||
}
|
||
}
|
||
|
||
export default new AuthService();
|