Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Refund Guarantee

Description / Background

Many users are hesitant to prepay for buffet or set menu bookings because they worry about losing money if their plans change. This leads to fewer bookings, especially when cancellation policies are unclear or too strict, which is common on other platforms like Booking.com or hotel websites.

To solve this, Hungry Hub is introducing the Refund Guarantee—an optional add-on at checkout. It allows users to get a full refund (via promo code) if they cancel at least 24 hours before their reservation.

This feature helps build trust, reduces booking risk, and gives Hungry Hub a clear advantage over competitors with less flexible policies.

Objectives

  • Admin can set the refund guarantee on package “Accept Refund Guarantee?”
  • Admin can select from two Fee Type Choice:
    • Percentage of package price (%) → package price is 100 THB and the percentage of refund fee is 10%, so the refund fee is 10 THB
    • Fixed price (THB / SGD) → admin input the price number
  • Admin can see the Refund Guarantee label on the booking list
  • At checkout, total price = package price × quantity + refund fee (percentage or fixed)
    • The refund fee will be package-based, meaning that if a user books 2 different packages, the user needs to pay for 2 refund fees
  • Users and Guest can add Refund Guarantee when they checkout
  • The Refund Guarantee is optional
  • Users and Guest can only use prepaid for the Refund Guarantee
  • Users and Guest can view the terms and conditions of refund guarantee
  • The refund section will be displayed when user books package that accepts refund guarantee
  • The refund section will be displayed when user books package and add-on that all of them accept refund guarantee
  • The refund section will not be displayed when one of the package or add-on user books did not accept refund guarantee
  • The refund guarantee will be returned to the gift card when the user books the package using the gift card.
  • Guests will receive the refund voucher via email.
  • The refund voucher is valid for 1 year (365 days) after it is sent and can be used once.
  • The refund guarantee received will be in the form of a promotional voucher.
  • The refund guarantee fee is calculated on a per-package and per-add-on basis. Example:
  • If user books 2 packages, the refund guarantee will be multiplied by 2
  • If user books 1 package and 2 add-ons, the refund guarantee will use 1 price from the package and 2 prices from the add-ons
  • When user books using gift card, the refund will be returned to the gift card
  • The refund guarantee received will be in the form of a promotional voucher
  • CANCEL condition:
    • Cancel but out of refund period → Use Cancelled label under the BookingID on Booking List Page
    • Cancel & refund (When user cancels the booking within the refundable period and claims the refund):
      1. Use Cancelled with refund label under the BookingID on Booking List Page Admin Dashboard
      2. Use Canceled on Booking List Page Partner Portal. Once canceled, the payment becomes Due, and the Total Due will only be the package total (excluding the refund guarantee fee).
      3. Automatically refunded via promo code directly (make sure it’s showing on promo code table as well)
      4. Users receive 100% of the booking price after discounts are applied (excluding the Refund Guarantee fee)
      5. The refund promo code is valid for 1 year (365 days) from the date of issuance and can be used once.
  • Users and Guest can cancel the booking and claim the refund via:
    • Selecting Cancel Booking and Claim Refund in the email confirmation within the required time before dining time
    • Claiming through the booking confirmation page
  • Users and Guest can’t claim refund when period has ended
  • Display tooltip with updated description: “Select Cancel Booking and Claim Refund in the email confirmation within the required time before your dining time to receive a promo code equal to your subtotal (excluding the Refund Guarantee fee)”
  • When user clicks Claim Refund, show pop-up message: “The promo code will be sent to your email and valid for 1 year”
  • Include refund guarantee information in confirmation email
  • Display message: “Select Cancel Booking and Claim Refund on your booking within the required time before your dining time to receive a promo code equal to your subtotal (excluding the Refund Guarantee fee)”
  • Include: “The promo code will be sent to your email and valid for 1 year”
  • Refund Voucher Specifications
    • Format Requirements:
      • All countries & currencies (as long as in the same currency)
      • All package types supported
      • No minimum total price requirement
      • No specific city restriction
      • No “first time booking only” restriction
      • No max pax limitation
      • All devices supported
      • All payment methods supported
      • Not required 100% prepayment
      • Don’t allow to mix with points
      • Promo code category: Refund Guarantee
      • Subsidized by: HungryHub
      • Discount type: Fixed amount (equal to subtotal excluding refund fee)
      • Max usage: 1 time per voucher
      • Expiry date: 1 year (365 days) from issue date
    • Every voucher created must be shown on promo code admin dashboard, email, and end user side

Scope

 booking flow

Sequence Diagram / Flow

 

ERD

UML

Backend Implementation

  • Created ReservationRefundGuarantee model with reservation_id, refund_fee_cents, refund_currency, refundable_amount_cents, refundable_until, status (pending/claimed/expired), and voucher_id fields
  • Added can_claim_refund?, refund_period_expired?, mark_as_claimed!, mark_as_expired!, pending?, claimed?, expired?, and formatted_refundable_until methods to ReservationRefundGuarantee model
  • Added belongs_to :voucher (optional) association to ReservationRefundGuarantee model to link created refund vouchers
  • Used Money gem monetization for refund_fee and refundable_amount fields in ReservationRefundGuarantee model
  • Added accept_refund_guarantee, refund_type (percentage/fixed_amount), refund_amount, and refund_hours_in_advance fields to HhPackage::AddOn model with validations
  • Added same refund guarantee fields to HhPackage::PackageAttr model to enable packages to support refund guarantee
  • Extended Reservation model with refund_fee_amount_float, refundable_amount_float, has_cancelled_with_refund?, and assign_refund_guarantee_data methods
  • Added has_one :refund_guarantee association to Reservation model
  • Added :cancelled_with_refund status symbol to reservation statuses
  • Integrated @accept_refund flag and @custom_refund_fee_cents attribute into ChargeCalculator for refund guarantee calculation
  • Modified ChargeCalculator#calculate to process refund fees for packages and add-ons with refund guarantee enabled
  • Updated ChargeCalculator#calc_charge_percent to return 100% when refund guarantee is enabled (requires full prepayment)
  • Implemented refund fee calculation logic supporting both percentage and fixed_amount types with ceiling rounding
  • Added total_refund_price, total_refund_price_cents, total_refund_price_v2, total_refundable_amount, total_refundable_amount_cents, and total_refundable_amount_v2 to charge calculation output
  • Supported custom refund fee override via custom_refund_fee_cents for admin adjustments
  • Refactored VoucherCalculator to apply vouchers to combined package + add-on + refund fee totals (vouchers apply to refundable amount, not refund fee itself)
  • Updated voucher application order to calculate base prices, add refund fees, apply vouchers, then calculate final charge
  • Created RefundGuaranteeClaimService to validate reservations, check claimability, cancel bookings, create refund vouchers, and mark guarantees as claimed
  • Updated RefundGuaranteeClaimService to support guest bookings (non-member) with voucher creation and email delivery
  • Changed refund voucher expiry from 3 months to 1 year with constant EXPIRY_DURATION = 1.year
  • Modified refund voucher to support both member (specific_customer with user_id) and guest (all_restaurants without user_id) voucher types
  • Linked created refund voucher to ReservationRefundGuarantee record via voucher_id foreign key after successful creation
  • Removed member-only validation for refund guarantee - now supports both members and guest bookings
  • Implemented error handling in RefundGuaranteeClaimService for reservation_not_found, refund_not_available, refund_period_expired, refund_already_claimed, and refund_not_claimable scenarios
  • Removed validation requiring user_id in ReservationService::Form::HungryHub and Agents::UpdateForAdmin to allow guest refund guarantee
  • Updated ReservationService::Create to accept custom_refund_fee_cents and pass accept_refund flag to charge calculator
  • Modified CancelReservationService to support claim_refund parameter that triggers refund guarantee claim flow and skips normal cancellation email notification
  • Added accept_refund_guarantee, refund_type, refund_amount, and refund_hours_in_advance strong parameters to admin add-ons controller
  • Created admin UI components in Price.vue and addOnSettingStore.js with refund guarantee checkbox, radio buttons for percentage/fixed price, amount input, and hours in advance input
  • Implemented admin UI validations ensuring refund type selection, amount > 0, percentage 0-100 range, and hours >= 0
  • Updated admin reservations controller to include refund guarantee in calculate_package_price, support claim via cancel reason, preserve refund data in updates, include in populate_seed, and added send refund notification endpoint
  • Enabled admin custom refund fee override via custom_refund_fee_cents parameter for special cases
  • Created new POST endpoint /api/v5/reservations/:reservation_id/claim_refund.json (member) requiring authentication
  • Created new POST endpoint /api/v5/reservations/:encrypted_id/claim_refund_guest.json (guest) without authentication using encrypted reservation ID
  • Updated member claim refund endpoint to allow guest bookings by checking reservation.user_id.present? instead of requiring match
  • Added accept_refund_guarantee parameter to calculate_package_price API endpoint for charge calculation with refund fees
  • Added accept_refund_guarantee parameter to voucher validation API endpoint for refund-aware voucher calculations
  • Added accept_refund_guarantee parameter to reservation creation API endpoints for booking with refund guarantee
  • Extended ReservationSerializer with refund_fee_v2, refundable_amount_v2, and max_refundable_at fields (conditional display)
  • Added refund_guarantee object with type, amount, and hours_in_advance to add-on and package serializers
  • Created RefundGuaranteeHelper with calculate_refundable_until_time and format_refund_guarantee_deadline methods for deadline calculations
  • Created ExpireRefundStatusWorker background worker to automatically expire pending refund guarantees past their deadline with batch processing and error handling
  • Updated CheckPaymentWorker to include refund guarantee data in payment verification
  • Added :cancelled_with_refund status handling to ReservationDecorator with red background color (#FFEAEB) and translation support
  • Created UserMailer#claim_refund_guarantee method to send refund confirmation emails with voucher details for both members and guests
  • Added email banners and promo code icons: hh-banner-refund-en.png, hh-banner-refund-th.png, hh-download-app-en.png, hh-download-app-th.png, hh-promo-code-icon-1/2/3.png
  • Created claim_refund_guarantee_en.html.erb and claim_refund_guarantee_th.html.erb email templates (306 lines each) with voucher code, expiry date, and usage instructions
  • Created shared partial _refund_guarantee_description.html.erb with member/guest-specific messaging (member: profile, guest: email) and 1-year validity period
  • Updated booking confirmation email templates (AYCE, DIY, PP, XP) for English and Thai to use shared refund guarantee partial instead of inline code
  • Refactored email templates for owner, staff, and guest assistance to use shared refund guarantee description partial
  • Modified UserMailer#booking_cancel to skip sending cancellation email when refund guarantee claim is processed (delegated to claim_refund_guarantee mailer)
  • Triggered NotificationWorkers::Reservation.perform_in(30.seconds, reservation.id, 'refund') after successful refund claim to send confirmation email
  • Added i18n translation actions.reservation.refund_claimed_guest for guest refund success message in en, th, ko, cn locales
  • Removed unused i18n key errors.refund_guarantee.only_for_member from error translations
  • Added "refund guarantee" cancel reason constant to reservation class methods
  • Added CHARGE_TYPE_ON_CHARGE and CHARGE_TYPE_ON_HOLD constants to package model
  • Updated admin reservation UI with “Send Refund Confirmation to User” button for cancelled_with_refund bookings
  • Updated UserCancelWithRefundComponent.vue with member/guest conditional messaging (profile vs email), 1-year validity text, and is_member prop support
  • Updated quickEdit.vue to pass reservation prop (including is_member flag) to UserCancelWithRefundComponent
  • Added is_member flag to quick_edit.html.erb template data serialization
  • Created comprehensive model specs for ReservationRefundGuarantee (359 lines) covering associations, monetization, status transitions, optional voucher relation, and currency handling
  • Created service specs for RefundGuaranteeClaimService (549+ lines) covering successful claims, voucher creation for members and guests, 1-year expiry, validation failures, currency handling, and error scenarios
  • Refactored charge calculator specs to cover refund guarantee fee calculation (percentage & fixed), custom fee override, charge percent 100% enforcement, and voucher integration
  • Created new voucher calculator specs (620 lines) covering package/delivery vouchers, combined vouchers, percentage/fixed amounts, deductible vouchers, and priority sorting
  • Created worker specs for ExpireRefundStatusWorker (383 lines) covering batch expiration, time-based filtering, status updates, and error handling
  • Created factory for reservation_refund_guarantees test data generation
  • Updated package form Vue components (PackageForm.vue, PricingForm.vue, form.vue) to support refund guarantee settings with UI controls and validations
  • Updated quickEdit.vue admin component to display and manage refund guarantee data
  • Added refund guarantee UI to admin reservation audit data, quick edit, and reservation views
  • Updated reservation report worker to include refund guarantee data in reporting
  • Passed accept_refund_guarantee flag through voucher form validation
  • Updated pt_add_reference helper in PtOnlineSchemaChange::Helper to support custom foreign key constraint names via :name option
  • Added voucher_id column to reservation_refund_guarantees table via migration 20251216094800_add_voucher_relation_to_reservation_refund_guarantees.rb
  • Added foreign key constraint fk_reservation_refund_guarantees_voucher_id on voucher_id referencing vouchers.id using pt-online-schema-change
  • Created data task migration 20251217033132_backfill_vouchers_on_reservation_refund_guarantees.rb to backfill existing voucher links
  • Added accept_refund and custom_refund_fee_cents fields to reservation_properties table
  • Created reservation_refund_guarantees table with indexes on reservation_id, status, refundable_until, and voucher_id
  • Added refund guarantee columns to hh_package_package_attrs table with indexes
  • Added refund guarantee columns to add_ons table with indexes
  • Updated database schema to reflect all refund guarantee tables, columns, and foreign key relationships

https://github.com/hungryhub-team/hh-server/pull/7183 https://github.com/hungryhub-team/hh-server/pull/7572

Mobile Implementation (Android)

  • Created new RefundGuarantee Kotlin data class (app/src/main/java/com/hb/hungryhub/model/hybrid/checkout/RefundGuarantee.kt) with three fields: type, amount, and hours_in_advance
  • Used @SerializedName annotations for JSON serialization/deserialization from API responses
  • Added refund_guarantee field to Package model (app/src/main/java/com/hb/hungryhub/model/bookingguest/Package.java) with Gson @Expose annotation
  • Implemented getter and setter methods for refundGuarantee in Package model
  • Imported RefundGuarantee class in Package model to support refund guarantee data structure
  • Added refund_guarantee field to AddOn model (app/src/main/java/com/hb/hungryhub/model/hybrid/checkout/AddOn.java) with Gson @Expose annotation
  • Implemented getter and setter methods for refundGuarantee in AddOn model
  • Enabled packages to carry refund guarantee information including fee type (percentage or fixed), amount value, and minimum hours in advance for eligibility
  • Enabled add-ons to support refund guarantee feature with same data structure as packages

https://github.com/hungryhub-team/hh-android/pull/2086

Hybrid Implementation (Web/PWA)

  • Added RESERVATION_CANCELLED_WITH_REFUND constant for tracking cancelled bookings with refund status
  • Added VOUCHER_CATEGORY_REFUND_GUARANTEE constant for refund guarantee voucher category
  • Added EVENT_ON_BOOKING_CANCELLED constant for hybrid event when booking is cancelled
  • Created RefundGuaranteeSchema type definition with type, amount, and hoursInAdvance fields
  • Extended Pack and AddOn schemas to include refundGuarantee property for type safety
  • Extended booking charge types to include refund_guarantee and subtotal_refund_guarantee items
  • Extended voucher categories to include refund_guarantee type
  • Added acceptRefundGuarantee boolean parameter to calculateCharge API payload schema
  • Created claimRefund.ts API service with POST endpoint to cancel booking and claim refund with error handling using useHandleError composable
  • Added claimRefundGuest function to claimRefund.ts API service for guest users to claim refund using encrypted booking ID
  • Added acceptRefundGuarantee parameter and refundFeeV2/maxRefundableAt response fields to createBooking API
  • Extended getBookingDetail API response schema with refundFeeV2, maxRefundableAt, refundableAmountV2, dueAmountV2, and prepaidAmountV2 fields
  • Added acceptRefundGuarantee parameter to validateVouchers API payload
  • Created RefundGuarantee.vue component (202 lines) with radio button selector, gradient borders (green/red), shield icons, price fetching, loading state, and view details link
  • Created RefundGuaranteeTnc.vue component displaying terms, 3-step how-it-works guide, gift card terms, how-to-buy guide with responsive layout and i18n support
  • Updated RefundGuaranteeTnc.vue to show dynamic content based on user authentication status (member vs guest instructions)
  • Hidden gift card terms section for guest users in RefundGuaranteeTnc.vue
  • Created RefundGuaranteePage.vue component (75 lines) as modal/bottom sheet container with lazy loading for terms content
  • Created RefundGuaranteeLabel.vue component displaying refundable deadline with Tippy.js tooltip on booking confirmation
  • Updated RefundGuaranteeLabel.vue to accept isUserSignedIn prop and display dynamic tooltip content (email vs profile instructions)
  • Created RefundGuaranteeModal.vue reusable modal component (106 lines) with customizable title, subtitle, description, cancel/confirm buttons
  • Updated ChargeItem2.vue to support refund_guarantee and subtotal_refund_guarantee charge types, hide quantity display, and treat as charge types
  • Integrated RefundGuarantee component into CheckOutPage.vue with visibility conditions (prepaid, hours in advance check) and selection validation with toast error
  • Removed user sign-in requirement for displaying refund guarantee at checkout (now available for guests)
  • Added refund guarantee state reset on checkout page exit and mount
  • Added “Extra Coverage” section title slot and retry button for failed calculations in BookingChargeSummary2.vue
  • Implemented border styling logic in BookingChargeSummary.vue to visually separate refund items with borders
  • Added acceptRefundGuarantee state (string: “”, “true”, “false”) and resetRefundGuarantee() action to booking store
  • Created isAcceptRefundGuarantee getter in booking package store to check if packages/add-ons support refund guarantee
  • Created maxRefundGuaranteeHours getter to return maximum hours in advance from all selected items
  • Added totalRefundPriceV2 and totalRefundableAmountV2 fields to booking charge store state
  • Modified calculateCharge in booking charge store to accept applyChargeData parameter controlling store data application
  • Updated calculate charge payload to include acceptRefundGuarantee flag based on store state and package acceptance
  • Added maxRefundableAt field to booking confirmation store data
  • Added totalRefundPriceV2, totalRefundableAmountV2, and dueAmountV2 fields to booking confirmation charge object
  • Created isBeforeRefundableDate getter using dayjs to check if current time is before refund deadline
  • Mapped prepaidAmountV2 to chargePriceV2 in booking confirmation to show paid amount
  • Added refund_guarantee to voucher categories in OffersPage.vue, AvailablePromoCode.vue, ExpiredPromoCode.vue, and OffersList.vue
  • Added translation mapping for cancelledWithRefund status in BookingHistoryCard.vue
  • Updated BookingHistoryCard.vue to hide action button for bookings with cancelled_with_refund status
  • Added green shield icon display for active refund guarantee bookings in UpcomingBookingDesktop.vue and UpcomingBookingMobile.vue (only before deadline)
  • Extended ChargeObject type to include totalRefundPriceV2 and totalRefundableAmountV2 fields
  • Updated buildChargeSummary function in bookingCharge.ts to add subtotal item and refund guarantee charge item with estimated prices
  • Added refund guarantee support in charge summary parsing for booking confirmation in chargeSummary.ts
  • Included acceptRefundGuarantee in calculate charge payload for confirmation page calculations
  • Conditionally added acceptRefundGuarantee to booking payload in createBooking.ts when package accepts, user selected “true”, and booking has charge price
  • Updated rebuildPackagesData and rebuildAddOnsData in checkOutEvents.ts to preserve refundGuarantee data during rebuilds
  • Included acceptRefundGuarantee in voucher validation payload in validateVoucher.ts
  • Included refundGuarantee in package and add-on payload data in getBookingProcessData.ts
  • Set acceptRefundGuarantee: false in calculate charge payload for booking edit in BookingEditChargeSummary.vue to prevent recalculation
  • Updated BookingConfirmationContent.vue to handle guest and member refund claim flows separately with different API calls
  • Added guestRedirectionAfterClaimRefund function to redirect guests after successful refund claim (hybrid event or home page)
  • Updated refund confirmation modal to display dynamic message based on authentication status (profile vs email delivery)
  • Updated BookingConfirmationContainer.vue to handle cancelled_with_refund status as payment-failed state
  • Added claim refund button with state-based styling in BookingConfirmationSidebar.vue
  • Updated BookingConfirmationDesktop.vue and BookingConfirmationMobile.vue to pass isUserSignedIn prop to RefundGuaranteeLabel
  • Updated payment type computed property to use dueAmountV2 for remaining payment amount
  • Added guest-specific translation keys in 16 language files: submitYourClaimViaEmail, selectClaimRefundGuest, get100PercentPromoCodeRefundGuest, selectCancelAndClaimRefundInEmail, thePromoCodeWillBeSentToEmail
  • Updated promo code validity period from 3 months to 1 year in all language files
  • Added subtotal translation key in 16 language files (English, Thai, Japanese, Korean, Chinese Simplified/Traditional, Indonesian, Malay, Vietnamese, French, German, Spanish, Russian, Khmer, Lao)
  • Created SVG icon files: icon-shield-check.svg, icon-shield-cross.svg
  • Created PNG icon files: icon-shield-gray.png, icon-shield-green.png, icon-shield-red.png
  • Created guarantee step icons: icon-buy-guarantee-step-1.svg, icon-buy-guarantee-step-2.svg
  • Created guarantee feature icons: icon-guarantee-1.png, icon-guarantee-2.png
  • Created guide images: buy-guarantee-1.png, buy-guarantee-2.png

https://github.com/hungryhub-team/hh-pegasus/pull/2058 https://github.com/hungryhub-team/hh-pegasus/pull/2398

PRD & Task

 PRD:

Tasks: https://app.clickup.com/t/86cze8rc8 https://app.clickup.com/t/86d18dqmf

Design

user : https://www.figma.com/design/suDZ8JohloPf0kpLZ9AuL7/Checkout-Page?node-id=7197-2345&t=dYD29w4xEt2YGFZZ-1 v2 and guest: https://www.figma.com/design/suDZ8JohloPf0kpLZ9AuL7/Checkout-Page?node-id=7197-2345&t=cn78twhOsoWXUMqV-1

API Blueprint

User APIs (API v5)

MethodPathDescriptionAuthenticationParameters
POST/api/v5/reservations/:reservation_id/claim_refund.jsonCancel booking and claim refund guarantee (member)Required (Bearer token)reservation_id (in path)
POST/api/v5/reservations/:encrypted_id/claim_refund_guest.jsonCancel booking and claim refund guarantee (guest)Not required (uses encrypted ID)reservation_id (encrypted, in path)
POST/api/v5/restaurants/:restaurant_id/calculate_package_priceCalculate booking charges including refund feesOptionalaccept_refund_guarantee (boolean), package_bought (array), adult (integer), kids (integer), distance (float)
POST/api/v5/vouchers/validateValidate vouchers with refund guarantee considerationRequiredaccept_refund_guarantee (boolean), voucher codes, booking details
POST/api/v5/reservationsCreate reservation with refund guaranteeRequiredreservation object with accept_refund_guarantee flag

Admin APIs

MethodPathDescriptionAuthenticationParameters
POST/admin/reservations/:id/calculate_package_priceCalculate charges for admin bookings (supports custom refund fee)Admin sessionaccept_refund (boolean), custom_refund_fee_cents (integer), package/add-on details
PUT/admin/reservations/:idUpdate reservation (preserves refund guarantee)Admin sessionFull reservation object with refund guarantee data
POST/admin/reservations/:id/cancelCancel booking with optional refund claimAdmin sessionreason (string, set to “refund guarantee” to claim)
POST/PUT/admin/add_onsCreate/Update add-on with refund guarantee settingsAdmin sessionaccept_refund_guarantee, refund_type, refund_amount, refund_hours_in_advance

Vendor APIs (No direct refund guarantee endpoints, uses standard booking flow)

All vendor APIs maintain compatibility with refund guarantee through existing booking creation flows.

API Response Examples

Claim Refund Success Response (Member)

{
  "success": true,
  "data": null,
  "message": "Refund claimed successfully"
}

Claim Refund Success Response (Guest)

{
  "success": true,
  "data": null,
  "message": "Your booking has been cancelled and a promo code has been sent to your email"
}

Claim Refund Error Responses

// 403 Forbidden (Member endpoint only)
{
  "success": false,
  "message": "You are not authorized to access this reservation",
  "data": null
}

// 404 Not Found
{
  "success": false,
  "message": "Reservation not found",
  "data": null
}

// 422 Unprocessable Entity (various refund errors)
{
  "success": false,
  "message": "Refund guarantee has expired" | "Refund already claimed" | "Refund not available",
  "data": null
}

Calculate Charge Response (with Refund Guarantee)

{
  "success": true,
  "data": {
    "total_price": 1150,
    "total_price_cents": 115000,
    "total_price_v2": 1150,
    "charge_price": 1150,
    "charge_price_cents": 115000,
    "charge_percent": 100,
    "total_refund_price": 100,
    "total_refund_price_cents": 10000,
    "total_refund_price_v2": 100,
    "total_refundable_amount": 1050,
    "total_refundable_amount_cents": 105000,
    "total_refundable_amount_v2": 1050,
    "selected_packages": [...],
    "selected_add_ons": [...],
    "currency": "THB"
  }
}

Reservation Serializer (with Refund Guarantee)

{
  "id": 12345,
  "status": "pending_arrival",
  "refund_fee_v2": 100,
  "refundable_amount_v2": 1050,
  "max_refundable_at": "2025-11-23T14:00:00+07:00",
  // ... other reservation fields
}

Package/Add-on Serializer (with Refund Guarantee Settings)

{
  "id": 1234,
  "name": "Premium Buffet Package",
  "refund_guarantee": {
    "type": "percentage",
    "amount": "10",
    "hours_in_advance": 24
  },
  // ... other package fields
}

New Query

No new complex queries added. Uses standard ActiveRecord associations and scopes.

DB Schema / Database Migration

  • Create reservation_refund_guarantees Table

    ColumnTypeNullDefaultDescription
    idbigintNOPrimary key
    reservation_idintegerYESForeign key to reservations table
    refund_fee_centsintegerNO0Refund guarantee fee paid (in cents)
    refund_currencystring(191)YES‘THB’Currency code
    refundable_amount_centsintegerNO0Amount that can be refunded (in cents)
    refundable_untildatetimeYESDeadline for claiming refund
    statusstring(191)YESEnum: pending, claimed, expired
    created_atdatetimeNORecord creation timestamp
    updated_atdatetimeNORecord update timestamp
  • Add Refund Guarantee Fields to hh_package_package_attrs Table

    ColumnTypeNullDefaultDescription
    accept_refund_guaranteebooleanYESfalseWhether package accepts refund guarantee
    refund_typestring(191)YESEnum: percentage, fixed_amount
    refund_amountintegerYESPercentage value (0-100) or amount in cents
    refund_hours_in_advanceintegerYESMinimum hours before booking for refund eligibility
  • Add accept_refund_guarantee, refund_type, refund_amount, refund_hours_in_advance to add_ons Table

  • Add Refund Guarantee Fields to reservation_properties Table

    ColumnTypeNullDefaultDescription
    accept_refundbooleanYESfalseWhether booking has refund guarantee
    custom_refund_fee_centsintegerYESAdmin override for refund fee (in cents)

Indexes:

  • index_reservation_properties_on_accept_refund
  • index_add_ons_on_accept_refund_guarantee
  • index_hh_package_package_attrs_on_accept_refund_guarantee
  • index_reservation_refund_guarantees_on_reservation_id
  • index_reservation_refund_guarantees_on_status
  • index_reservation_refund_guarantees_on_refundable_until

Schema Relationships

reservations
  └─ has_one :refund_guarantee (reservation_refund_guarantees)
  └─ has_one :property (reservation_properties)
      └─ stores: accept_refund, custom_refund_fee_cents

hh_package_packages
  └─ has_one :package_attr (hh_package_package_attrs)
      └─ stores: accept_refund_guarantee, refund_type, refund_amount, refund_hours_in_advance

add_ons
  └─ stores: accept_refund_guarantee, refund_type, refund_amount, refund_hours_in_advance (directly on table)

vouchers
  └─ new category: 'refund_guarantee' (uses existing vouchers table)

Improvement:

Feature NameDateWhat ChangedDescription