mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2026-03-10 18:41:46 +00:00
feat: enhance card service and transaction notification for shared account handling
- Updated CardService to differentiate between shared and separate accounts during card control updates, optimizing balance allocation. - Enhanced TransactionNotificationListener to accurately reflect balance based on account structure for internal transfers and top-ups. - Improved logging for better traceability of account operations and balance calculations.
This commit is contained in:
@ -240,16 +240,37 @@ export class CardService {
|
|||||||
|
|
||||||
this.logger.debug(`Updating card control - cardReference: ${card.cardReference}, finalAmount: ${finalAmountNumber}`);
|
this.logger.debug(`Updating card control - cardReference: ${card.cardReference}, finalAmount: ${finalAmountNumber}`);
|
||||||
|
|
||||||
|
// Check if child and parent share the same account
|
||||||
|
const isSharedAccount = card.parentId && card.account.id === fundingAccount.id;
|
||||||
|
|
||||||
|
this.logger.debug(
|
||||||
|
`Account structure - Child account: ${card.account.id}, Parent account: ${fundingAccount.id}, ` +
|
||||||
|
`Shared: ${isSharedAccount ? 'YES' : 'NO'}`
|
||||||
|
);
|
||||||
|
|
||||||
// First, ensure all external operations succeed before creating transaction
|
// First, ensure all external operations succeed before creating transaction
|
||||||
|
if (isSharedAccount) {
|
||||||
|
// Shared account: Only update card limit and reserved balance
|
||||||
|
// Money is already in the shared account, just allocate it to the child
|
||||||
|
this.logger.debug(`Shared account detected - only updating card limit and reserved balance`);
|
||||||
|
await Promise.all([
|
||||||
|
this.neoleapService.updateCardControl(card.cardReference, finalAmountNumber),
|
||||||
|
this.updateCardLimit(card.id, finalAmountNumber),
|
||||||
|
this.accountService.increaseReservedBalance(fundingAccount, amount),
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
// Separate accounts: Transfer money from parent to child
|
||||||
|
this.logger.debug(`Separate accounts - transferring money from parent to child`);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.neoleapService.updateCardControl(card.cardReference, finalAmountNumber),
|
this.neoleapService.updateCardControl(card.cardReference, finalAmountNumber),
|
||||||
this.updateCardLimit(card.id, finalAmountNumber),
|
this.updateCardLimit(card.id, finalAmountNumber),
|
||||||
this.accountService.increaseReservedBalance(fundingAccount, amount),
|
this.accountService.increaseReservedBalance(fundingAccount, amount),
|
||||||
// Increase child account balance
|
// Increase child account balance
|
||||||
this.accountService.creditAccountBalance(card.account.accountReference, amount),
|
this.accountService.creditAccountBalance(card.account.accountReference, amount),
|
||||||
// Decrease parent account balance (only if parent is funding)
|
// Decrease parent account balance
|
||||||
card.parentId ? this.accountService.decreaseAccountBalance(fundingAccount.accountReference, amount) : Promise.resolve(),
|
this.accountService.decreaseAccountBalance(fundingAccount.accountReference, amount),
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
// Only create transaction and emit event after all operations succeed
|
// Only create transaction and emit event after all operations succeed
|
||||||
await this.transactionService.createInternalChildTransaction(card.id, amount);
|
await this.transactionService.createInternalChildTransaction(card.id, amount);
|
||||||
|
|||||||
@ -111,17 +111,53 @@ export class TransactionNotificationListener {
|
|||||||
const amount = transaction.transactionAmount;
|
const amount = transaction.transactionAmount;
|
||||||
const merchant = transaction.merchantName || 'merchant';
|
const merchant = transaction.merchantName || 'merchant';
|
||||||
|
|
||||||
// For child notifications, show the child's account balance (available balance)
|
// For child notifications, show the appropriate balance based on account structure
|
||||||
let balance = 0;
|
let balance = 0;
|
||||||
let accountCurrency: string | undefined;
|
let accountCurrency: string | undefined;
|
||||||
|
|
||||||
if (isTopUp) {
|
if (isTopUp && isChildSpending) {
|
||||||
// For top-up: show child's account balance after the transfer
|
// Internal transfer: For shared accounts, show card limit (child's spending power)
|
||||||
|
// For separate accounts, show child's account balance
|
||||||
|
try {
|
||||||
|
// Reload card to get updated data
|
||||||
|
const cardWithUpdatedBalance = await this.cardService.getCardById(card.id);
|
||||||
|
|
||||||
|
// Check if child has parent (shared account scenario)
|
||||||
|
if (cardWithUpdatedBalance.parentId) {
|
||||||
|
// Likely shared account - use card limit as the child's "balance"
|
||||||
|
balance = cardWithUpdatedBalance.limit || card.limit || 0;
|
||||||
|
accountCurrency = cardWithUpdatedBalance.account?.currency || card.account?.currency;
|
||||||
|
this.logger.debug(
|
||||||
|
`[Child Internal Transfer] Shared account - using card limit: ${balance} ${accountCurrency}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Separate account - use child's account balance
|
||||||
|
if (cardWithUpdatedBalance?.account?.accountReference) {
|
||||||
|
const account = await this.accountService.getAccountByReferenceNumber(
|
||||||
|
cardWithUpdatedBalance.account.accountReference
|
||||||
|
);
|
||||||
|
balance = account.balance;
|
||||||
|
accountCurrency = account.currency;
|
||||||
|
this.logger.debug(
|
||||||
|
`[Child Internal Transfer] Separate account - using account balance: ${balance} ${accountCurrency}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
balance = cardWithUpdatedBalance.account?.balance || card.account?.balance || 0;
|
||||||
|
accountCurrency = cardWithUpdatedBalance.account?.currency || card.account?.currency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.warn(
|
||||||
|
`[Child Internal Transfer] Could not fetch balance: ${error?.message}. Using card limit.`
|
||||||
|
);
|
||||||
|
balance = card.limit || 0;
|
||||||
|
accountCurrency = card.account?.currency;
|
||||||
|
}
|
||||||
|
} else if (isTopUp) {
|
||||||
|
// External top-up: show child's account balance
|
||||||
try {
|
try {
|
||||||
// Reload card to get account reference with fresh balance
|
|
||||||
const cardWithUpdatedBalance = await this.cardService.getCardById(card.id);
|
const cardWithUpdatedBalance = await this.cardService.getCardById(card.id);
|
||||||
if (cardWithUpdatedBalance?.account?.accountReference) {
|
if (cardWithUpdatedBalance?.account?.accountReference) {
|
||||||
// Fetch by reference number to get fresh balance from database
|
|
||||||
const account = await this.accountService.getAccountByReferenceNumber(
|
const account = await this.accountService.getAccountByReferenceNumber(
|
||||||
cardWithUpdatedBalance.account.accountReference
|
cardWithUpdatedBalance.account.accountReference
|
||||||
);
|
);
|
||||||
@ -131,16 +167,12 @@ export class TransactionNotificationListener {
|
|||||||
`[Child Top-Up Notification] Fetched account by reference - balance: ${balance} ${accountCurrency}`
|
`[Child Top-Up Notification] Fetched account by reference - balance: ${balance} ${accountCurrency}`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Fallback: use card's account balance
|
|
||||||
balance = cardWithUpdatedBalance.account?.balance || card.account?.balance || 0;
|
balance = cardWithUpdatedBalance.account?.balance || card.account?.balance || 0;
|
||||||
accountCurrency = cardWithUpdatedBalance.account?.currency || card.account?.currency;
|
accountCurrency = cardWithUpdatedBalance.account?.currency || card.account?.currency;
|
||||||
this.logger.debug(
|
|
||||||
`[Child Top-Up Notification] Using card account balance - balance: ${balance} ${accountCurrency}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.warn(
|
this.logger.warn(
|
||||||
`[Child Top-Up Notification] Could not fetch account by reference: ${error?.message}. Using card account balance.`
|
`[Child Top-Up Notification] Could not fetch account: ${error?.message}. Using card balance.`
|
||||||
);
|
);
|
||||||
balance = card.account?.balance || 0;
|
balance = card.account?.balance || 0;
|
||||||
accountCurrency = card.account?.currency;
|
accountCurrency = card.account?.currency;
|
||||||
|
|||||||
Reference in New Issue
Block a user