expo-finance-kit
Version:
Native Expo module for Apple FinanceKit - Access financial data from Apple Card and other accounts
190 lines • 7.51 kB
JavaScript
/**
* Balance management module for Expo Finance Kit
* Handles fetching and managing account balance data
*/
import ExpoFinanceKit from '../ExpoFinanceKitModule';
import { FinanceKitErrorCode } from '../ExpoFinanceKit.types';
import { ensureAuthorized } from '../helpers';
import { validateBalanceQueryOptions, transformBalance } from '../utils/validators';
import { createFinanceKitError } from '../utils/errors';
/**
* Fetches account balances
* @param options - Query options for filtering balances
* @returns Promise resolving to array of account balances
*/
export async function getBalances(options) {
const isAuthorized = await ensureAuthorized();
if (!isAuthorized) {
throw createFinanceKitError(FinanceKitErrorCode.Unauthorized, 'User has not authorized access to financial data');
}
if (options) {
validateBalanceQueryOptions(options);
}
try {
const balances = await ExpoFinanceKit.getBalances();
let filteredBalances = balances.map(transformBalance);
// Apply filters
if (options?.accountIds && options.accountIds.length > 0) {
filteredBalances = filteredBalances.filter((balance) => options.accountIds.includes(balance.accountId));
}
return filteredBalances;
}
catch (error) {
throw createFinanceKitError(FinanceKitErrorCode.Unknown, 'Failed to fetch balances', { originalError: error });
}
}
/**
* Fetches balance for a specific account
* @param accountId - The account ID to fetch balance for
* @returns Promise resolving to the account balance or null
*/
export async function getBalanceByAccount(accountId) {
if (!accountId || typeof accountId !== 'string') {
throw createFinanceKitError(FinanceKitErrorCode.InvalidAccountId, 'Invalid account ID provided');
}
const isAuthorized = await ensureAuthorized();
if (!isAuthorized) {
throw createFinanceKitError(FinanceKitErrorCode.Unauthorized, 'User has not authorized access to financial data');
}
try {
// Use the new native method that gets balance for specific account
const balance = await ExpoFinanceKit.getBalanceForAccount(accountId);
return balance ? transformBalance(balance) : null;
}
catch (error) {
// If the new method is not available, fall back to filtering
if (error && typeof error === 'object' && 'message' in error &&
typeof error.message === 'string' &&
error.message.includes('getBalanceForAccount')) {
const balances = await getBalances();
const filtered = balances.filter(b => b.accountId === accountId);
return filtered.length > 0 ? filtered[0] : null;
}
throw createFinanceKitError(FinanceKitErrorCode.Unknown, 'Failed to fetch balance for account', { originalError: error });
}
}
/**
* Fetches all balances and calculates total
* @returns Promise resolving to total balance across all accounts
*/
export async function getTotalBalance() {
const isAuthorized = await ensureAuthorized();
if (!isAuthorized) {
throw createFinanceKitError(FinanceKitErrorCode.Unauthorized, 'User has not authorized access to financial data');
}
try {
// First get all accounts
const accounts = await ExpoFinanceKit.getAccounts();
// Get current balance for each account
const balancePromises = accounts.map(async (account) => {
try {
const balance = await ExpoFinanceKit.getBalanceForAccount(account.id);
return balance ? transformBalance(balance) : null;
}
catch (error) {
console.warn(`Failed to get balance for account ${account.id}:`, error);
return null;
}
});
const balanceResults = await Promise.all(balancePromises);
const validBalances = balanceResults.filter((b) => b !== null);
const byCurrency = new Map();
let totalInUSD = 0; // Would need exchange rates for accurate conversion
validBalances.forEach(balance => {
const current = byCurrency.get(balance.currencyCode) || 0;
byCurrency.set(balance.currencyCode, current + balance.amount);
// For now, just sum all amounts (would need currency conversion)
totalInUSD += balance.amount;
});
return {
total: totalInUSD,
byCurrency,
accounts: validBalances,
};
}
catch (error) {
throw createFinanceKitError(FinanceKitErrorCode.Unknown, 'Failed to fetch total balance', { originalError: error });
}
}
/**
* Gets balance summary for all accounts
* @returns Promise resolving to balance summary
*/
export async function getBalanceSummary() {
const balances = await getBalances();
// In a real implementation, we'd correlate with account types
// For now, we'll use positive/negative balance as a proxy
const totalAssets = balances
.filter(b => b.amount > 0)
.reduce((sum, b) => sum + b.amount, 0);
const totalLiabilities = Math.abs(balances
.filter(b => b.amount < 0)
.reduce((sum, b) => sum + b.amount, 0));
return {
totalAssets,
totalLiabilities,
netWorth: totalAssets - totalLiabilities,
accountCount: balances.length,
lastUpdated: Date.now(),
};
}
/**
* Monitors balance changes over time
* @param accountId - Account to monitor
* @param callback - Callback function for balance updates
* @returns Function to stop monitoring
*/
export function monitorBalanceChanges(accountId, callback) {
let intervalId;
let lastBalance = null;
const checkBalance = async () => {
try {
const balance = await getBalanceByAccount(accountId);
if (balance && balance.amount !== lastBalance) {
lastBalance = balance.amount;
callback(balance);
}
}
catch (error) {
console.error('Error monitoring balance:', error);
}
};
// Check every 5 minutes
intervalId = setInterval(checkBalance, 5 * 60 * 1000);
// Initial check
checkBalance();
// Return cleanup function
return () => {
if (intervalId) {
clearInterval(intervalId);
}
};
}
/**
* Gets historical balance data (simulated)
* @param accountId - Account ID to get history for
* @param days - Number of days of history
* @returns Promise resolving to historical balance data
*/
export async function getBalanceHistory(accountId, days = 30) {
// This would typically fetch from a data source that tracks balance history
// For now, we'll simulate it
const currentBalance = await getBalanceByAccount(accountId);
if (!currentBalance) {
throw createFinanceKitError(FinanceKitErrorCode.AccountNotFound, 'Account not found');
}
const history = [];
const dailyChange = (Math.random() - 0.5) * 100; // Simulate daily changes
for (let i = days; i >= 0; i--) {
const date = new Date();
date.setDate(date.getDate() - i);
const variance = currentBalance.amount * 0.01 * (Math.random() - 0.5);
const balance = currentBalance.amount + (dailyChange * i) + variance;
history.push({
date: date.getTime(),
balance: Math.round(balance * 100) / 100,
});
}
return history;
}
//# sourceMappingURL=balances.js.map