রিফান্ড একটি সংগঠিত প্রক্রিয়া—সরবরাহকারী-পাশের রিফান্ডযোগ্যতা, গ্রাহক-পাশের ফেরত পেমেন্ট, কমিশন রিকল, কর সমন্বয়। প্রকার: voluntary, involuntary, tax-only, no-show, schedule-change, waiver। সার্ভিস-ডেট অতিক্রম হওয়ার আগে: deferred রিভার্স; পরে: বর্তমান-পিরিয়ড রেভিনিউ হ্রাস।
Chapter 4.3 — Refunds & Cancellations
1. Purpose
This chapter defines how travoBooks processes refunds, cancellations, and money-out events for previously issued bookings. It covers refund quoting (per fare rules), refund authorisation (supplier and customer sides), the financial reversal mechanics including commission recall, and the customer-payback paths (cash, gateway reversal, customer credit balance, bank transfer). Voids (same-day reversal) are touched in Chapter 4.2; this chapter focuses on post-void-window refunds.
2. Why it matters in modern travel accounting
Refunds are the most error-prone area in travel accounting. Common failure modes: - Customer refunded gross amount but commission never clawed back from supplier → margin loss - Refund posted in the wrong period after period close → restatement - ADM issued by airline for incorrect refund computation → cash leakage - Partial refund not aligned with fare-rule penalty → revenue overstated - Customer credit balance accumulates without expiry policy → silent liability
travoBooks' refund engine treats every refund as a structured, multi-party, multi-account financial event rather than a single negative line.
3. Industry relevance
The model accommodates: - Air refunds (full/partial, voluntary/involuntary, refundable/non-refundable with taxes-only refund) - Hotel cancellations (free-cancel window, partial penalty, full penalty) - Tour / package cancellations (tier-based penalty schedule) - Insurance (typically non-refundable after cooling-off) - Ancillary EMD refunds (often non-refundable)
4. Compliance considerations
- IFRS 15 — reversal of recognised revenue when contract obligation extinguished; if revenue not yet recognised (advance payment), liability adjusted.
- IFRS 9 — refund estimates form part of variable consideration in some package contracts; not material for Phase 1.
- GAAP — ASC 606 — equivalent treatment.
- Consumer protection laws — many jurisdictions mandate timeframes for refund issuance.
- PCI DSS — gateway refund flows must not log card data; only token references.
5. Business logic
5.1 Refund types
| Type | Code | Description |
|---|---|---|
| Voluntary full refund | VOL_FULL |
Customer cancels by choice |
| Voluntary partial refund | VOL_PARTIAL |
Customer changes scope (drops a pax / segment) |
| Involuntary refund | INVOL |
Supplier-initiated cancellation; full refund + fee waivers |
| Taxes-only refund | TAX_ONLY |
Non-refundable fare; unused taxes recoverable |
| No-show refund | NO_SHOW |
Customer no-show; usually only taxes (if any) |
| Schedule-change refund | SKCHG |
Material schedule change; involuntary |
| Death / medical waiver | WAIVER |
Doc-supported supplier waiver |
Each type drives different penalty / commission-recall logic.
5.2 Refund states
| State | Description |
|---|---|
QUOTED |
Refund amount calculated; not yet committed |
PENDING_APPROVAL |
Above threshold or unusual; needs approver |
AUTHORISED |
Approved; awaiting execution |
EXECUTING |
Supplier refund being processed |
EXECUTED |
Supplier confirmed; customer payback pending |
PAYBACK_COMPLETE |
Customer paid back |
FAILED |
Supplier or payback failed |
CANCELLED |
Refund request withdrawn before execution |
5.3 Refund record structure
| Field | Type | Notes |
|---|---|---|
refund_id |
BIGINT PK | |
partner_id |
BIGINT FK | |
booking_id |
BIGINT FK | |
refund_type |
ENUM | |
state |
ENUM | |
transaction_currency |
CHAR(3) | |
customer_refund_amount |
DECIMAL(18,2) | What customer receives |
supplier_refund_amount |
DECIMAL(18,2) | What supplier returns to us |
cancellation_fee_amount |
DECIMAL(18,2) | Penalty kept by supplier |
service_fee_retained |
DECIMAL(18,2) | Partner's own service fee retained |
commission_recall_amount |
DECIMAL(18,2) | Commission clawed back |
tax_refund_amount |
DECIMAL(18,2) | Taxes refunded (per breakdown) |
payback_method |
ENUM(cash, gateway_reversal, bank_transfer, customer_credit) |
|
payback_status |
ENUM(pending, complete, failed) |
|
payback_reference |
VARCHAR(64) | Bank/gateway ref |
requested_by_user_id |
BIGINT FK | |
approved_by_user_id |
BIGINT FK NULL | |
executed_at |
DATETIME NULL | |
je_id |
BIGINT FK NULL | Reversing JE reference |
reason_code |
VARCHAR(32) | Standardised reason |
reason_text |
TEXT | Free-form |
| Audit columns |
5.4 Refund quote computation
For air, the quote calls supplier Refund_QuoteRefund / OrderReshopRQ (refund) which returns:
- Penalty (cancellation fee retained by supplier)
- Refundable amount per pax per ticket
- Tax refundability
- Original form-of-payment
travoBooks layers on top: - Partner-side fees — service fee retained per partner config - Commission recall — original commission must be reversed (supplier won't pay commission on refunded ticket) - Currency — refund in original transaction currency at original FX rate for the reversal portion; new FX rate for any commission-recall denominated differently
For hotel / non-air, the quote follows the captured cancellation policy (free until date X, penalty Y% between dates).
5.5 Approval thresholds
A refund requires approver when any of:
- customer_refund_amount > partner.refund_approval_threshold
- refund_type = WAIVER (any amount)
- Booking period is closed (requires period reopen — controller-level approval)
- Commission recall fails (supplier won't accept) and partner-config requires manual override
5.6 Financial mechanics (the JE)
Refund posting is the inverse of issuance, with overlays for penalty and commission. Continuing the Beta Corp Emirates DAC-DXB example (issued at USD 600 = BDT 65,400):
Voluntary refund with USD 100 penalty kept by EK, USD 25 partner service fee retained:
| Account | 🅓 Debit | 🅒 Credit | Dim |
|---|---|---|---|
| 2011 BSP Payable BD | 54,500 | EK, BSP-period | |
| 4011 Air Base Commission | 3,924 | EK (recall) | |
| 1109 Commission Receivable from Supplier | 3,924 | EK | |
| 4031 Service Fee Revenue | 0 | (retained — not reversed) | |
| 1101 AR — Beta Corp | 50,576 | Beta Corp | |
| 4041 Cancellation Fee Income | 0 | (none for us; EK kept it) | |
| 5099 Cancellation Fee Pass-through Expense | 0 | (none) |
Customer net refund: USD 600 ticket − USD 100 EK penalty − USD 25 partner fee = USD 475 ≈ BDT 51,775 (illustrative FX at refund date).
Key invariants:
- The refund JE reverses the original issuance JE for the refundable portion only.
- Penalty (supplier-kept) stays in supplier-payable but is reclassified as 5099 — Supplier Cancellation Penalty expense flowing through pass-through; alternatively suppressed if it was never our cash.
- Commission recall is always posted symmetric to original commission recognition.
- Service fee retention (partner's own) is not reversed unless partner policy says so.
5.7 Customer payback paths
| Method | Mechanism | Timing |
|---|---|---|
gateway_reversal |
Stripe/SSL refund API against original transaction | T+1 to T+10 business days (gateway dependent) |
cash |
Cashier hands back cash; recorded against cash drawer | Immediate |
bank_transfer |
Manual wire by accountant; uses customer bank details | T+1 to T+5 |
customer_credit |
Credit added to customer's account balance; usable on next booking | Immediate |
Payback is a separate event from the supplier-refund execution. The pattern is: 1. Supplier refund executed → debit booking-side accounts; AR / cash-out account debited. 2. Customer payback → AR or cash-out cleared.
If payback fails (gateway rejects, bank details wrong), the customer is left with a customer credit balance automatically, with notification to accountant.
5.8 Period considerations
If the original issuance is in a closed period, the refund cannot post into that period. travoBooks enforces: - Refund JE date = current open period. - The JE references the original JE for traceability but does not back-date. - Period-close reports include "refunds against prior-period revenue" disclosure.
If the original is in a locked period (auditor-locked), no refund JE is allowed without controller override.
5.9 Cancellation without refund
Some cancellations have zero refund (fully non-refundable, post-departure cancellation, no-show with no recoverable taxes). The booking still transitions to CANCELLED_AFTER_ISSUE, but no refund JE is needed (or only commission-recall is needed if applicable).
6. Inputs → processing → outputs
Process refund
Input: {booking_id, refund_type, reason_code, reason_text, override_supplier_quote?, payback_method}
Processing:
1. Permission check (bookings.refund.partner).
2. Validate booking state in (ISSUED, PARTIALLY_USED, PARTIALLY_REFUNDED).
3. Period check — current period open.
4. Quote refund from supplier (cached for 15 min).
5. Compute partner-side fees and commission recall.
6. Threshold check → maker-checker if required.
7. Begin DB transaction.
8. Execute supplier-side refund.
9. Persist refund record.
10. Post reversing JE.
11. Update booking state.
12. Update balances.
13. Commit.
14. Async: trigger payback path; send customer notification; webhook.
Output: refund_id, JE reference, estimated payback date, customer notification.
7. Module dependencies
| Direction | Module |
|---|---|
| Depends on | Booking, Ticketing, Supplier Integration, Tax Config, Payment Gateway, JE Engine, Period-Close |
| Depended on by | Reporting (refund analytics), Reconciliation (BSP refund matching), Webhooks, Dunning (cancels open invoice rows) |
8. Security & permissions
| Permission | Allows |
|---|---|
refunds.quote.partner |
Get quote |
refunds.create.partner |
Create refund (subject to threshold) |
refunds.approve.partner |
Approve above-threshold |
refunds.waive_penalty.partner |
Override supplier penalty (rare) |
refunds.reopen_period.platform |
Refund into closed period |
Above-threshold refunds always require checker different from maker.
9. Validation rules
| Code | Condition |
|---|---|
REFUND_BOOKING_NOT_ELIGIBLE |
Wrong state (DRAFT, EXPIRED, never issued) |
REFUND_PERIOD_CLOSED |
Original or current period prevents posting |
REFUND_QUOTE_EXPIRED |
Quote older than 15 min |
REFUND_AMOUNT_EXCEEDS_AVAILABLE |
Amount > original − previous refunds |
REFUND_COMMISSION_RECALL_FAILED |
Supplier rejects commission claw-back |
REFUND_SUPPLIER_FAILED |
Supplier API rejected |
REFUND_PAYBACK_GATEWAY_FAILED |
Gateway reversal failed |
REFUND_DUPLICATE |
Idempotency replay |
REFUND_REQUIRES_APPROVAL |
Above threshold without checker |
REFUND_BANK_DETAILS_MISSING |
Bank transfer chosen but no bank details on customer |
10. Error handling
The most delicate failure is supplier accepted refund, payback failed — partner is out of pocket against an open AR. The workflow: 1. JE for supplier-side is committed (supplier did refund). 2. Payback fails → customer balance becomes a customer credit automatically. 3. Notification to accountant + customer. 4. Accountant can retry payback via different path (bank transfer instead of failed gateway).
Idempotency: refund operations carry Idempotency-Key; replays return original result, never double-refund.
11. Real-world examples
Example A — Full voluntary refund with penalty
(Beta Corp EK example above; see §5.6.)
Example B — Taxes-only refund on non-refundable
Customer no-show on a non-refundable Biman ticket (BDT 8,000 base, BDT 750 government taxes). Government taxes are recoverable; carrier-imposed are not.
JE:
Debit 2011 BSP Payable BD 500
Debit 4011 Air Base Commission 0 (none originally)
Credit 1101 AR — Customer 500 (or Cash if walk-in)
Booking state: NO_SHOW_REFUNDED (partial state)
Example C — Hotel free-cancel
Hotelbeds booking, EUR 200, cancelled within free-cancel window.
Supplier confirms full refund.
JE: Debit 2003 AP — Hotelbeds 200 EUR (translated)
Credit 1101 AR — Customer 200 EUR (translated at original FX)
+ FX gain/loss adjustment for translation difference
Booking state: CANCELLED_AFTER_ISSUE
Payback: gateway reversal (200 EUR back to card)
Example D — Cross-period refund
Ticket issued in April (closed period). Customer cancels in June.
Quote: BDT 60,000 refund.
JE dated 2026-06-15 (June, current open period):
Debit BSP Payable, debit commission recall, credit AR/Cash, etc.
Booking links to original JE in April but does not modify it.
Period-close report for June shows BDT 60,000 in "refunds against prior periods".
12. Step-by-step workflow
13. Database tables touched
| Table | Role |
|---|---|
refunds |
Refund record |
refund_quotes |
Quote history (audit) |
bookings |
State transition |
booking_tickets / _emds |
Ticket status updates |
customer_balances |
AR reduction |
supplier_balances |
AP reduction |
customer_credit_balance |
If payback as credit |
journal_entries / _lines |
Reversing entries |
audit_logs |
Events |
14. Future scalability
- Auto-refund engine — for high-volume OTAs, auto-refund within free-cancel window without human touch.
- ATPCO-aware quote engine — pre-compute refund quotes from fare rules without supplier round-trip.
- Refund-prediction model — flag bookings with elevated refund probability for staffing.
- Customer credit expiry policy — automatic expiry of stale customer credit (with statutory notice).
15. Common pitfalls
- ⚠️ Forgetting commission recall. The supplier will not pay commission on a refunded ticket — if you don't recall it, you double-count revenue.
- ⚠️ Refunding in original FX rate vs. current rate. Use the original rate for the reversal portion; FX gain/loss is a separate line.
- ⚠️ Posting back-dated refunds into closed periods. Always use current-period date; reference the original JE.
- ⚠️ Treating supplier-execution and customer-payback as one event. They are two — model them separately.
- 🔒 Gateway refund flow must not log card data. Token references only.
- ⚠️ Quoting refunds without re-fetching. Quotes age out; always re-fetch within 15 minutes of execution.