Malaysia Expansion
Description / Background
Hungry Hub is expanding its platform availability to Malaysia to support growth in new markets across Southeast Asia. Previously, Hungry Hub operated only in Thailand and Singapore, which previously limited platform access for users and restaurant partners located in Malaysia. With this expansion, Malaysian restaurants can now onboard to Hungry Hub, and users in Malaysia will gain full access to the web and mobile app experiences. The platform has been localized for the Malaysian market, including local currency (MYR), language options, and regional payment methods. This enhancement allows Hungry Hub to connect more diners with local dining experiences while enabling restaurant partners in Malaysia to increase visibility, manage reservations efficiently, and grow their revenue through Hungry Hub’s fixed-price dining model.
Objectives
-
Users can register using Malaysian phone number
-
Users can pick Malaysia from the city list
-
Users can pick MY language
-
Users can see the Malaysian Restaurants
-
Users can book table for Malaysian Restaurant
-
Users can view prices in MYR
-
Users can receive email confirmation and other email notification from our system
-
Users can see the Malaysian timezone Display
-
Users can see the Malaysian location tags on search
-
Users can see the Malaysian cuisine filter
-
Users can access the HungryHub WhatsApp support number
-
Users can mix and match the packages while booking
-
Users can request big group booking
-
Users can use the Come More Pay Less promotion
-
Users can use CC (on-hold)
-
Users can book without package
-
Users can have tier

-
Admins can create Malaysian restaurant
-
Admins can create Malaysian city filter on the Admin Dashboard
-
Admins can manage the Malaysian Homepage
-
Admins can manage the Malaysian Group Landing Page
-
Admins can see MYR (RM) currency on Admin Dashboard
-
Admins can set all package types to MYR currency
-
The system will use the Xendit payment gateway
-
Support payment method:
- Credit Card
- Debit Card
-
Users can earn points by completing bookings
-
Users can use voucher and promo code
-
Users can use referral program
Features hidden for Malaysia Expansion:
- Users can buy giftcard
- Users can use Gift Card to pay for the booking
Scope
Phase 1:
- Create Malaysia Country Model and database migration
- Add Malaysian cities to the database with all required data
- Extend the currency system with MYR support
- Update multi-country routing and API logic
- Implement Malaysian timezone handling (GMT+8)
- Add Malaysia to the Country Management interface
- Create a Malaysian Cities Management panel (requires city list)
- Implement restaurant tags for Malaysia (Location, Shopping Mall, Popular Zone, KTM)
- Create a City Settings panel (Banner, Featured Restaurants, Homepage settings)
- Build a Group Landing Page with the default Malaysian city
- Add MYR (RM) currency to the system
- Implement restaurant-package currency matching validation
- Add MYR support to all admin tables (Booking List, Voucher Orders)
- Create a currency lock system (only during package creation, not editing)
- Update financial management tools with MYR support
- Enable all package types for Malaysia using MYR
- Calculate package prices for Malaysia
- Enable booking creation (Pay on Site)
- Adjust Booking Confirmation API
- Create email templates for Malaysian reservations
- Adjust reservation payloads
- Add Malaysia to the homepage city dropdown
- Implement Malaysian phone number handling (default +60)
- Force Malaysian location for signups from Malaysia
- Build a Malaysian restaurant discovery interface
- Display Malaysian timezone (GMT+8)
- Add Malaysian location tags to search filters
- Create restaurant search with local tags
- Highlight and filter Halal restaurants
- Add Malaysian cuisine category filtering
- Implement MYR-based price range filtering
- Implement MYR as base currency (not estimated)
- Add currency format RM
- Enable currency switcher for users
- Create Malaysian Terms and Conditions
- Replace LINE with WhatsApp for Malaysian support
Phase 2:
- Configure Xendit payment gateway for Malaysia
- Create payment transaction monitoring for Malaysia
- Build promo code management with country isolation
- Create gift card management for Malaysia
- Enable Mix and Match booking for Malaysian restaurants
- Implement Big Group Booking for Malaysia
- Add Dynamic Pricing support for Malaysian packages
- Enable Price by Day and Price by Party Size
- Implement Come More Pay Less feature
- Add Credit Card Pre-Authorization (On Hold)
- Enable booking without packages
- Exclude We Travel Together from Malaysian packages
- Enable Credit/Debit Card processing via Xendit
- Create a Payment Method Selection UI for Malaysia
- Integrate WhatsApp for Malaysian customer support
- Create Malaysian SMS/notification services
- Build localized email templates for Malaysia
Phase 3: (Upcoming)
- Configure Omise payment gateway for Malaysia - All Omise configurations for MY are still on hold until further notice
- Add FPX payment method configuration
- Configure digital wallet payment methods
- Integrate FPX payment method via Omise
- Enable WeChat Pay and Alipay via Omise
Location
All devices
Sequence Diagram / Flow
User Booking Flow
Admin Restaurant Setup Flow
ERD
See database migrations for detailed schema.
Backend Implementation
- Added Malaysia country constants (MYS), alpha2 (MY), and MYR currency code
- Added Malaysia hungry points images (mys-en.png, mys-th.png)
- Extended
Countrymodel withfind_malaysiamethod and 1-year caching - Implemented
Country.convert_points_to_currencymethod supporting MYR (utilizing a cent-based system similar to SGD) - Added
CENT_CONVERSION_RATE = 0.01constant for SGD/MYR point conversion - Created
CountryIdMemoizationconcern for instance-level country ID caching (thailand_id, singapore_id, malaysia_id) - Extended
Citymodel withfind_malaysiamethod (defaults to Kuala Lumpur) - Unified country list ordering across entire codebase using
ApiV5::Constants::COUNTRY_CODES(THA, SGP, MYS) - Implemented MySQL FIELD() SQL function for database-level country ordering in grids
- Added Malaysia to
Country.valid_countriescached method (used in “Give points manually” admin menu) - Updated reservations controller download/reports with Malaysia filter option
- Updated restaurants controller with ordered country selection using centralized constants
- Updated ticket groups controller country selection with proper ordering
- Updated vouchers controller with Malaysia country filter and ordered display
- Enhanced manage restaurants controller with strict country presence validation and alpha3 code lookup
- Added prepayment currency auto-fallback to restaurant currency in reservation property (fixes hardcoded THB)
- Added country filter and column to cities grid with delivery_channels and country includes
- Added country association includes to all package grids (AYCE, BuffetExtra, DIY, HungryAtHome, HungrySet, PartyPack, SetMenu, Xperience)
- Added country filter and column to shared package filters module
- Added country filter with FIELD() ordering to vouchers grid
- Removed duplicate
points_by_countrymethod from VoucherForm::Reward, now usesCountry.convert_points_to_currency - Added
REGISTRATION_REWARD_AMOUNT_MYR = 20constant to Voucher model - Enhanced restaurant pricing error logs to include
restaurant_id, country_id, and currency context. - Enhanced error logging for delivery fee calculation with distance and restaurant details
- Created JavaScript country constants file (
lib/countryConstants.js) with COUNTRY_CODES, COUNTRIES array, ALPHA3_TO_ALPHA2, ALPHA2_TO_COUNTRY mappings - Updated package MainForm.vue with MYR currency option and country sorting logic
- Updated restaurant package mixin with MYR currency symbol (RM) support
- Updated signup flow (Signup.vue) with country selection, location-based country detection, country code mapping, phone input integration
- Fixed prepayment currency default from hardcoded THB to restaurant currency in reservations controller populate_seed
Phase 2 Backend Implementation:
- Implemented Omise payment gateway integration for voucher purchases with Malaysia support
- Added country-based Omise API key management with Malaysia fallback logic and configuration flags
- Extended VoucherService::CreateTransaction with multi-currency support (THB, SGD, MYR) and 3DS authentication
- Implemented MyActiveMerchants::OmiseGateway with voucher adapter and Malaysia customer handling
- Created Omise customer management for Malaysia users (omise_customer_id_my field)
- Added Malaysia phone number detection logic to User model
- Implemented voucher transaction voiding for both Omise and GB PrimePay gateways
- Enhanced API v5 voucher transactions controller to accept country_code and IP parameters
- Added VoucherTransaction total computation with cents-based currency support
- Implemented default currency logic for vouchers with Malaysia reward adjustments
- Extended AdminSetting with Malaysia payment provider defaults and constants
- Added Malaysia to payment provider mapping in instance methods
- Implemented Malaysia points expiry handling with race condition safety in RewardWorkers::PointsExpired
- Added Malaysia (total_points_my) to UserBenefitExpiry model for point tracking
- Extended LoyaltyLevel model with Malaysia benefit fields, descriptions, and translations
- Added Malaysia benefit parameters to loyalty levels controller strong params
- Enhanced LoyaltyLevelSerializer to expose Malaysia descriptions and values
- Added Malaysia loyalty benefit fields to admin show views
- Implemented daily booking limit enforcement in tier qualification progress tracking
- Added Malaysia review points mapping to ReviewPoints constants
- Added Malaysia five-review points mapping to EarlyBird constants
- Added Malaysia referral points mapping to ReferralPoints constants
- Updated ticket form to use Omise for Singapore and Malaysia tickets
- Added Malaysia referral voucher amount support (REFERRER_REWARD_AMOUNT_MYR)
- Extended reservation model with Malaysia referrer reward constant
- Added Malaysia referral banner selection to welcome email template
- Implemented currency validation with unsupported currency error messages (en, th, cn i18n)
- Enhanced PromptPay service documentation with multi-country source mapping
- Enhanced WeChat payment service documentation with multi-country behavior notes
- Improved pt-online-schema-change helper with cleanup logic and preserve-triggers option
- Enhanced SQL Helper with safer default quoting and NULL handling
- Added comprehensive test coverage for points expiry race conditions across TH/SG/MY
- Added test suite for currency validation and i18n messages in voucher transactions
- Added test suite for tier qualification daily booking limit logic
- Added Omise API keys to .env.example for Malaysia integration
Malaysia Expansion Phase 2 Malaysia Expansion Phase 2 Migration
Xendit Payment Integration (Phase 2):
- Implemented Xendit payment gateway integration for Malaysia with Payment Request API and Session JS
- Added AdminSetting XENDIT_PROVIDER constant and set Malaysia defaults to Xendit for all payment methods (credit card, PromptPay, Alipay, WeChat Pay)
- Added xendit_count_down_in_minute configuration field to AdminSetting with 10-minute default timeout
- Created Externals::Xendit module with namespace pattern for Xendit models and associations
- Created Externals::Xendit::Charge model with payment_request_id, amount_cents, currency, status, channel_code, failure_code fields and SUCCESS_STATUS/PENDING_STATUS constants
- Created Externals::Xendit::Customer model with customer_id and reservation_id for Xendit customer tracking
- Added externals_xendit_charges table with indexes on payment_request_id (unique), customer_id, reservation_id, status
- Added externals_xendit_customers table with indexes on customer_id (unique) and reservation_id
- Added xendit_customer_id_my column to users table for Malaysia customer tracking
- Extended Reservation model with xendit enum value in cc_provider enumerizer
- Extended Reservation model with xendit_customer, xendit_charges, paid_xendit_charges associations
- Implemented automatic charge/customer method overrides in Reservation credit_card concern to return correct association based on payment_provider
- Created XenditProviderChecker concern for detecting Xendit provider usage across payment types
- Created Xendit::PaymentService for creating payment sessions with Session JS mode, customer data, and 3DS support
- Enhanced HhPackage::ReservationPackages::ChargeCalculator with decimal handling for MYR amounts (fixes integer rounding issues)
- Created Xendit::WebhookHandlerService for handling payment_session.completed, payment.capture, payment_session.failed/expired, payment_session.pending/created events
- Created Xendit::CardSaverService for saving card information from payment sessions with retry logic and guest user support
- Created Api::V5::XenditPaymentSessionsController endpoint for creating payment sessions with validation for first_name, email, mobile_number, amount
- Created Admin::XenditTransactionsController with index, show, and download actions for transaction management
- Created XenditTransactionsGrid with filters for restaurant, reservation_id, status, voucher_transaction_id, ticket_transaction_id and columns for transaction details
- Implemented Xendit webhook endpoint at Api::WebhooksController#xendit for handling payment_session and payment events
- Extended ReservationService::Create with Xendit payment flow supporting both regular 3DS payments and on-hold payments (use_3d_secure=false)
- Implemented Xendit authorization URL fetching and inclusion in Firebase reservation updates for 3DS authentication
- Enhanced ReservationService::Create with payment_request_id parameter support and Xendit charge record creation
- Extended MarkReservationAsPaidService to support both Omise and Xendit charge processing with type-specific status handling
- Updated UserMailer payment_type method to return ‘Credit Card’ for Xendit (no PromptPay support)
- Added config/routes.rb routes for xendit_payment_sessions and xendit webhook endpoint
- Implemented PaymentTypeFilterConcern for filtering payment types by currency (MYR only shows credit_card)
- Extended Api::V5::RestaurantPackageSerializer with PaymentTypeFilterConcern to filter payment types by restaurant currency
- Extended Api::V5::VoucherSerializer with PaymentTypeFilterConcern to filter payment types by voucher currency
- Enhanced MyActiveMerchants::Gateway valid? method with Xendit payment_request_id validation
- Enhanced HhPackage::ReservationPackages::ChargeCalculator with decimal handling for MYR amounts (fixes integer rounding issues)
- Enhanced HhPackage::ReservationPackages::VoucherCalculator normalize_amount to return Float for decimal amounts (26 → 26, 25.5 → 25.5)
- Extended Reward model with uniqueness validation on reservation_id + user_id + points_total + description to prevent duplicate rewards
- Enhanced Agents::ModifyBookingForUser move_charges method to handle both Omise and Xendit charges/customers
- Added Xendit environment variables to .env.example (XENDIT_SECRET_KEY, XENDIT_PUBLIC_KEY)
- Updated admin_setting.en.yml locale hint to include Xendit in payment provider list
- Comprehensive RSpec test coverage for Xendit payment service, charge updater, webhook handler, card saver with 322+ specs for payment sessions controller
Mobile Implementation (Android)
Phase 1:
- Added
MYR_CURRENCY_RATESpreference constant to store Malaysian Ringgit exchange rates - Extended DashboardActivity to load MYR currency rates on initialization
- Persisted MYR rates in SharedPreferences alongside THB and SGD rates
- Updated Utils.getConvertedAndFormattedCurrency to support MYR conversion mapping
- Added MYR source currency detection in CartItemAdapter for RM price parsing
- Implemented logging when currency rates are missing locally
- Removed unused tooltip helper methods from Utils
fix: malaysia expansion adjustment (multi currency)
Phase 2:
- Added
countryCodeandcountryNamefields to Attributes.java with getter/setter methods - Updated CreateAccountFragment to detect country based on countryName attribute
- Implemented phone code detection logic supporting Malaysia (+60), Singapore (+65), and Thailand (+66)
- Added Malaysia registration reward support in account creation flow
- Localized promo text with dynamic reward amount parameter across all language variants
- Replaced hardcoded Singapore-specific promo text with parameterized strings
- Updated 14 strings.xml files across all locales (base, en, ja, km, ko, lo, ms, my, th, vi, zh-rCN, zh-rTW, zh-rHK) with dynamic promo format
- Implemented fallback logic for legacy city name handling in country detection
- Enhanced reward text visibility and display based on detected country phone code
fix: registration reward malaysia
Hybrid Implementation (Vue/Astro)
- Added Malaysia (MY) to supported countries alongside Thailand (TH) and Singapore (SG)
- Created
AvailableCountriesCodetype and country model constants in index.ts - Implemented country data helper functions in city.ts for country-aware logic
- Augmented city JSON data (cityList.json, cityListCN.json, cityListTH.json) with country fields (countryCode, countryName, countryCurrency, countryPhoneCode)
- Extended City schema to include frontend country attributes
- Updated cacheApiRespond to inject country data into city objects
- Created CountryGuestLabel.vue reusable component for country-specific guest labels
- Replaced hardcoded isSingapore boolean props with a dynamic country object across multiple components.
- Updated PointsByCountry.vue to use new country code types
- Refactored FeaturedProgramBase.vue with country-aware spend/redeem logic and visibility
- Updated FeaturedProgramDesktop.vue, FeaturedProgramMobile.vue, and RestaurantFeaturedProgramPopUp.vue to use country model
- Modified CheckOutPage.vue for country-based flows (labels, points, payment providers)
- Enhanced PointRedemptionPage.vue to use country data for currency, points, and rates
- Updated BookingPayment.vue with Thailand-only toggle and tooltip logic
- Refactored Footer.vue to replace Singapore store check with country selector utility
- Updated ProfileDesktop.vue and ProfileMobile.vue with AvailableCountriesCode type
- Modified ReferralPage.vue to use new country code type
- Enhanced CurrencyOption.vue to derive local currency from selected country
- Updated HamburgerMenuList.vue and Topbar.vue to show Thailand-only menu items (gift card, blog)
- Modified RegisterForm.vue to initialize phone/country from selected city attributes
- Updated restaurant components (RestaurantFeaturedProgramDesktop, RestaurantNewLabel, RestaurantInformation, ReviewSummariesDesktop) to use country data for currency and points
- Refactored booking components (AddOnSlider, BookingSummary, BookChangePackage) to derive currency from restaurant country data
- Updated benefit components (BenefitToBeEarnedDesktop, BenefitToBeEarnedMobile, MyBenefitDesktop, UpcomingBenefitsDesktop, UpcomingBenefitsMobile, UserPrivileges) with AvailableCountriesCode type
- Modified bookingFlow.ts payment validation to use country code and Omise rules
- Updated restaurantPage.ts to parse and expose countryData for restaurants
- Enhanced validateVoucher.ts to derive currency from restaurant country data
- Updated member.ts store to use AvailableCountriesCode
- Extended city.ts store with selected country code utilities
- Modified restaurantDetail.ts store to hold countryData and remove isSingapore flag
- Updated search.ts all-city placeholder to include country fields
- Modified Common.ts SupportedCountry type to include MY with uppercase convention
- Updated User.ts PointsByCountry to use AvailableCountriesCode
- Enhanced HomeBody.astro, RestaurantHeaderDesktop.astro, RestaurantPageDesktop.astro, RestaurantPageMobile.astro to use country utilities for Thailand-only sections
- Added Malaysia currency rate (MYR) to constants
- Implemented currency retrieval logic from countryData in multiple components
- Enhanced currency formatting to support MYR symbol (RM) display
- Updated earned points functions (earnedPoint.ts) to accept AvailableCountriesCode
- Adjusted build script order in package.json
- Imported MALAYSIA_COUNTRY_CODE constant to CheckOutPage.vue
- Extended credit card payment provider selection logic to include Malaysia
- Configured Malaysia to use Singapore credit card options (Omise gateway)
- Updated checkout flow to route Malaysia users to appropriate payment provider
- Integrated Xendit payment gateway with session and card collection utilities
- Added Xendit public key to .env.example configuration
- Created new lib/xendit.ts module with requestSessionId and collectCardData functions
- Refactored lib/omise.ts to include createOmiseToken functionality (moved from services)
- Removed deprecated services/booking/createOmiseToken.ts file
- Enhanced services/booking/CreditCard.ts with multi-provider tokenization flow (Xendit, Omise, GBPrimePay)
- Added typed token response interfaces for Xendit, Omise, and GBPrimePay
- Updated Booking.ts type definitions to include xendit as credit card provider option
- Extended createBooking.ts API schema to support xendit paymentRequestId parameter
- Added paymentRequestId state and setter to booking store
- Updated CheckOutPage.vue with Xendit payload generation and 3DS authentication flow
- Enhanced BookingPayment.ts to handle Xendit temporary card responses and unified error handling
- Created partial/booking/XenditForm.vue component for demo/test form with Xendit script loader
- Added pages/[lang]/xendit.astro test page for Xendit payment integration testing
- Updated AddNewCreditCardMobile.vue to pass user data and amount to tokenization services
- Added comprehensive automation test coverage for Malaysian market (prepayment, pay now, on-hold scenarios)
- Created ThreeDSXendit.js page object for handling Xendit 3DS authentication in tests
- Enhanced astro-checkoutPage.js with fillCheckoutFormAsGuestCC, usePromoCodeMYR, selectSingleGiftCard, and selectMultipleGiftCard methods
- Updated astro-confirmationPage.js to support prepayment deposit verification
- Enhanced astro-reservationPage.js with selectTogglePayNow and selectCreditCardPaymentXendit methods
- Added formatCurrencyMYR helper function to test utilities
- Created 14 new test spec files covering guest and member Malaysian reservation flows
- Updated Singapore test specs with @singapore-expansion tags for better test organization
Malaysia Expansion Malaysia Currency Support Malaysia Expansion Phase 2 Xendit Payment Integration
PRD & Task
PRD: Malaysia Expansion (https://doc.clickup.com/d/h/8ca1fpw-55296/f50d01f7b1a6685) Task : Malaysia Expansion Task : Malaysia Expansion (Left over) Task : Malaysia Post Release
Design
https://www.canva.com/design/DAG0C0wLLJw/K3-bzQlKUZQn39b_nwqENg/edit
API Blueprint
| Method | Path | URL | Description | Payload |
|---|---|---|---|---|
| GET | /api/v5/countries | - | Returns ordered countries (TH, SG, MY) | - |
| GET | /api/v5/restaurants/calculate_package_price | - | Supports MYR pricing calculation with enhanced error logging | restaurant_id, distance, adult, kids |
API Updates:
- Country-specific filtering in admin endpoints
- Enhanced error responses with currency/country context (restaurant_id, country_id, currency)
- Ordered country list returned via
ApiV5::Constants::COUNTRY_CODESarray
New Query
See API Blueprint section for query details.
DB Schema / Database Migration
Phase 1:
- Add data migration to add Malaysia country to the countries table
- Add data migration to add Malaysian cities to the cities table
Phase 2:
- Added
omise_customer_id_mystring column touserstable with index for Malaysia Omise customer tracking - Added
total_points_myinteger column touser_benefit_expiriestable for Malaysia points tracking - Added Malaysia benefit fields to
loyalty_levelstable:dine_in_discount_rate_my(float) - dine-in discount percentagebirthday_voucher_amount_my(float) - birthday voucher valuereview_voucher_amount_my(integer) - review reward voucher amountfive_review_voucher_amount_my(integer) - five-review milestone voucherreferral_voucher_amount_my(integer) - referral reward voucher
- Added Malaysia benefit description fields to
loyalty_level_translationstable:dine_in_description_my(text) - localized dine-in benefit descriptionreview_description_my(text) - localized review benefit descriptionreferral_description_my(text) - localized referral benefit description
- Created data migration to populate Malaysia loyalty benefits for all tiers (HUNGER through PLATINUM)
- Enhanced pt-online-schema-change initializer with artifact cleanup and
--preserve-triggersoption - All Phase 2 schema changes use pt-online-schema-change for zero-downtime migrations
Improvement
No improvements recorded yet.