In this volume · VOLUME 04
Travel Operations
Booking Lifecycle Ticketing & PNR Refunds & Cancellations Voids, Reissues & Adjustments Non-Air Products ADM / ACM Memos

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

flowchart TD A[Customer Requests Refund] --> B[Quote from Supplier] B --> C{Quote OK?} C -->|No| D[Inform Customer] C -->|Yes| E{Above Threshold?} E -->|Yes| F[Approver Reviews] E -->|No| G[Authorise] F -->|Approved| G F -->|Rejected| D G --> H[BEGIN TX] H --> I[Execute Supplier Refund] I --> J{Supplier OK?} J -->|No| K[Rollback; Mark FAILED] J -->|Yes| L[Persist Refund Record] L --> M[Post Reversing JE] M --> N[Update Balances] N --> O[COMMIT] O --> P[Async: Customer Payback] P --> Q{Payback OK?} Q -->|Yes| R[PAYBACK_COMPLETE] Q -->|No| S[Customer Credit Balance + Alert] R --> T[Notify Customer + Webhook] S --> T

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.