Payment — Core concepts
| Concept | Meaning |
|---|---|
| PaymentOption | Chosen/saved payment method on billing or checkout: PayLater, PayNow, Direct Debit, Deposit... |
| Payment method availability | Rules deciding which methods are usable by BU, site, category, provider and master toggle. |
| PayLater | Let booking/invoice be created while collection happens later. |
| PayNow | Collect immediately via a card/checkout provider. |
| Windcave | Checkout/card provider; uses redirect/result/webhook to update status. |
| Direct Debit | Automatic collection via customer token/batch. |
| Kiwi Direct Debit | Direct Debit/provider-specific flow requiring BU scoping and customer create/sync. |
| Deposit | Partial upfront payment for a booking; does not mean the invoice is fully paid. |
| DirectPaymentBatch | Batch/provider transaction group for multiple direct payments or deposit payments. |
| AccountPaymentCustomer | Token/customer payment profile by account + BU + provider. |
| Zero-amount checkout | Checkout with total payable = 0: skip the provider, use Proceed/Submit then auto-confirm or keep pending. |
| Checkout background finalization | Popup saying the system is finalizing the booking in the background after the provider returns success. |
| PayLaterDisplayName | Consumer-facing PayLater display name configured at BU level; fallback "Pay Later". |
| Master toggle | Switch to enable/disable a method at BU level; state derived from whether the provider is configured. |
Payment option vs provider
PaymentOption is the business/customer-facing choice. Provider is the external system processing the transaction. For example, PayNow can use Windcave; Direct Debit can use Kiwi. Availability configuration decides whether an option is shown or allowed for submit.
Deposit is not full payment
Deposit can confirm a booking after collecting a partial amount, but billing generation may still create a full-amount invoice. The remaining settlement belongs to the later payment/finance workflow.
PayLater category eligibility
ProgramCategory.PayLaterAllowedTypeId is a category-scope rule; valid values depend on the Program Category type and subsidy.
| Program Category type | Valid PayLaterAllowedTypeId values |
|---|---|
| Holiday / Class | None, Subsidy, SubsidyApproved, All |
| Term Care | None, FullTime, PartTime, Subsidy, SubsidyApproved, FullTimeOrSubsidy, FullTimeOrSubsidyApproved, PartTimeOrSubsidy, PartTimeOrSubsidyApproved, FullTimeAndSubsidy, FullTimeAndSubsidyApproved, PartTimeAndSubsidy, PartTimeAndSubsidyApproved, All |
- Subsidy-related values (
Subsidy,SubsidyApproved,*OrSubsidy*,*AndSubsidy*) are only valid when subsidy is enabled for the category. - Eligibility follows booking classification precedence
FULL_TIME > PART_TIME > CASUAL: ≥1 Full Time session → counts as Full Time; no FT but has PT → Part Time; only Casual → does not satisfy FT/PT. - The
*Approvedvariants require subsidy approved status;*AndSubsidy*requires both conditions at once.
Real-world example: A booking has 1 Full Time session + 3 Casual sessions, category set to FullTimeOrSubsidyApproved. → Determine whether the booking satisfies the FT rule. → It counts as Full Time by precedence → Pay Later is available even without any subsidy.
Master toggle vs category availability
- Master toggle = enable/disable provider at BU level (
DirectPaymentProvider,PrimaryPayNowProvider...). - Category availability = booking eligibility per ProgramCategory.
- The two layers are independent: changing availability does not change the provider; changing the toggle does not rewrite the category option.
- Checkout only shows a method when both pass: provider enabled at the BU and category allows it.
Real-world example: The BU has configured the Direct Debit provider but category A is set to Disabled. → Avoid confusing the two config layers. → The DD section stays enabled in settings, but customers checking out category A do not see Direct Debit.
Zero-amount checkout bypass
- Total payable =
0→ no provider init/redirect regardless of the chosen method. - Immediate-collection methods (Pay Now, Pay Deposit, Direct Debit) use the
Proceedbutton: confirm the booking and show the confirmed success surface. - Pay Later is the only method → use
Submit: keep the bookingSubmitted/pending and show the pending message. Submit: eligible for auto-confirm (Pay Now full/deposit, DD sign-up, DD include-current-booking) → confirm; otherwise → keep pending.- Auto-confirm intent resolves from payment settings, DD state, selected option, eligibility — not from display text.
Real-world example: A customer has already paid in full before; a new booking checkout has total = 0. → Avoid redirecting to Windcave for a zero-amount charge. → Pick Pay Now → the Proceed button appears → confirm immediately, no provider is opened.
Checkout background finalization
- After the provider returns success (total payable > 0), the portal shows a popup saying it is finalizing the booking in the background.
- The popup asks the user not to leave the page or close the browser; it does not require clicking
OK. - When done → the popup closes itself and returns to the resolved success state.
Real-world example: A customer pays 90,000 VND by card and Windcave redirects back to the portal. → The system has not finished confirming the booking. → A popup "processing, don't close the browser" appears and disappears by itself when finalization completes.
Payment method availability semantics
SubsidyEnabledis the gate:false→ options containing "Subsidy Intended"/"Excludes Subsidy-Intended" cannot be selected; a violating save is rejected.- "Excludes Subsidy-Intended" removes bookings with subsidy intent from eligibility.
- "Approved Profiles Only" can no longer be selected for new configs; legacy values are still readable and the UI shows the corresponding non-approved label.
- Multi-order/category checkout: a method only shows when every order is eligible.
- The server does not trust the UI's hidden state: submitting a hidden method → reject before confirm/payment.
Real-world example: The cart has 2 orders from 2 categories; one category sets Direct Debit = All Bookings (Excludes Subsidy-Intended) and that order is subsidy-intended. → Avoid offering an invalid method. → Direct Debit is not shown for the whole cart because not every order is eligible.
Ownership summary
| Owner | Owns |
|---|---|
| Payment | Provider transaction, payment customer token, batch lifecycle, checkout result. |
| Finance | Invoice/billing lifecycle and financial documents. |
| BookingOrder | Booking lock/deposit snapshot related to booking confirmation. |
| BusinessUnit | Method/provider settings and availability scope. |