Wallet Operations
Transaction Types
| Type | Mode | Description |
|---|---|---|
REWARD | CREDIT | Reward points earned from a booking |
LOAD | CREDIT | Manual wallet load by admin |
PROMOTION | CREDIT | Promotional credit |
REFER_AND_EARN | CREDIT | Referral bonus |
BOOKING | DEBIT | Wallet used for a booking payment |
DEDUCTION | DEBIT | System deduction (e.g., expiry) |
REFUND | CREDIT | Refund of a cancelled booking |
Transaction Statuses
| Status | Description |
|---|---|
UPCOMING | Reward credited but not yet earned (before checkout) |
EARNED | Available for use |
ON_HOLD | Reserved for a pending booking |
USED | Consumed in a completed booking |
EXPIRED | Past validTill date, deducted from balance |
RELEASED | Hold was cancelled, amount returned to wallet |
CANCELLED | Reward cancelled (e.g., booking cancelled before checkout) |
REFUNDED | Refunded 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:
| Field | Description |
|---|---|
walletTransactionLogId | The DEBIT transaction (the hold) |
walletConsumedTransactionLogId | The CREDIT transaction being consumed |
amount | Amount consumed from this source |
This enables accurate refunds — the system knows exactly which credits were used.
Scheduled Jobs
| Job | Cron | Purpose |
|---|---|---|
moveUpcomingToEarned | 0 0 2 * * * (2 AM) | UPCOMING → EARNED |
expireEarnedTransactions | 0 0 3 * * * (3 AM) | Expire past-validTill EARNED |
releaseAllHoldPayments | 0 */30 * * * * (every 30 min) | Release stale ON_HOLD |
All cron expressions are configurable via properties (e.g., wallet.scheduler.move-upcoming).