UNPKG

saksh-wallet

Version:

A Node.js library for managing user wallets, including functionalities for crediting, debiting, and converting currencies, as well as retrieving balances and transaction reports.

193 lines (153 loc) 7.06 kB
const EventEmitter = require('events'); const mongoose = require('mongoose'); const Transaction = require('./models/Transaction'); const WalletUser = require('./models/WalletUser'); const RecurringPayment = require('./models/RecurringPayment'); class SakshTransactionManagement { constructor(userManagement, transactionModel = Transaction) { this.userManagement = userManagement; this.Transaction = transactionModel; } async sakshUpdateDailyLimit(user, amount) { const today = new Date().setHours(0, 0, 0, 0); if (new Date(user.lastTransactionDate).setHours(0, 0, 0, 0) !== today) { user.dailyTotal = 0; user.lastTransactionDate = Date.now(); } if (user.dailyTotal + amount > user.dailyLimit) { throw new Error('Daily transaction limit exceeded'); } user.dailyTotal += amount; } async sakshUpdateBalance(userId, amount, currency, type, description, referenceNumber, category) { if (amount <= 0) { throw new Error('Amount must be greater than zero'); } const session = await mongoose.startSession(); session.startTransaction(); try { let user = await this.userManagement.User.findOne({ userId }).session(session); if (!user) { user = new this.userManagement.User({ userId }); } if (!this.nolimit && user.applyDailyLimit) { await this.sakshUpdateDailyLimit(user, amount); } const currentBalance = await this.userManagement.sakshGetBalance(userId, currency); if (type === 'debit' && currentBalance < amount) { throw new Error('Insufficient funds'); } let newBalance; if (type === 'credit') { newBalance = currentBalance + amount; } else if (type === 'debit') { newBalance = currentBalance - amount; } else { throw new Error('Invalid transaction type'); } user.balances.set(currency, newBalance); await user.save({ session }); const transaction = new this.Transaction({ userId, type, amount, currency, description, referenceNumber, category }); await transaction.save({ session }); await session.commitTransaction(); session.endSession(); return newBalance; } catch (error) { await session.abortTransaction(); session.endSession(); throw error; } } async sakshCredit(userId, amount, currency, description, referenceNumber, category) { return this.sakshUpdateBalance(userId, amount, currency, 'credit', description, referenceNumber, category); } async sakshDebit(userId, amount, currency, description, referenceNumber, category) { await this.sakshCheckBudget(userId, category, amount); return this.sakshUpdateBalance(userId, amount, currency, 'debit', description, referenceNumber, category); } async sakshTransferFunds(fromUserId, toUserId, amount, currency, description, referenceNumber, category, customFeeCallback = null) { const session = await mongoose.startSession(); session.startTransaction(); try { let feeAmount = 0; if (customFeeCallback) { feeAmount = customFeeCallback(amount); } const totalDebitAmount = amount + feeAmount; await this.sakshDebit(fromUserId, totalDebitAmount, currency, `${description}${feeAmount > 0 ? ' (including fee)' : ''}`, referenceNumber, category); await this.sakshCredit(toUserId, amount, currency, description, referenceNumber, category); if (feeAmount > 0) { await this.sakshCredit(this.adminUserId, feeAmount, currency, `Transfer fee from ${fromUserId} to ${toUserId}`, referenceNumber, category); } await session.commitTransaction(); session.endSession(); return { fromUserId, toUserId, amount, currency, feeAmount, adminUserId: this.adminUserId }; } catch (error) { await session.abortTransaction(); session.endSession(); throw error; } } async sakshGetTransactionReport(userId) { return await this.Transaction.find({ userId }); } async sakshReverseTransaction(transactionId, session) { const originalTransaction = await this.Transaction.findById(transactionId).session(session); if (!originalTransaction) { throw new Error('Transaction not found'); } const { userId, type, amount, currency, description, referenceNumber } = originalTransaction; let user = await this.userManagement.User.findOne({ userId }).session(session); if (!user) { user = new this.userManagement.User({ userId }); } if (type === 'credit') { if (!user.balances.has(currency) || user.balances.get(currency) < amount) { throw new Error('Insufficient funds to reverse the transaction'); } user.balances.set(currency, user.balances.get(currency) - amount); } else if (type === 'debit') { user.balances.set(currency, (user.balances.get(currency) || 0) + amount); } else { throw new Error('Invalid transaction type'); } await user.save({ session }); const reversedTransaction = new this.Transaction({ userId, type: type === 'credit' ? 'debit' : 'credit', amount, currency, description: `Reversal of transaction ${transactionId}`, referenceNumber: `REV-${referenceNumber}`, }); await reversedTransaction.save({ session }); return reversedTransaction; } async sakshReverseMultipleTransactions(transactionIds) { const session = await mongoose.startSession(); session.startTransaction(); try { const reversedTransactions = []; for (const transactionId of transactionIds) { const reversedTransaction = await this.sakshReverseTransaction(transactionId, session); reversedTransactions.push(reversedTransaction); } await session.commitTransaction(); session.endSession(); return reversedTransactions; } catch (error) { await session.abortTransaction(); session.endSession(); throw error; } } } module.exports = SakshTransactionManagement;