fix(payments): prevent auto transaction creation, enable sms by default, and fix transaction sms context

This commit is contained in:
2026-08-21 15:17:41 +03:30
parent 30db6582b7
commit ada1b624b1
3 changed files with 645 additions and 19 deletions
+9 -18
View File
@@ -41,7 +41,7 @@ const PAYMENT_STATUS_LABELS = {
};
const formatPaymentContext = async (payment) => {
const user = await User.findById(payment.user).select('name phoneNumber email').lean();
const user = await User.findById(payment.user?._id || payment.user).select('name phoneNumber email').lean();
let courseName = '';
let schedule = { classStartDate: '', classDays: '', courseTime: '' };
@@ -97,9 +97,9 @@ const notifyPaymentStatusChanged = async (payment, newStatus, oldStatus) => {
}
};
const notifyTransactionRecorded = async (payment, transaction) => {
const notifyTransactionRecorded = async (payment, transaction, source = {}) => {
try {
const notify = await resolveNotifyFlags({}, 'transactionRecorded');
const notify = await resolveNotifyFlags(source, 'transactionRecorded');
if (!notify.sms && !notify.email && !notify.bot) return;
const { user, invoiceCode } = await formatPaymentContext(payment);
@@ -118,13 +118,15 @@ const notifyTransactionRecorded = async (payment, transaction) => {
invoiceCode,
transactionCode: transaction.uniqueCode || '',
receiptNumber: transaction.receiptNumber || ''
}, user._id)
}, user._id),
requestSource: source
});
} catch (err) {
logger.error(`[notifyTransactionRecorded] Failed for transaction ${transaction._id}: ${err.message}`);
}
};
const populatePayment = (query) => query
.populate({ path: 'user', select: 'name phoneNumber' })
.populate({ path: 'classes', select: 'name tuitionFee' })
@@ -195,16 +197,10 @@ const reconcilePendingTransactions = async (payment, { adjustAmount = true } = {
if (pending.length > 1) {
await Transaction.deleteMany({ _id: { $in: pending.slice(1).map((row) => row._id) } });
}
return;
}
await Transaction.create(buildTransactionPayload(payment, {
amount: remaining,
status: 'pending',
dueDate: payment.dueDate || new Date()
}));
};
const buildTransactionPayload = (payment, trxData, actorId = null) => {
const status = trxData.status === 'pending' ? 'pending' : (trxData.status === 'paid' || trxData.date ? 'paid' : 'pending');
const paidDate = parseDate(trxData.date) || (status === 'paid' ? new Date() : undefined);
@@ -314,12 +310,6 @@ const createPayment = async (body, actorId = null) => {
if (incomingTransactions?.length) {
await createTransactionsForPayment(payment, incomingTransactions, actorId);
} else if (payment.dueDate && getPayableAmount(payment) > 0) {
await Transaction.create(buildTransactionPayload(payment, {
amount: getPayableAmount(payment),
status: 'pending',
dueDate: payment.dueDate
}, actorId));
}
await refreshPaymentTotals(payment);
@@ -416,12 +406,13 @@ const addTransaction = async (paymentId, trxData, actorId = null) => {
userId: payment.user,
actorId
});
await notifyTransactionRecorded(payment, transaction);
await notifyTransactionRecorded(payment, transaction, trxData);
await emitPaymentStatusChangedIfNeeded(payment, previousStatus, actorId);
return getPaymentById(paymentId);
};
const updateTransaction = async (transactionId, body, actorId = null) => {
const trx = await Transaction.findById(transactionId);
if (!trx) throw new AppError('TRANSACTION_NOT_FOUND');