Skip to main content

Wallet Operations

Transaction Types

TypeModeDescription
REWARDCREDITReward points earned from a booking
LOADCREDITManual wallet load by admin
PROMOTIONCREDITPromotional credit
REFER_AND_EARNCREDITReferral bonus
BOOKINGDEBITWallet used for a booking payment
DEDUCTIONDEBITSystem deduction (e.g., expiry)
REFUNDCREDITRefund of a cancelled booking

Transaction Statuses

StatusDescription
UPCOMINGReward credited but not yet earned (before checkout)
EARNEDAvailable for use
ON_HOLDReserved for a pending booking
USEDConsumed in a completed booking
EXPIREDPast validTill date, deducted from balance
RELEASEDHold was cancelled, amount returned to wallet
CANCELLEDReward cancelled (e.g., booking cancelled before checkout)
REFUNDEDRefunded after booking cancellation

Hold / Confirm / Release Flow

1. Hold (checkout initiated)

Guest starts checkout
→ WalletOperationsService.holdWalletMoney(walletId, amount, bookingId)
→ Creates BOOKING/DEBIT/ON_HOLD transaction
→ Returns transactionId

The hold does NOT deduct from wallet balance yet. It reserves the amount.

2. Confirm (booking completed)

Booking confirmed
→ WalletOperationsService.confirmHold(transactionId)
→ Consumes from EARNED transactions (FIFO by validTill ASC)
→ Creates WalletConsumedLog entries for each consumed source
→ Updates source transactions' availableToConsume
→ Marks hold as USED
→ Deducts from wallet.totalAmount

3. Release (booking abandoned/cancelled)

Booking cancelled
→ WalletOperationsService.releaseHold(transactionId)
→ Reverses any consumption (restores availableToConsume)
→ Deletes WalletConsumedLog entries
→ Marks hold as RELEASED

Reward Credit Flow

After checkout:
→ WalletOperationsService.creditReward(walletId, bookingId, amount, expiryDays)
→ Creates REWARD/CREDIT/UPCOMING transaction
→ Sets validTill = now + expiryDays
→ Adds amount to wallet.totalAmount immediately

Daily 2 AM scheduler:
→ moveUpcomingToEarned()
→ Updates UPCOMING → EARNED (now available for use)

Daily 3 AM scheduler:
→ expireEarnedTransactions()
→ Finds EARNED transactions past validTill
→ Creates DEDUCTION transaction, marks as EXPIRED
→ Deducts from wallet.totalAmount

Reward Calculation

rewardAmount = (bookingNetAmount / rewardRule.amountSpent) * rewardRule.rewardPoints

The reward rule is determined by the guest's loyalty tier. Falls back to the default reward rule if no tier-specific rule exists.

Applicable Amount Calculation

applicableAmount = min(walletBalance, bookingAmount * loyaltyTier.redemptionCapability / 100)

Example: Wallet has Rs 5,000, booking is Rs 20,000, tier allows 40% redemption:

  • Max applicable = 20,000 * 40% = Rs 8,000
  • Actual applicable = min(5,000, 8,000) = Rs 5,000

Consumption Tracking

When a hold is confirmed, the system consumes from EARNED transactions in FIFO order (earliest validTill first). Each consumption is logged:

FieldDescription
walletTransactionLogIdThe DEBIT transaction (the hold)
walletConsumedTransactionLogIdThe CREDIT transaction being consumed
amountAmount consumed from this source

This enables accurate refunds — the system knows exactly which credits were used.

Scheduled Jobs

JobCronPurpose
moveUpcomingToEarned0 0 2 * * * (2 AM)UPCOMING → EARNED
expireEarnedTransactions0 0 3 * * * (3 AM)Expire past-validTill EARNED
releaseAllHoldPayments0 */30 * * * * (every 30 min)Release stale ON_HOLD

All cron expressions are configurable via properties (e.g., wallet.scheduler.move-upcoming).