Skip to content

fix: applied credits were being removed on redundant api calls#291

Open
vendz wants to merge 3 commits into
devfrom
hotfix/travel-credits-issue
Open

fix: applied credits were being removed on redundant api calls#291
vendz wants to merge 3 commits into
devfrom
hotfix/travel-credits-issue

Conversation

@vendz
Copy link
Copy Markdown
Member

@vendz vendz commented May 2, 2026

Fix: Wallet credit not respected on booking amount updates

Problem

Two separate bugs caused customers to be overcharged when wallet credits were involved:

Bug 1 — Utsav booking (utsavBooking.helper.js)
bookUtsavForMumukshus accumulated package_info.amount (gross) into the Razorpay order total instead of the discountedAmount returned by createPendingTransaction. This meant a customer with ₹400 credit booking a ₹530 utsav package would have the wallet correctly debited (transaction row: amount=130, discount=400) but a Razorpay order created for the full ₹530.

Bug 2 — Travel booking update (travelManagement.controller.js)
The PUT /admin/travel/bookingupdate endpoint blindly overwrote transaction.amount with whatever the admin submitted, without accounting for an already-applied wallet discount. If createPendingTransaction had run useCredit (reducing amount=530 → 130, discount=400), a subsequent admin edit resending amount=530 would restore the gross while leaving discount=400 intact — causing the Razorpay order to be generated for ₹530 while the wallet credit was silently consumed and the transaction description falsely claimed "credits used: 400".

This is the confirmed root cause of the payment discrepancy for booking 51cdddd9-203d-471a-88b9-26f24309c834 (charged ₹530 instead of ₹130).

Bug 3 — adjustAmount helper (transactions.helper.js)
The existing adjustAmount function referenced an undefined user variable (parameter was updatedBy: string) and had incorrect logic for the amount-increase case (discount: originalAmount was semantically wrong). It was unused due to this crash, so no live impact — but it was the right abstraction for the fix above.

Evidence from logs

07:08:46  POST /admin/travel/booking/status  bookingid=28a566a4… charges=399  → proceed for payment
07:09:01  PUT  /admin/travel/bookingupdate   bookingid=28a566a4… amount="398"  ← redundant save #1
07:09:09  PUT  /admin/travel/bookingupdate   bookingid=28a566a4… amount="400"  ← redundant save #2

Changes

helpers/transactions.helper.js

  • Fixed adjustAmount: replaced undefined user with { username: updatedBy }, rewrote both branches (gross decrease and gross increase) into a single unified credit re-application — newCreditsUsed = min(newGross, existingDiscount), refund excess to wallet if gross shrank, set STATUS_PAYMENT_COMPLETED if net hits zero. Removed unused booking parameter.

helpers/utsavBooking.helper.js

  • bookUtsavForMumukshus: destructure and accumulate discountedAmount (not package_info.amount) from createPendingTransaction, matching the correct pattern already used in room and adhyayan booking helpers.

controllers/admin/travelManagement.controller.js

  • updateBooking: replace ~40 lines of inline (and partially incorrect) credit math with a call to adjustAmount. Credit re-application, wallet refund on gross decrease, and status transitions are now handled in one place.
  • Import adjustAmount from transactions.helper.js.

Behaviour after fix

Scenario Before After
Admin edits amount, no credits applied ✅ correct ✅ unchanged
Admin edits amount, credits already applied, same gross ❌ corrupts amount ✅ no-op
Admin edits amount, credits applied, gross increases ❌ corrupts amount ✅ re-applies credit, increases net owed
Admin edits amount, credits applied, gross decreases below credit ❌ corrupts amount ✅ refunds excess credit to wallet, net = 0, status = completed
Utsav booking with wallet credit ❌ Razorpay order for gross ✅ Razorpay order for net

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors travel booking and transaction management, specifically introducing a more comprehensive adjustAmount helper to handle credit refunds and transaction status updates. It also updates the Utsav booking logic to correctly track discounted amounts. Feedback identifies a logic gap in adjustAmount where increasing a transaction's total does not revert a "completed" status to "pending," which could result in unpaid balances. Further improvements were suggested to allow applying additional wallet credits when amounts increase and to restore the booking parameter for automatic status synchronization.

Comment on lines +227 to 230
let newStatus = transaction.status;
if (newNet === 0) {
newStatus = STATUS_PAYMENT_COMPLETED;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The logic for updating newStatus is incomplete. If a transaction was previously completed (e.g., fully paid) and the gross amount is increased such that a balance is now owed (newNet > 0), the status remains completed. This will lead to payment discrepancies where bookings appear fully paid despite having an outstanding balance.

  let newStatus = transaction.status;
  if (newNet === 0) {
    newStatus = STATUS_PAYMENT_COMPLETED;
  } else if (
    [STATUS_PAYMENT_COMPLETED, STATUS_CASH_COMPLETED].includes(transaction.status)
  ) {
    newStatus = STATUS_CASH_PENDING;
  }


const bookingType = getBookingType(transaction);
const creditsAlreadyUsed = transaction.discount;
const newCreditsUsed = Math.min(amount, creditsAlreadyUsed);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic only re-applies credits that were already associated with this transaction. If the gross amount increases, it should ideally check the user's wallet (card.credits) to see if any additional credits can be applied to cover the new balance, similar to how createPendingTransaction works. Additionally, consider keeping the booking parameter to automatically update the booking status (e.g., to confirmed) if the transaction becomes fully paid, consistent with the behavior in useCredit.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes incorrect handling of wallet credits when booking amounts are created/updated, ensuring Razorpay orders and transaction net amounts remain consistent with applied credits.

Changes:

  • Utsav booking now totals the discounted (net) amount returned by createPendingTransaction, instead of the package gross.
  • Introduces a rewritten adjustAmount helper to re-apply existing credits and refund excess credits on gross decreases.
  • Travel admin booking update now delegates credit/amount reconciliation to adjustAmount (replacing inline credit math).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
helpers/utsavBooking.helper.js Uses discountedAmount from createPendingTransaction when accumulating total payable amount; minor formatting fix in date range logic.
helpers/transactions.helper.js Reworks adjustAmount to compute new net/discount and refund excess credits to wallet.
controllers/admin/travelManagement.controller.js Uses adjustAmount for admin booking amount updates and applies some formatting/cleanup.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +227 to +240
let newStatus = transaction.status;
if (newNet === 0) {
newStatus = STATUS_PAYMENT_COMPLETED;
}

await transaction.update(
{
amount: newNet,
discount: newCreditsUsed,
description: newCreditsUsed > 0
? `credits used: ${newCreditsUsed}`
: `Transaction updated. New balance: ${newNet}.`,
status: newStatus,
updatedBy,
Comment on lines +672 to 674
const card = await validateCard(transaction.cardno);
await adjustAmount(card, transaction, Number(amount), req.user.username, t);
updatedFields.push('amount');
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot suggest changes based on this feedback

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already addressed in 38d86ebamount is now parsed with Number(), validated with Number.isFinite() && >= 0, and a 400 ApiError is thrown before adjustAmount is called on invalid input.

Comment thread helpers/transactions.helper.js Outdated
Merged upstream changes (Number() casts for amount/discount) with stashed
changes (guard against updating completed transactions). Both are needed:
- Guard prevents invalid state changes
- Number() casts ensure arithmetic works correctly with originalDiscount

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants