@wix/headless-bookings 0.0.117 → 0.0.118

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@
4
4
  * Note: Currently supports single service only
5
5
  */
6
6
  import React from 'react';
7
- import { type PaymentServiceConfig, type PricingServiceSelection } from '../../../services/payment/payment.js';
7
+ import { type PaymentServiceConfig, type PricingServiceSelection, type ServicePaymentOptions } from '../../../services/payment/payment.js';
8
8
  import type { MoneyData } from '@wix/headless-components/react';
9
9
  /**
10
10
  * Props for Payment.Root component
@@ -164,6 +164,19 @@ export interface LineItemProviderProps {
164
164
  * ```
165
165
  */
166
166
  export declare function LineItemProvider(props: LineItemProviderProps): React.ReactNode;
167
+ /**
168
+ * Props for ServiceOptions render prop component
169
+ */
170
+ export interface ServiceOptionsProps {
171
+ children: (options: ServicePaymentOptions) => React.ReactNode;
172
+ }
173
+ /**
174
+ * Core component that exposes service payment options via render props.
175
+ * Reads isOnline, isInPerson, isDeposit from the payment service signal.
176
+ *
177
+ * @component
178
+ */
179
+ export declare function ServiceOptions(props: ServiceOptionsProps): React.ReactNode;
167
180
  /**
168
181
  * Props for LineItemInfo render prop component
169
182
  */
@@ -8,6 +8,7 @@ exports.Data = Data;
8
8
  exports.LineItems = LineItems;
9
9
  exports.useLineItemContext = useLineItemContext;
10
10
  exports.LineItemProvider = LineItemProvider;
11
+ exports.ServiceOptions = ServiceOptions;
11
12
  exports.LineItemInfo = LineItemInfo;
12
13
  const jsx_runtime_1 = require("react/jsx-runtime");
13
14
  /**
@@ -199,6 +200,17 @@ function LineItemProvider(props) {
199
200
  };
200
201
  return ((0, jsx_runtime_1.jsx)(LineItemContext.Provider, { value: contextValue, children: children }));
201
202
  }
203
+ /**
204
+ * Core component that exposes service payment options via render props.
205
+ * Reads isOnline, isInPerson, isDeposit from the payment service signal.
206
+ *
207
+ * @component
208
+ */
209
+ function ServiceOptions(props) {
210
+ const paymentService = (0, services_manager_react_1.useService)(payment_js_1.PaymentServiceDefinition);
211
+ const serviceOptions = paymentService.serviceOptionsSignal.get();
212
+ return props.children(serviceOptions);
213
+ }
202
214
  /**
203
215
  * Core component that provides access to line item info via render props.
204
216
  * Must be used within LineItemProvider.
@@ -5,7 +5,7 @@
5
5
  import React from 'react';
6
6
  import { AsChildChildren } from '@wix/headless-utils/react';
7
7
  import { MoneyData } from '@wix/headless-components/react';
8
- import type { PaymentServiceConfig, PricingServiceSelection } from '../../services/payment/payment.js';
8
+ import type { PaymentServiceConfig, PricingServiceSelection, ServicePaymentOptions } from '../../services/payment/payment.js';
9
9
  export declare const TestIds: {
10
10
  readonly paymentRoot: "payment-root";
11
11
  readonly paymentSubtotal: "payment-subtotal";
@@ -19,6 +19,7 @@ export declare const TestIds: {
19
19
  readonly paymentLineItemTotal: "payment-line-item-total";
20
20
  readonly paymentLineItemSubtotal: "payment-line-item-subtotal";
21
21
  readonly paymentLineItemDeposit: "payment-line-item-deposit";
22
+ readonly paymentServiceOptions: "payment-service-options";
22
23
  };
23
24
  /**
24
25
  * Props for Payment.Root component
@@ -194,6 +195,41 @@ export interface PayLaterProps extends Omit<React.HTMLAttributes<HTMLElement>, '
194
195
  * ```
195
196
  */
196
197
  export declare const PayLater: React.ForwardRefExoticComponent<PayLaterProps & React.RefAttributes<HTMLElement>>;
198
+ /**
199
+ * Props for Payment.ServiceOptions component
200
+ */
201
+ export interface ServiceOptionsProps {
202
+ asChild?: boolean;
203
+ /** Render prop receiving service payment options. This component renders no
204
+ * visible DOM element by default — it is a flags-only data component. */
205
+ children?: AsChildChildren<ServicePaymentOptions>;
206
+ }
207
+ /**
208
+ * Exposes service payment options via render props.
209
+ * Use this to determine which payment scenario to render without reading the service entity directly.
210
+ *
211
+ * Payment scenarios:
212
+ * - `isOnline=true, isInPerson=false, isDeposit=false` → Pay now only (Due now = total)
213
+ * - `isOnline=true, isInPerson=false, isDeposit=true` → Pay now + security deposit
214
+ * - `isOnline=true, isInPerson=true` → Radio: Pay Online / Pay on Arrival
215
+ * - `isOnline=false, isInPerson=true` → Pay on arrival only
216
+ *
217
+ * @component
218
+ * Note: This component renders no visible DOM element. It is a flags-only data
219
+ * component — use `asChild` with a render function to consume the flags.
220
+ * For the actual split amounts (deposit / pay-later), use `Payment.PayNow` and
221
+ * `Payment.PayLater`.
222
+ *
223
+ * @example
224
+ * ```tsx
225
+ * <Payment.ServiceOptions asChild>
226
+ * {({ isOnline, isInPerson, isDeposit }) => (
227
+ * // render the correct payment scenario
228
+ * )}
229
+ * </Payment.ServiceOptions>
230
+ * ```
231
+ */
232
+ export declare const ServiceOptions: React.ForwardRefExoticComponent<ServiceOptionsProps & React.RefAttributes<HTMLElement>>;
197
233
  /**
198
234
  * Props for Payment.LineItems component (Container Level)
199
235
  */
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.LineItemDeposit = exports.LineItemSubtotal = exports.LineItemTotal = exports.LineItemRepeater = exports.LineItemsList = exports.LineItems = exports.PayLater = exports.PayNow = exports.Total = exports.Tax = exports.Subtotal = exports.Root = exports.TestIds = void 0;
39
+ exports.LineItemDeposit = exports.LineItemSubtotal = exports.LineItemTotal = exports.LineItemRepeater = exports.LineItemsList = exports.LineItems = exports.ServiceOptions = exports.PayLater = exports.PayNow = exports.Total = exports.Tax = exports.Subtotal = exports.Root = exports.TestIds = void 0;
40
40
  const jsx_runtime_1 = require("react/jsx-runtime");
41
41
  /**
42
42
  * Payment - High-level component for displaying payment information
@@ -59,6 +59,7 @@ exports.TestIds = {
59
59
  paymentLineItemTotal: 'payment-line-item-total',
60
60
  paymentLineItemSubtotal: 'payment-line-item-subtotal',
61
61
  paymentLineItemDeposit: 'payment-line-item-deposit',
62
+ paymentServiceOptions: 'payment-service-options',
62
63
  };
63
64
  /**
64
65
  * Root component that provides payment context to the entire app.
@@ -200,6 +201,36 @@ exports.PayLater = react_1.default.forwardRef((props, ref) => {
200
201
  return ((0, jsx_runtime_1.jsx)(CorePayment.Data, { children: ({ payLater, isDeposit }) => ((0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { ref: ref, asChild: asChild, className: className, customElement: children, customElementProps: { payLater, isDeposit }, "data-testid": exports.TestIds.paymentPayLater, "data-has-deposit": isDeposit, children: isDeposit && payLater && payLater.amount > 0 && ((0, jsx_runtime_1.jsx)(react_3.Money, { money: payLater })) })) }));
201
202
  });
202
203
  exports.PayLater.displayName = 'Payment.PayLater';
204
+ /**
205
+ * Exposes service payment options via render props.
206
+ * Use this to determine which payment scenario to render without reading the service entity directly.
207
+ *
208
+ * Payment scenarios:
209
+ * - `isOnline=true, isInPerson=false, isDeposit=false` → Pay now only (Due now = total)
210
+ * - `isOnline=true, isInPerson=false, isDeposit=true` → Pay now + security deposit
211
+ * - `isOnline=true, isInPerson=true` → Radio: Pay Online / Pay on Arrival
212
+ * - `isOnline=false, isInPerson=true` → Pay on arrival only
213
+ *
214
+ * @component
215
+ * Note: This component renders no visible DOM element. It is a flags-only data
216
+ * component — use `asChild` with a render function to consume the flags.
217
+ * For the actual split amounts (deposit / pay-later), use `Payment.PayNow` and
218
+ * `Payment.PayLater`.
219
+ *
220
+ * @example
221
+ * ```tsx
222
+ * <Payment.ServiceOptions asChild>
223
+ * {({ isOnline, isInPerson, isDeposit }) => (
224
+ * // render the correct payment scenario
225
+ * )}
226
+ * </Payment.ServiceOptions>
227
+ * ```
228
+ */
229
+ exports.ServiceOptions = react_1.default.forwardRef((props, ref) => {
230
+ const { asChild, children } = props;
231
+ return ((0, jsx_runtime_1.jsx)(CorePayment.ServiceOptions, { children: ({ isOnline, isInPerson, isDeposit }) => ((0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { ref: ref, asChild: asChild, customElement: children, customElementProps: { isOnline, isInPerson, isDeposit }, "data-testid": exports.TestIds.paymentServiceOptions, "data-is-online": isOnline, "data-is-in-person": isInPerson, "data-is-deposit": isDeposit, children: null })) }));
232
+ });
233
+ exports.ServiceOptions.displayName = 'Payment.ServiceOptions';
203
234
  /**
204
235
  * Container component for line items.
205
236
  * Follows the 3-level List/Options/Repeater pattern.
@@ -9,6 +9,6 @@ export { ServiceListService, ServiceListServiceDefinition, type ServiceListServi
9
9
  export { SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, } from '../api/query-services/index.js';
10
10
  export { executeBookAction, canBook, buildBookingRequest, buildCheckoutRequest, type CanBookParams, type BookResult, type BookingError, type BookActionParams, type BookChildProps, type BookProps, type BuildCheckoutParams, } from './booking/book-action/index.js';
11
11
  export { BookingFormService, BookingFormServiceDefinition, type BookingFormServiceAPI, type BookingFormServiceConfig, } from './booking-form/booking-form.js';
12
- export { PaymentService, PaymentServiceDefinition, type PaymentServiceAPI, type PaymentServiceConfig, type PaymentDetails, type PricingServiceSelection, type LineItemAdditionalInfo, loadPaymentConfig, type SuccessPaymentConfigResult, type ErrorPaymentConfigResult, } from './payment/payment.js';
12
+ export { PaymentService, PaymentServiceDefinition, type PaymentServiceAPI, type PaymentServiceConfig, type PaymentDetails, type PricingServiceSelection, type LineItemAdditionalInfo, type ServicePaymentOptions, loadPaymentConfig, type SuccessPaymentConfigResult, type ErrorPaymentConfigResult, } from './payment/payment.js';
13
13
  export { LocationListService, LocationListServiceDefinition, LocationType, loadLocationListServiceInitialData, locationListServiceBinding, type LocationListServiceAPI, type LocationListServiceConfig, type LocationListActions, type Location, type LoadLocationListServiceResult, getLocationById, } from './location-list/location-list.js';
14
14
  export { StaffMemberListService, StaffMemberListServiceDefinition, loadStaffMemberListServiceInitialData, staffMemberListServiceBinding, type StaffMemberListServiceAPI, type StaffMemberListServiceConfig, type StaffMemberListActions, type StaffMemberData, type LoadStaffMemberListServiceResult, } from './staff-member-list/staff-member-list.js';
@@ -4,7 +4,7 @@
4
4
  * Supports two modes: SSR (config with pre-fetched data) and lazy loading (reactive)
5
5
  */
6
6
  import { type PaymentServiceConfig, type PaymentServiceInternalConfig, type PricingServiceSelection } from './payment.def.js';
7
- export { PaymentServiceDefinition, type PaymentServiceConfig, type PaymentDetails, type LineItemAdditionalInfo, type PricingServiceSelection, } from './payment.def.js';
7
+ export { PaymentServiceDefinition, type PaymentServiceConfig, type PaymentDetails, type LineItemAdditionalInfo, type PricingServiceSelection, type ServicePaymentOptions, } from './payment.def.js';
8
8
  export type { PaymentServiceAPI, PaymentServiceInternalConfig, } from './payment.def.js';
9
9
  export declare const PaymentService: import("@wix/services-definitions").ServiceFactory<string & {
10
10
  __api: import("./payment.def.js").PaymentServiceAPI;
@@ -5,6 +5,18 @@
5
5
  import type { Signal } from '@wix/services-definitions/core-services/signals';
6
6
  import type { CalculateTotalsResponse } from '@wix/auto_sdk_ecom_totals-calculator';
7
7
  import type { Service } from '@wix/auto_sdk_bookings_services';
8
+ /**
9
+ * Service payment options derived from service.payment.options.
10
+ * Describes which payment methods the service supports.
11
+ */
12
+ export interface ServicePaymentOptions {
13
+ /** Customer can pay online at booking time */
14
+ isOnline: boolean;
15
+ /** Customer can pay in person (on arrival) */
16
+ isInPerson: boolean;
17
+ /** A deposit is required at booking time */
18
+ isDeposit: boolean;
19
+ }
8
20
  /**
9
21
  * Payment option for line items
10
22
  */
@@ -44,6 +56,8 @@ export interface PricingServiceSelection {
44
56
  export interface PaymentServiceConfig {
45
57
  /** Pre-calculated payment details for SSR (no API call) */
46
58
  paymentDetails?: PaymentDetails;
59
+ /** Service payment options for SSR (derived from service.payment.options) */
60
+ serviceOptions?: ServicePaymentOptions;
47
61
  }
48
62
  /**
49
63
  * Internal config used by Core component to pass lazy loading data
@@ -59,6 +73,7 @@ export interface PaymentServiceAPI {
59
73
  paymentDetailsSignal: Signal<PaymentDetails | null>;
60
74
  isLoadingSignal: Signal<boolean>;
61
75
  errorSignal: Signal<string | null>;
76
+ serviceOptionsSignal: Signal<ServicePaymentOptions>;
62
77
  }
63
78
  export declare const PaymentServiceDefinition: string & {
64
79
  __api: PaymentServiceAPI;
@@ -20,6 +20,35 @@ const constants_js_1 = require("../constants.js");
20
20
  // ============================================================================
21
21
  // Helper Functions
22
22
  // ============================================================================
23
+ const DEFAULT_SERVICE_OPTIONS = {
24
+ isOnline: false,
25
+ isInPerson: false,
26
+ isDeposit: false,
27
+ };
28
+ /**
29
+ * Derives static service payment options from the first service's configuration.
30
+ *
31
+ * Note on `isDeposit`: this reflects whether the service is *configured* with a
32
+ * deposit (i.e. `payment.fixed.deposit.value > 0`), consistent with how
33
+ * `buildLineItem` determines `hasDeposit`. Only `FIXED` rate type is checked;
34
+ * `VARIED` pricing is not currently supported for deposit detection here.
35
+ *
36
+ * This is distinct from the runtime `isDeposit` flag in `CorePayment.Data`, which
37
+ * is computed dynamically from the ecom API result (`payNow.amount < total.amount`).
38
+ * Use `Payment.ServiceOptions` to determine *which scenario to render* (static config),
39
+ * and `Payment.PayNow`/`Payment.PayLater` for the *actual split amounts* (runtime).
40
+ */
41
+ function deriveServiceOptions(pricingServiceSelections) {
42
+ const service = pricingServiceSelections[0]?.service;
43
+ const options = service?.payment?.options;
44
+ const depositValue = service?.payment?.fixed?.deposit?.value;
45
+ const hasDeposit = !!depositValue && Number(depositValue) > 0;
46
+ return {
47
+ isOnline: !!options?.online,
48
+ isInPerson: !!options?.inPerson,
49
+ isDeposit: hasDeposit,
50
+ };
51
+ }
23
52
  function getAdditionalInfo(service, lineItemId) {
24
53
  const payment = service.payment;
25
54
  const customPrice = payment?.rateType === auto_sdk_bookings_services_1.RateType.CUSTOM && payment.custom?.description
@@ -97,6 +126,7 @@ exports.PaymentService = services_definitions_1.implementService.withConfig()(pa
97
126
  const paymentDetailsSignal = signalsService.signal(config.paymentDetails || null);
98
127
  const isLoadingSignal = signalsService.signal(false);
99
128
  const errorSignal = signalsService.signal(null);
129
+ const serviceOptionsSignal = signalsService.signal(config.serviceOptions || DEFAULT_SERVICE_OPTIONS);
100
130
  // Internal calculate function
101
131
  const calculate = async (pricingServiceSelections, totalParticipants) => {
102
132
  if (!pricingServiceSelections.length) {
@@ -122,10 +152,18 @@ exports.PaymentService = services_definitions_1.implementService.withConfig()(pa
122
152
  };
123
153
  // Mode 1: SSR - paymentDetails already set in signal initialization
124
154
  if (config.paymentDetails) {
125
- // No-op - data is pre-fetched
155
+ if (!config.serviceOptions &&
156
+ process.env['NODE_ENV'] !== 'production') {
157
+ console.warn('[PaymentService] SSR mode: `serviceOptions` was not provided in config. ' +
158
+ 'Payment.ServiceOptions will return all-false defaults. ' +
159
+ 'Use `loadPaymentConfig` to automatically derive serviceOptions, ' +
160
+ 'or pass `serviceOptions` explicitly in `paymentServiceConfig`.');
161
+ }
162
+ // serviceOptionsSignal already initialized from config.serviceOptions (or DEFAULT_SERVICE_OPTIONS)
126
163
  }
127
164
  // Mode 2: Lazy loading - pricingServiceSelections provided via internal config
128
165
  else if (config.pricingServiceSelections?.length) {
166
+ serviceOptionsSignal.set(deriveServiceOptions(config.pricingServiceSelections));
129
167
  void calculate(config.pricingServiceSelections, config.totalParticipants ?? 1);
130
168
  }
131
169
  // Mode 3: Reactive - observe BookingService signals (client-side only)
@@ -147,6 +185,9 @@ exports.PaymentService = services_definitions_1.implementService.withConfig()(pa
147
185
  service: selection.service,
148
186
  lineItemId: selection.instanceId,
149
187
  }));
188
+ if (pricingServiceSelections.length) {
189
+ serviceOptionsSignal.set(deriveServiceOptions(pricingServiceSelections));
190
+ }
150
191
  void calculate(pricingServiceSelections, numParticipants);
151
192
  });
152
193
  }
@@ -154,6 +195,7 @@ exports.PaymentService = services_definitions_1.implementService.withConfig()(pa
154
195
  paymentDetailsSignal,
155
196
  isLoadingSignal,
156
197
  errorSignal,
198
+ serviceOptionsSignal,
157
199
  };
158
200
  });
159
201
  /**
@@ -169,13 +211,19 @@ async function loadPaymentConfig(params) {
169
211
  if (!pricingServiceSelections.length) {
170
212
  return {
171
213
  type: 'success',
172
- config: { paymentDetails: undefined },
214
+ config: {
215
+ paymentDetails: undefined,
216
+ serviceOptions: DEFAULT_SERVICE_OPTIONS,
217
+ },
173
218
  };
174
219
  }
175
220
  const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, totalParticipants);
176
221
  return {
177
222
  type: 'success',
178
- config: { paymentDetails },
223
+ config: {
224
+ paymentDetails,
225
+ serviceOptions: deriveServiceOptions(pricingServiceSelections),
226
+ },
179
227
  };
180
228
  }
181
229
  catch (error) {
@@ -4,7 +4,7 @@
4
4
  * Note: Currently supports single service only
5
5
  */
6
6
  import React from 'react';
7
- import { type PaymentServiceConfig, type PricingServiceSelection } from '../../../services/payment/payment.js';
7
+ import { type PaymentServiceConfig, type PricingServiceSelection, type ServicePaymentOptions } from '../../../services/payment/payment.js';
8
8
  import type { MoneyData } from '@wix/headless-components/react';
9
9
  /**
10
10
  * Props for Payment.Root component
@@ -164,6 +164,19 @@ export interface LineItemProviderProps {
164
164
  * ```
165
165
  */
166
166
  export declare function LineItemProvider(props: LineItemProviderProps): React.ReactNode;
167
+ /**
168
+ * Props for ServiceOptions render prop component
169
+ */
170
+ export interface ServiceOptionsProps {
171
+ children: (options: ServicePaymentOptions) => React.ReactNode;
172
+ }
173
+ /**
174
+ * Core component that exposes service payment options via render props.
175
+ * Reads isOnline, isInPerson, isDeposit from the payment service signal.
176
+ *
177
+ * @component
178
+ */
179
+ export declare function ServiceOptions(props: ServiceOptionsProps): React.ReactNode;
167
180
  /**
168
181
  * Props for LineItemInfo render prop component
169
182
  */
@@ -188,6 +188,17 @@ export function LineItemProvider(props) {
188
188
  };
189
189
  return (_jsx(LineItemContext.Provider, { value: contextValue, children: children }));
190
190
  }
191
+ /**
192
+ * Core component that exposes service payment options via render props.
193
+ * Reads isOnline, isInPerson, isDeposit from the payment service signal.
194
+ *
195
+ * @component
196
+ */
197
+ export function ServiceOptions(props) {
198
+ const paymentService = useService(PaymentServiceDefinition);
199
+ const serviceOptions = paymentService.serviceOptionsSignal.get();
200
+ return props.children(serviceOptions);
201
+ }
191
202
  /**
192
203
  * Core component that provides access to line item info via render props.
193
204
  * Must be used within LineItemProvider.
@@ -5,7 +5,7 @@
5
5
  import React from 'react';
6
6
  import { AsChildChildren } from '@wix/headless-utils/react';
7
7
  import { MoneyData } from '@wix/headless-components/react';
8
- import type { PaymentServiceConfig, PricingServiceSelection } from '../../services/payment/payment.js';
8
+ import type { PaymentServiceConfig, PricingServiceSelection, ServicePaymentOptions } from '../../services/payment/payment.js';
9
9
  export declare const TestIds: {
10
10
  readonly paymentRoot: "payment-root";
11
11
  readonly paymentSubtotal: "payment-subtotal";
@@ -19,6 +19,7 @@ export declare const TestIds: {
19
19
  readonly paymentLineItemTotal: "payment-line-item-total";
20
20
  readonly paymentLineItemSubtotal: "payment-line-item-subtotal";
21
21
  readonly paymentLineItemDeposit: "payment-line-item-deposit";
22
+ readonly paymentServiceOptions: "payment-service-options";
22
23
  };
23
24
  /**
24
25
  * Props for Payment.Root component
@@ -194,6 +195,41 @@ export interface PayLaterProps extends Omit<React.HTMLAttributes<HTMLElement>, '
194
195
  * ```
195
196
  */
196
197
  export declare const PayLater: React.ForwardRefExoticComponent<PayLaterProps & React.RefAttributes<HTMLElement>>;
198
+ /**
199
+ * Props for Payment.ServiceOptions component
200
+ */
201
+ export interface ServiceOptionsProps {
202
+ asChild?: boolean;
203
+ /** Render prop receiving service payment options. This component renders no
204
+ * visible DOM element by default — it is a flags-only data component. */
205
+ children?: AsChildChildren<ServicePaymentOptions>;
206
+ }
207
+ /**
208
+ * Exposes service payment options via render props.
209
+ * Use this to determine which payment scenario to render without reading the service entity directly.
210
+ *
211
+ * Payment scenarios:
212
+ * - `isOnline=true, isInPerson=false, isDeposit=false` → Pay now only (Due now = total)
213
+ * - `isOnline=true, isInPerson=false, isDeposit=true` → Pay now + security deposit
214
+ * - `isOnline=true, isInPerson=true` → Radio: Pay Online / Pay on Arrival
215
+ * - `isOnline=false, isInPerson=true` → Pay on arrival only
216
+ *
217
+ * @component
218
+ * Note: This component renders no visible DOM element. It is a flags-only data
219
+ * component — use `asChild` with a render function to consume the flags.
220
+ * For the actual split amounts (deposit / pay-later), use `Payment.PayNow` and
221
+ * `Payment.PayLater`.
222
+ *
223
+ * @example
224
+ * ```tsx
225
+ * <Payment.ServiceOptions asChild>
226
+ * {({ isOnline, isInPerson, isDeposit }) => (
227
+ * // render the correct payment scenario
228
+ * )}
229
+ * </Payment.ServiceOptions>
230
+ * ```
231
+ */
232
+ export declare const ServiceOptions: React.ForwardRefExoticComponent<ServiceOptionsProps & React.RefAttributes<HTMLElement>>;
197
233
  /**
198
234
  * Props for Payment.LineItems component (Container Level)
199
235
  */
@@ -20,6 +20,7 @@ export const TestIds = {
20
20
  paymentLineItemTotal: 'payment-line-item-total',
21
21
  paymentLineItemSubtotal: 'payment-line-item-subtotal',
22
22
  paymentLineItemDeposit: 'payment-line-item-deposit',
23
+ paymentServiceOptions: 'payment-service-options',
23
24
  };
24
25
  /**
25
26
  * Root component that provides payment context to the entire app.
@@ -161,6 +162,36 @@ export const PayLater = React.forwardRef((props, ref) => {
161
162
  return (_jsx(CorePayment.Data, { children: ({ payLater, isDeposit }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, customElement: children, customElementProps: { payLater, isDeposit }, "data-testid": TestIds.paymentPayLater, "data-has-deposit": isDeposit, children: isDeposit && payLater && payLater.amount > 0 && (_jsx(Money, { money: payLater })) })) }));
162
163
  });
163
164
  PayLater.displayName = 'Payment.PayLater';
165
+ /**
166
+ * Exposes service payment options via render props.
167
+ * Use this to determine which payment scenario to render without reading the service entity directly.
168
+ *
169
+ * Payment scenarios:
170
+ * - `isOnline=true, isInPerson=false, isDeposit=false` → Pay now only (Due now = total)
171
+ * - `isOnline=true, isInPerson=false, isDeposit=true` → Pay now + security deposit
172
+ * - `isOnline=true, isInPerson=true` → Radio: Pay Online / Pay on Arrival
173
+ * - `isOnline=false, isInPerson=true` → Pay on arrival only
174
+ *
175
+ * @component
176
+ * Note: This component renders no visible DOM element. It is a flags-only data
177
+ * component — use `asChild` with a render function to consume the flags.
178
+ * For the actual split amounts (deposit / pay-later), use `Payment.PayNow` and
179
+ * `Payment.PayLater`.
180
+ *
181
+ * @example
182
+ * ```tsx
183
+ * <Payment.ServiceOptions asChild>
184
+ * {({ isOnline, isInPerson, isDeposit }) => (
185
+ * // render the correct payment scenario
186
+ * )}
187
+ * </Payment.ServiceOptions>
188
+ * ```
189
+ */
190
+ export const ServiceOptions = React.forwardRef((props, ref) => {
191
+ const { asChild, children } = props;
192
+ return (_jsx(CorePayment.ServiceOptions, { children: ({ isOnline, isInPerson, isDeposit }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, customElement: children, customElementProps: { isOnline, isInPerson, isDeposit }, "data-testid": TestIds.paymentServiceOptions, "data-is-online": isOnline, "data-is-in-person": isInPerson, "data-is-deposit": isDeposit, children: null })) }));
193
+ });
194
+ ServiceOptions.displayName = 'Payment.ServiceOptions';
164
195
  /**
165
196
  * Container component for line items.
166
197
  * Follows the 3-level List/Options/Repeater pattern.
@@ -9,6 +9,6 @@ export { ServiceListService, ServiceListServiceDefinition, type ServiceListServi
9
9
  export { SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, } from '../api/query-services/index.js';
10
10
  export { executeBookAction, canBook, buildBookingRequest, buildCheckoutRequest, type CanBookParams, type BookResult, type BookingError, type BookActionParams, type BookChildProps, type BookProps, type BuildCheckoutParams, } from './booking/book-action/index.js';
11
11
  export { BookingFormService, BookingFormServiceDefinition, type BookingFormServiceAPI, type BookingFormServiceConfig, } from './booking-form/booking-form.js';
12
- export { PaymentService, PaymentServiceDefinition, type PaymentServiceAPI, type PaymentServiceConfig, type PaymentDetails, type PricingServiceSelection, type LineItemAdditionalInfo, loadPaymentConfig, type SuccessPaymentConfigResult, type ErrorPaymentConfigResult, } from './payment/payment.js';
12
+ export { PaymentService, PaymentServiceDefinition, type PaymentServiceAPI, type PaymentServiceConfig, type PaymentDetails, type PricingServiceSelection, type LineItemAdditionalInfo, type ServicePaymentOptions, loadPaymentConfig, type SuccessPaymentConfigResult, type ErrorPaymentConfigResult, } from './payment/payment.js';
13
13
  export { LocationListService, LocationListServiceDefinition, LocationType, loadLocationListServiceInitialData, locationListServiceBinding, type LocationListServiceAPI, type LocationListServiceConfig, type LocationListActions, type Location, type LoadLocationListServiceResult, getLocationById, } from './location-list/location-list.js';
14
14
  export { StaffMemberListService, StaffMemberListServiceDefinition, loadStaffMemberListServiceInitialData, staffMemberListServiceBinding, type StaffMemberListServiceAPI, type StaffMemberListServiceConfig, type StaffMemberListActions, type StaffMemberData, type LoadStaffMemberListServiceResult, } from './staff-member-list/staff-member-list.js';
@@ -4,7 +4,7 @@
4
4
  * Supports two modes: SSR (config with pre-fetched data) and lazy loading (reactive)
5
5
  */
6
6
  import { type PaymentServiceConfig, type PaymentServiceInternalConfig, type PricingServiceSelection } from './payment.def.js';
7
- export { PaymentServiceDefinition, type PaymentServiceConfig, type PaymentDetails, type LineItemAdditionalInfo, type PricingServiceSelection, } from './payment.def.js';
7
+ export { PaymentServiceDefinition, type PaymentServiceConfig, type PaymentDetails, type LineItemAdditionalInfo, type PricingServiceSelection, type ServicePaymentOptions, } from './payment.def.js';
8
8
  export type { PaymentServiceAPI, PaymentServiceInternalConfig, } from './payment.def.js';
9
9
  export declare const PaymentService: import("@wix/services-definitions").ServiceFactory<string & {
10
10
  __api: import("./payment.def.js").PaymentServiceAPI;
@@ -5,6 +5,18 @@
5
5
  import type { Signal } from '@wix/services-definitions/core-services/signals';
6
6
  import type { CalculateTotalsResponse } from '@wix/auto_sdk_ecom_totals-calculator';
7
7
  import type { Service } from '@wix/auto_sdk_bookings_services';
8
+ /**
9
+ * Service payment options derived from service.payment.options.
10
+ * Describes which payment methods the service supports.
11
+ */
12
+ export interface ServicePaymentOptions {
13
+ /** Customer can pay online at booking time */
14
+ isOnline: boolean;
15
+ /** Customer can pay in person (on arrival) */
16
+ isInPerson: boolean;
17
+ /** A deposit is required at booking time */
18
+ isDeposit: boolean;
19
+ }
8
20
  /**
9
21
  * Payment option for line items
10
22
  */
@@ -44,6 +56,8 @@ export interface PricingServiceSelection {
44
56
  export interface PaymentServiceConfig {
45
57
  /** Pre-calculated payment details for SSR (no API call) */
46
58
  paymentDetails?: PaymentDetails;
59
+ /** Service payment options for SSR (derived from service.payment.options) */
60
+ serviceOptions?: ServicePaymentOptions;
47
61
  }
48
62
  /**
49
63
  * Internal config used by Core component to pass lazy loading data
@@ -59,6 +73,7 @@ export interface PaymentServiceAPI {
59
73
  paymentDetailsSignal: Signal<PaymentDetails | null>;
60
74
  isLoadingSignal: Signal<boolean>;
61
75
  errorSignal: Signal<string | null>;
76
+ serviceOptionsSignal: Signal<ServicePaymentOptions>;
62
77
  }
63
78
  export declare const PaymentServiceDefinition: string & {
64
79
  __api: PaymentServiceAPI;
@@ -15,6 +15,35 @@ import { BOOKING_APP_ID } from '../constants.js';
15
15
  // ============================================================================
16
16
  // Helper Functions
17
17
  // ============================================================================
18
+ const DEFAULT_SERVICE_OPTIONS = {
19
+ isOnline: false,
20
+ isInPerson: false,
21
+ isDeposit: false,
22
+ };
23
+ /**
24
+ * Derives static service payment options from the first service's configuration.
25
+ *
26
+ * Note on `isDeposit`: this reflects whether the service is *configured* with a
27
+ * deposit (i.e. `payment.fixed.deposit.value > 0`), consistent with how
28
+ * `buildLineItem` determines `hasDeposit`. Only `FIXED` rate type is checked;
29
+ * `VARIED` pricing is not currently supported for deposit detection here.
30
+ *
31
+ * This is distinct from the runtime `isDeposit` flag in `CorePayment.Data`, which
32
+ * is computed dynamically from the ecom API result (`payNow.amount < total.amount`).
33
+ * Use `Payment.ServiceOptions` to determine *which scenario to render* (static config),
34
+ * and `Payment.PayNow`/`Payment.PayLater` for the *actual split amounts* (runtime).
35
+ */
36
+ function deriveServiceOptions(pricingServiceSelections) {
37
+ const service = pricingServiceSelections[0]?.service;
38
+ const options = service?.payment?.options;
39
+ const depositValue = service?.payment?.fixed?.deposit?.value;
40
+ const hasDeposit = !!depositValue && Number(depositValue) > 0;
41
+ return {
42
+ isOnline: !!options?.online,
43
+ isInPerson: !!options?.inPerson,
44
+ isDeposit: hasDeposit,
45
+ };
46
+ }
18
47
  function getAdditionalInfo(service, lineItemId) {
19
48
  const payment = service.payment;
20
49
  const customPrice = payment?.rateType === RateType.CUSTOM && payment.custom?.description
@@ -92,6 +121,7 @@ export const PaymentService = implementService.withConfig()(PaymentServiceDefini
92
121
  const paymentDetailsSignal = signalsService.signal(config.paymentDetails || null);
93
122
  const isLoadingSignal = signalsService.signal(false);
94
123
  const errorSignal = signalsService.signal(null);
124
+ const serviceOptionsSignal = signalsService.signal(config.serviceOptions || DEFAULT_SERVICE_OPTIONS);
95
125
  // Internal calculate function
96
126
  const calculate = async (pricingServiceSelections, totalParticipants) => {
97
127
  if (!pricingServiceSelections.length) {
@@ -117,10 +147,18 @@ export const PaymentService = implementService.withConfig()(PaymentServiceDefini
117
147
  };
118
148
  // Mode 1: SSR - paymentDetails already set in signal initialization
119
149
  if (config.paymentDetails) {
120
- // No-op - data is pre-fetched
150
+ if (!config.serviceOptions &&
151
+ process.env['NODE_ENV'] !== 'production') {
152
+ console.warn('[PaymentService] SSR mode: `serviceOptions` was not provided in config. ' +
153
+ 'Payment.ServiceOptions will return all-false defaults. ' +
154
+ 'Use `loadPaymentConfig` to automatically derive serviceOptions, ' +
155
+ 'or pass `serviceOptions` explicitly in `paymentServiceConfig`.');
156
+ }
157
+ // serviceOptionsSignal already initialized from config.serviceOptions (or DEFAULT_SERVICE_OPTIONS)
121
158
  }
122
159
  // Mode 2: Lazy loading - pricingServiceSelections provided via internal config
123
160
  else if (config.pricingServiceSelections?.length) {
161
+ serviceOptionsSignal.set(deriveServiceOptions(config.pricingServiceSelections));
124
162
  void calculate(config.pricingServiceSelections, config.totalParticipants ?? 1);
125
163
  }
126
164
  // Mode 3: Reactive - observe BookingService signals (client-side only)
@@ -142,6 +180,9 @@ export const PaymentService = implementService.withConfig()(PaymentServiceDefini
142
180
  service: selection.service,
143
181
  lineItemId: selection.instanceId,
144
182
  }));
183
+ if (pricingServiceSelections.length) {
184
+ serviceOptionsSignal.set(deriveServiceOptions(pricingServiceSelections));
185
+ }
145
186
  void calculate(pricingServiceSelections, numParticipants);
146
187
  });
147
188
  }
@@ -149,6 +190,7 @@ export const PaymentService = implementService.withConfig()(PaymentServiceDefini
149
190
  paymentDetailsSignal,
150
191
  isLoadingSignal,
151
192
  errorSignal,
193
+ serviceOptionsSignal,
152
194
  };
153
195
  });
154
196
  /**
@@ -164,13 +206,19 @@ export async function loadPaymentConfig(params) {
164
206
  if (!pricingServiceSelections.length) {
165
207
  return {
166
208
  type: 'success',
167
- config: { paymentDetails: undefined },
209
+ config: {
210
+ paymentDetails: undefined,
211
+ serviceOptions: DEFAULT_SERVICE_OPTIONS,
212
+ },
168
213
  };
169
214
  }
170
215
  const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, totalParticipants);
171
216
  return {
172
217
  type: 'success',
173
- config: { paymentDetails },
218
+ config: {
219
+ paymentDetails,
220
+ serviceOptions: deriveServiceOptions(pricingServiceSelections),
221
+ },
174
222
  };
175
223
  }
176
224
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/headless-bookings",
3
- "version": "0.0.117",
3
+ "version": "0.0.118",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "scripts": {
@@ -73,5 +73,5 @@
73
73
  "groupId": "com.wixpress.headless-components"
74
74
  }
75
75
  },
76
- "falconPackageHash": "f3af7c27f3ef8bd8bdc06d522c39551763e5bf65fd9fe6891ba25425"
76
+ "falconPackageHash": "dac1d1f3424ebe62c7911b33c4224023bcfa07880c8ea404481cc7b0"
77
77
  }