@wix/headless-bookings 1.0.5 → 1.0.6
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.
- package/cjs/dist/__mocks__/booking/booking-client-service.js +4 -1
- package/cjs/dist/react/core/booking/Booking.d.ts +6 -1
- package/cjs/dist/react/core/booking/Booking.js +2 -0
- package/cjs/dist/services/booking/book-action/buildBookingRequest.js +4 -2
- package/cjs/dist/services/booking/book-action/types.d.ts +15 -0
- package/cjs/dist/services/booking/booking.d.ts +20 -1
- package/cjs/dist/services/booking/booking.js +8 -0
- package/cjs/dist/services/payment/payment.d.ts +4 -0
- package/cjs/dist/services/payment/payment.def.d.ts +6 -0
- package/cjs/dist/services/payment/payment.js +39 -17
- package/dist/__mocks__/booking/booking-client-service.js +4 -1
- package/dist/react/core/booking/Booking.d.ts +6 -1
- package/dist/react/core/booking/Booking.js +2 -0
- package/dist/services/booking/book-action/buildBookingRequest.js +4 -2
- package/dist/services/booking/book-action/types.d.ts +15 -0
- package/dist/services/booking/booking.d.ts +20 -1
- package/dist/services/booking/booking.js +8 -0
- package/dist/services/payment/payment.d.ts +4 -0
- package/dist/services/payment/payment.def.d.ts +6 -0
- package/dist/services/payment/payment.js +39 -17
- package/package.json +2 -2
|
@@ -29,16 +29,18 @@ function createMockSignalsService() {
|
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
function createMockBookingService(config = {}) {
|
|
32
|
-
const { serviceSelections = [], location = null, timezone = 'UTC', formSubmission = null, } = config;
|
|
32
|
+
const { serviceSelections = [], location = null, timezone = 'UTC', formSubmission = null, selectedPaymentOption, } = config;
|
|
33
33
|
const serviceSelectionsSignal = mockSignal(serviceSelections);
|
|
34
34
|
const locationSignal = mockSignal(location);
|
|
35
35
|
const timezoneSignal = mockSignal(timezone);
|
|
36
36
|
const formSubmissionSignal = mockSignal(formSubmission);
|
|
37
|
+
const selectedPaymentOptionSignal = mockSignal(selectedPaymentOption);
|
|
37
38
|
return {
|
|
38
39
|
serviceSelections: serviceSelectionsSignal,
|
|
39
40
|
location: locationSignal,
|
|
40
41
|
timezone: timezoneSignal,
|
|
41
42
|
formSubmission: formSubmissionSignal,
|
|
43
|
+
selectedPaymentOption: selectedPaymentOptionSignal,
|
|
42
44
|
actions: {
|
|
43
45
|
updateItem: vitest_1.vi.fn(),
|
|
44
46
|
setItem: vitest_1.vi.fn(),
|
|
@@ -53,6 +55,7 @@ function createMockBookingService(config = {}) {
|
|
|
53
55
|
setTimezone: vitest_1.vi.fn(),
|
|
54
56
|
setFormSubmission: vitest_1.vi.fn(),
|
|
55
57
|
clearFormSubmission: vitest_1.vi.fn(),
|
|
58
|
+
setSelectedPaymentOption: vitest_1.vi.fn(),
|
|
56
59
|
clearAll: vitest_1.vi.fn(),
|
|
57
60
|
book: vitest_1.vi.fn(),
|
|
58
61
|
},
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Provides low-level access to BookingService via render props and service setup
|
|
4
4
|
*/
|
|
5
5
|
import React from 'react';
|
|
6
|
-
import { type BookingServiceConfig, type ServiceSelection, type BookingServiceActions } from '../../../services/booking/booking.js';
|
|
6
|
+
import { type BookingServiceConfig, type ServiceSelection, type BookingServiceActions, type SelectedPaymentOption } from '../../../services/booking/booking.js';
|
|
7
7
|
import type { FormValues } from '@wix/forms/components';
|
|
8
8
|
import type { TimeSlot, Resource } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
9
9
|
import { type Service, type V2Location as ServiceLocationType } from '@wix/auto_sdk_bookings_services';
|
|
@@ -40,6 +40,11 @@ export interface BookingData {
|
|
|
40
40
|
location: ServiceLocationType | null;
|
|
41
41
|
timezone: string | undefined;
|
|
42
42
|
formSubmission: FormValues | null;
|
|
43
|
+
/**
|
|
44
|
+
* Consumer-selected payment option. `undefined` means the service-config
|
|
45
|
+
* heuristic applies. Set via `actions.setSelectedPaymentOption`.
|
|
46
|
+
*/
|
|
47
|
+
selectedPaymentOption: SelectedPaymentOption | undefined;
|
|
43
48
|
actions: BookingServiceActions;
|
|
44
49
|
}
|
|
45
50
|
/**
|
|
@@ -117,6 +117,7 @@ function Data(props) {
|
|
|
117
117
|
const bookingLocation = bookingService.location.get();
|
|
118
118
|
const timezone = bookingService.timezone.get();
|
|
119
119
|
const formSubmission = bookingService.formSubmission.get();
|
|
120
|
+
const selectedPaymentOption = bookingService.selectedPaymentOption.get();
|
|
120
121
|
const { actions } = bookingService;
|
|
121
122
|
const location = resolveLocation(bookingLocation, serviceSelections);
|
|
122
123
|
return children({
|
|
@@ -124,6 +125,7 @@ function Data(props) {
|
|
|
124
125
|
location,
|
|
125
126
|
timezone,
|
|
126
127
|
formSubmission,
|
|
128
|
+
selectedPaymentOption,
|
|
127
129
|
actions,
|
|
128
130
|
});
|
|
129
131
|
}
|
|
@@ -58,8 +58,10 @@ function buildBookingRequest(params) {
|
|
|
58
58
|
const slotLocation = item.timeSlot.location ?? location;
|
|
59
59
|
return {
|
|
60
60
|
booking: {
|
|
61
|
-
// Payment option
|
|
62
|
-
|
|
61
|
+
// Payment option: consumer override (if set) wins over the
|
|
62
|
+
// service-config heuristic.
|
|
63
|
+
selectedPaymentOption: params.selectedPaymentOption ??
|
|
64
|
+
deriveSelectedPaymentOption(item.service),
|
|
63
65
|
// Participants
|
|
64
66
|
totalParticipants: item.totalParticipants || 1,
|
|
65
67
|
// Booked entity slot
|
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
import type { ServiceSelection } from '../booking.js';
|
|
5
5
|
import type { FormValues } from '@wix/forms/components';
|
|
6
6
|
import type { Location as TimeSlotLocationType } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
7
|
+
import type { SelectedPaymentOptionWithLiterals } from '@wix/auto_sdk_bookings_bookings';
|
|
8
|
+
/**
|
|
9
|
+
* Narrowed booking-API `selectedPaymentOption` to the two values a consumer
|
|
10
|
+
* can pick in the UI. Derived from the SDK enum (`SelectedPaymentOption`) so
|
|
11
|
+
* the type stays in sync; MEMBERSHIP variants are intentionally excluded
|
|
12
|
+
* because they require pricing-plan plumbing not exposed here.
|
|
13
|
+
*/
|
|
14
|
+
export type SelectedPaymentOption = Extract<SelectedPaymentOptionWithLiterals, 'ONLINE' | 'OFFLINE'>;
|
|
7
15
|
/**
|
|
8
16
|
* Enum for book result types
|
|
9
17
|
*/
|
|
@@ -38,6 +46,13 @@ export interface BookActionParams {
|
|
|
38
46
|
timezone?: string;
|
|
39
47
|
/** Optional app ID override for the catalog reference. Defaults to the bookings app ID. */
|
|
40
48
|
appId?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Optional consumer-selected payment option. When provided, overrides the
|
|
51
|
+
* heuristic derived from `service.payment.options` in `buildBookingRequest`
|
|
52
|
+
* and matches the line item's `paymentOption` produced by the payment
|
|
53
|
+
* service. When omitted, the existing service-config heuristic applies.
|
|
54
|
+
*/
|
|
55
|
+
selectedPaymentOption?: SelectedPaymentOption;
|
|
41
56
|
}
|
|
42
57
|
/**
|
|
43
58
|
* Props passed to children of Book component
|
|
@@ -7,7 +7,8 @@ import { type Signal } from '@wix/services-definitions/core-services/signals';
|
|
|
7
7
|
import type { Service, V2Location as ServiceLocationType } from '@wix/auto_sdk_bookings_services';
|
|
8
8
|
import type { Resource, EventInfo, Location as TimeSlotLocationType } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
9
9
|
import type { FormValues } from '@wix/forms/components';
|
|
10
|
-
import type { BookResult } from './book-action/types.js';
|
|
10
|
+
import type { BookResult, SelectedPaymentOption } from './book-action/types.js';
|
|
11
|
+
export type { SelectedPaymentOption } from './book-action/types.js';
|
|
11
12
|
/**
|
|
12
13
|
* Extended Resource type with a type field for categorization
|
|
13
14
|
*/
|
|
@@ -80,6 +81,13 @@ export interface BookingServiceActions {
|
|
|
80
81
|
setTimezone(timezone: string): void;
|
|
81
82
|
setFormSubmission(values: FormValues | null): void;
|
|
82
83
|
clearFormSubmission(): void;
|
|
84
|
+
/**
|
|
85
|
+
* Set (or clear with `null`) the consumer-selected payment option. When set
|
|
86
|
+
* to `'ONLINE'` or `'OFFLINE'`, this overrides the service-config heuristic
|
|
87
|
+
* used by `book()` and the payment preview. When set to `null`, the override
|
|
88
|
+
* is cleared and the heuristic applies again.
|
|
89
|
+
*/
|
|
90
|
+
setSelectedPaymentOption(value: SelectedPaymentOption | null): void;
|
|
83
91
|
clearAll(): void;
|
|
84
92
|
book(): Promise<BookResult>;
|
|
85
93
|
}
|
|
@@ -96,6 +104,12 @@ export interface BookingService {
|
|
|
96
104
|
timezone: Signal<string | undefined>;
|
|
97
105
|
/** Signal containing the form submission */
|
|
98
106
|
formSubmission: Signal<FormValues | null>;
|
|
107
|
+
/**
|
|
108
|
+
* Signal containing the consumer-selected payment option. `undefined` means
|
|
109
|
+
* "not chosen" — `book()` and the payment preview fall back to the
|
|
110
|
+
* service-config heuristic. Set to `'ONLINE'` or `'OFFLINE'` to override.
|
|
111
|
+
*/
|
|
112
|
+
selectedPaymentOption: Signal<SelectedPaymentOption | undefined>;
|
|
99
113
|
/** Actions for modifying booking state */
|
|
100
114
|
actions: BookingServiceActions;
|
|
101
115
|
}
|
|
@@ -120,6 +134,11 @@ export interface BookingServiceConfig {
|
|
|
120
134
|
timezone?: string;
|
|
121
135
|
/** Optional initial form submission */
|
|
122
136
|
formSubmission?: FormValues | null;
|
|
137
|
+
/**
|
|
138
|
+
* Optional initial consumer-selected payment option. Omit to use the
|
|
139
|
+
* service-config heuristic; set to `'ONLINE'` or `'OFFLINE'` to override.
|
|
140
|
+
*/
|
|
141
|
+
selectedPaymentOption?: SelectedPaymentOption;
|
|
123
142
|
/** Optional app ID override for the catalog reference. Defaults to the bookings app ID. */
|
|
124
143
|
appId?: string;
|
|
125
144
|
}
|
|
@@ -73,6 +73,7 @@ exports.BookingService = services_definitions_1.implementService.withConfig()(ex
|
|
|
73
73
|
const locationSignal = signalsService.signal(config.location || null);
|
|
74
74
|
const timezoneSignal = signalsService.signal(config.timezone ?? getClientTimezone());
|
|
75
75
|
const formSubmissionSignal = signalsService.signal(config.formSubmission || null);
|
|
76
|
+
const selectedPaymentOptionSignal = signalsService.signal(config.selectedPaymentOption);
|
|
76
77
|
// ========================================================================
|
|
77
78
|
// Helpers
|
|
78
79
|
// ========================================================================
|
|
@@ -167,6 +168,10 @@ exports.BookingService = services_definitions_1.implementService.withConfig()(ex
|
|
|
167
168
|
clearFormSubmission() {
|
|
168
169
|
formSubmissionSignal.set(null);
|
|
169
170
|
},
|
|
171
|
+
// Payment option action
|
|
172
|
+
setSelectedPaymentOption(value) {
|
|
173
|
+
selectedPaymentOptionSignal.set(value ?? undefined);
|
|
174
|
+
},
|
|
170
175
|
// Clear all
|
|
171
176
|
clearAll() {
|
|
172
177
|
this.clearItems();
|
|
@@ -180,6 +185,7 @@ exports.BookingService = services_definitions_1.implementService.withConfig()(ex
|
|
|
180
185
|
const formSubmission = formSubmissionSignal.get();
|
|
181
186
|
const location = locationSignal.get();
|
|
182
187
|
const timezone = timezoneSignal.get();
|
|
188
|
+
const selectedPaymentOption = selectedPaymentOptionSignal.get();
|
|
183
189
|
// Validation happens inside executeBookAction
|
|
184
190
|
return (0, bookAction_js_1.executeBookAction)({
|
|
185
191
|
serviceSelections,
|
|
@@ -187,6 +193,7 @@ exports.BookingService = services_definitions_1.implementService.withConfig()(ex
|
|
|
187
193
|
location,
|
|
188
194
|
timezone,
|
|
189
195
|
appId: config.appId,
|
|
196
|
+
selectedPaymentOption,
|
|
190
197
|
});
|
|
191
198
|
},
|
|
192
199
|
};
|
|
@@ -195,6 +202,7 @@ exports.BookingService = services_definitions_1.implementService.withConfig()(ex
|
|
|
195
202
|
location: locationSignal,
|
|
196
203
|
timezone: timezoneSignal,
|
|
197
204
|
formSubmission: formSubmissionSignal,
|
|
205
|
+
selectedPaymentOption: selectedPaymentOptionSignal,
|
|
198
206
|
actions,
|
|
199
207
|
};
|
|
200
208
|
});
|
|
@@ -4,6 +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
|
+
import type { SelectedPaymentOption } from '../booking/book-action/types.js';
|
|
7
8
|
export { PaymentServiceDefinition, type PaymentServiceConfig, type PaymentDetails, type LineItemAdditionalInfo, type PricingServiceSelection, type ServicePaymentOptions, } from './payment.def.js';
|
|
8
9
|
export type { PaymentServiceAPI, PaymentServiceInternalConfig, } from './payment.def.js';
|
|
9
10
|
export declare const PaymentService: import("@wix/services-definitions").ServiceFactory<string & {
|
|
@@ -25,8 +26,11 @@ export type ErrorPaymentConfigResult = {
|
|
|
25
26
|
*
|
|
26
27
|
* @param params.pricingServiceSelections - Pricing service selections for calculation
|
|
27
28
|
* @param params.totalParticipants - Number of participants (default: 1)
|
|
29
|
+
* @param params.selectedPaymentOption - Optional consumer-selected payment option;
|
|
30
|
+
* overrides the service-config heuristic on each line item's `paymentOption`.
|
|
28
31
|
*/
|
|
29
32
|
export declare function loadPaymentConfig(params: {
|
|
30
33
|
pricingServiceSelections: PricingServiceSelection[];
|
|
31
34
|
totalParticipants?: number;
|
|
35
|
+
selectedPaymentOption?: SelectedPaymentOption;
|
|
32
36
|
}): Promise<SuccessPaymentConfigResult | ErrorPaymentConfigResult>;
|
|
@@ -5,6 +5,7 @@
|
|
|
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
|
+
import type { SelectedPaymentOption } from '../booking/book-action/types.js';
|
|
8
9
|
/**
|
|
9
10
|
* Service payment options derived from service.payment.options.
|
|
10
11
|
* Describes which payment methods the service supports.
|
|
@@ -74,6 +75,11 @@ export interface PaymentServiceInternalConfig extends PaymentServiceConfig {
|
|
|
74
75
|
pricingServiceSelections?: PricingServiceSelection[];
|
|
75
76
|
/** Number of participants for lazy loading (internal use) */
|
|
76
77
|
totalParticipants?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Optional consumer-selected payment option used for lazy loading. When
|
|
80
|
+
* set, overrides the service-config heuristic in line item `paymentOption`.
|
|
81
|
+
*/
|
|
82
|
+
selectedPaymentOption?: SelectedPaymentOption;
|
|
77
83
|
}
|
|
78
84
|
export interface PaymentServiceAPI {
|
|
79
85
|
paymentDetailsSignal: Signal<PaymentDetails | null>;
|
|
@@ -57,7 +57,21 @@ function getAdditionalInfo(service, lineItemId) {
|
|
|
57
57
|
: undefined;
|
|
58
58
|
return { lineItemId, customPrice };
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Resolves the line item's `paymentOption`.
|
|
62
|
+
*
|
|
63
|
+
* Precedence: explicit consumer choice (`selectedPaymentOption`) wins over the
|
|
64
|
+
* service-config heuristic so the preview always agrees with what `book()`
|
|
65
|
+
* will send. When unset, the existing heuristic is preserved for backward
|
|
66
|
+
* compat.
|
|
67
|
+
*/
|
|
68
|
+
function getPaymentOption(service, hasDeposit, selectedPaymentOption) {
|
|
69
|
+
if (selectedPaymentOption === 'OFFLINE') {
|
|
70
|
+
return 'FULL_PAYMENT_OFFLINE';
|
|
71
|
+
}
|
|
72
|
+
if (selectedPaymentOption === 'ONLINE') {
|
|
73
|
+
return hasDeposit ? 'DEPOSIT_ONLINE' : 'FULL_PAYMENT_ONLINE';
|
|
74
|
+
}
|
|
61
75
|
const options = service.payment?.options;
|
|
62
76
|
if (options?.inPerson) {
|
|
63
77
|
return 'FULL_PAYMENT_OFFLINE';
|
|
@@ -68,7 +82,7 @@ function getPaymentOption(service, hasDeposit) {
|
|
|
68
82
|
}
|
|
69
83
|
return 'FULL_PAYMENT_ONLINE';
|
|
70
84
|
}
|
|
71
|
-
function buildLineItem(service, totalParticipants, lineItemId) {
|
|
85
|
+
function buildLineItem(service, totalParticipants, lineItemId, selectedPaymentOption) {
|
|
72
86
|
// Get fixed price from service
|
|
73
87
|
const servicePrice = service.payment?.fixed?.price?.value;
|
|
74
88
|
const price = Number(servicePrice || 0) * totalParticipants;
|
|
@@ -79,7 +93,7 @@ function buildLineItem(service, totalParticipants, lineItemId) {
|
|
|
79
93
|
? Number(depositValue) * totalParticipants
|
|
80
94
|
: undefined;
|
|
81
95
|
// Get payment option from service configuration (pass hasDeposit for correct API behavior)
|
|
82
|
-
const paymentOption = getPaymentOption(service, hasDeposit);
|
|
96
|
+
const paymentOption = getPaymentOption(service, hasDeposit, selectedPaymentOption);
|
|
83
97
|
return {
|
|
84
98
|
_id: lineItemId,
|
|
85
99
|
catalogReference: {
|
|
@@ -117,7 +131,7 @@ async function getPreviewedPrice(service, localStartDate, localEndDate, timeZone
|
|
|
117
131
|
deposit: result.priceInfo?.deposit ?? null,
|
|
118
132
|
};
|
|
119
133
|
}
|
|
120
|
-
async function buildLineItemWithPreview(item, totalParticipants) {
|
|
134
|
+
async function buildLineItemWithPreview(item, totalParticipants, selectedPaymentOption) {
|
|
121
135
|
if (item.localStartDate &&
|
|
122
136
|
item.localEndDate &&
|
|
123
137
|
getDurationRange(item.service)) {
|
|
@@ -142,18 +156,18 @@ async function buildLineItemWithPreview(item, totalParticipants) {
|
|
|
142
156
|
price: String(previewedPrice.price),
|
|
143
157
|
...(deposit ? { depositAmount: String(deposit) } : {}),
|
|
144
158
|
quantity: 1,
|
|
145
|
-
paymentOption: getPaymentOption(item.service, hasDeposit),
|
|
159
|
+
paymentOption: getPaymentOption(item.service, hasDeposit, selectedPaymentOption),
|
|
146
160
|
};
|
|
147
161
|
}
|
|
148
162
|
}
|
|
149
|
-
return buildLineItem(item.service, totalParticipants, item.lineItemId);
|
|
163
|
+
return buildLineItem(item.service, totalParticipants, item.lineItemId, selectedPaymentOption);
|
|
150
164
|
}
|
|
151
|
-
async function calculatePaymentDetails(pricingServiceSelections, totalParticipants) {
|
|
165
|
+
async function calculatePaymentDetails(pricingServiceSelections, totalParticipants, selectedPaymentOption) {
|
|
152
166
|
const data = pricingServiceSelections.map((selection) => ({
|
|
153
167
|
...selection,
|
|
154
168
|
lineItemId: selection.lineItemId || (0, guid_js_1.generateLineItemId)(),
|
|
155
169
|
}));
|
|
156
|
-
const lineItems = await Promise.all(data.map((item) => buildLineItemWithPreview(item, totalParticipants)));
|
|
170
|
+
const lineItems = await Promise.all(data.map((item) => buildLineItemWithPreview(item, totalParticipants, selectedPaymentOption)));
|
|
157
171
|
const response = await (0, auto_sdk_ecom_totals_calculator_1.calculateTotals)({
|
|
158
172
|
lineItems,
|
|
159
173
|
});
|
|
@@ -184,7 +198,7 @@ exports.PaymentService = services_definitions_1.implementService.withConfig()(pa
|
|
|
184
198
|
const errorSignal = signalsService.signal(null);
|
|
185
199
|
const serviceOptionsSignal = signalsService.signal(config.serviceOptions || DEFAULT_SERVICE_OPTIONS);
|
|
186
200
|
// Internal calculate function
|
|
187
|
-
const calculate = async (pricingServiceSelections, totalParticipants) => {
|
|
201
|
+
const calculate = async (pricingServiceSelections, totalParticipants, selectedPaymentOption) => {
|
|
188
202
|
if (!pricingServiceSelections.length) {
|
|
189
203
|
paymentDetailsSignal.set(null);
|
|
190
204
|
return;
|
|
@@ -192,7 +206,7 @@ exports.PaymentService = services_definitions_1.implementService.withConfig()(pa
|
|
|
192
206
|
isLoadingSignal.set(true);
|
|
193
207
|
errorSignal.set(null);
|
|
194
208
|
try {
|
|
195
|
-
const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, totalParticipants);
|
|
209
|
+
const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, totalParticipants, selectedPaymentOption);
|
|
196
210
|
paymentDetailsSignal.set(paymentDetails);
|
|
197
211
|
}
|
|
198
212
|
catch (error) {
|
|
@@ -220,24 +234,30 @@ exports.PaymentService = services_definitions_1.implementService.withConfig()(pa
|
|
|
220
234
|
// Mode 2: Lazy loading - pricingServiceSelections provided via internal config
|
|
221
235
|
else if (config.pricingServiceSelections?.length) {
|
|
222
236
|
serviceOptionsSignal.set(deriveServiceOptions(config.pricingServiceSelections));
|
|
223
|
-
void calculate(config.pricingServiceSelections, config.totalParticipants ?? 1);
|
|
237
|
+
void calculate(config.pricingServiceSelections, config.totalParticipants ?? 1, config.selectedPaymentOption);
|
|
224
238
|
}
|
|
225
239
|
// Mode 3: Reactive - observe BookingService signals (client-side only)
|
|
226
240
|
else if (typeof window !== 'undefined' && bookingService) {
|
|
227
|
-
// Computed signal that changes when services
|
|
241
|
+
// Computed signal that changes when services, selected time slots, or
|
|
242
|
+
// the consumer-selected payment option change (not participants).
|
|
228
243
|
const servicesKey = signalsService.computed(() => {
|
|
229
244
|
const selections = bookingService?.serviceSelections.get() || [];
|
|
230
245
|
const timezone = bookingService?.timezone.get();
|
|
246
|
+
const selectedPaymentOption = bookingService?.selectedPaymentOption.get();
|
|
231
247
|
return (selections
|
|
232
248
|
.map((s) => `${s.service._id}:${s.timeSlot?.startDate ?? ''}:${s.timeSlot?.endDate ?? ''}`)
|
|
233
|
-
.join(',') +
|
|
249
|
+
.join(',') +
|
|
250
|
+
`:${timezone ?? ''}` +
|
|
251
|
+
`:${selectedPaymentOption ?? ''}`);
|
|
234
252
|
});
|
|
235
253
|
signalsService.effect(() => {
|
|
236
|
-
// Subscribe to servicesKey to trigger effect when service
|
|
254
|
+
// Subscribe to servicesKey to trigger effect when service, time
|
|
255
|
+
// slot, or selected payment option changes.
|
|
237
256
|
servicesKey.get();
|
|
238
257
|
// Read current selections without subscribing (peek)
|
|
239
258
|
const selections = bookingService?.serviceSelections.peek() || [];
|
|
240
259
|
const timezone = bookingService?.timezone.peek();
|
|
260
|
+
const selectedPaymentOption = bookingService?.selectedPaymentOption.peek();
|
|
241
261
|
// For single service mode, take the first selection
|
|
242
262
|
const firstSelection = selections[0];
|
|
243
263
|
const numParticipants = firstSelection?.totalParticipants || 1;
|
|
@@ -251,7 +271,7 @@ exports.PaymentService = services_definitions_1.implementService.withConfig()(pa
|
|
|
251
271
|
if (pricingServiceSelections.length) {
|
|
252
272
|
serviceOptionsSignal.set(deriveServiceOptions(pricingServiceSelections));
|
|
253
273
|
}
|
|
254
|
-
void calculate(pricingServiceSelections, numParticipants);
|
|
274
|
+
void calculate(pricingServiceSelections, numParticipants, selectedPaymentOption);
|
|
255
275
|
});
|
|
256
276
|
}
|
|
257
277
|
return {
|
|
@@ -267,10 +287,12 @@ exports.PaymentService = services_definitions_1.implementService.withConfig()(pa
|
|
|
267
287
|
*
|
|
268
288
|
* @param params.pricingServiceSelections - Pricing service selections for calculation
|
|
269
289
|
* @param params.totalParticipants - Number of participants (default: 1)
|
|
290
|
+
* @param params.selectedPaymentOption - Optional consumer-selected payment option;
|
|
291
|
+
* overrides the service-config heuristic on each line item's `paymentOption`.
|
|
270
292
|
*/
|
|
271
293
|
async function loadPaymentConfig(params) {
|
|
272
294
|
try {
|
|
273
|
-
const { pricingServiceSelections, totalParticipants = 1 } = params;
|
|
295
|
+
const { pricingServiceSelections, totalParticipants = 1, selectedPaymentOption, } = params;
|
|
274
296
|
if (!pricingServiceSelections.length) {
|
|
275
297
|
return {
|
|
276
298
|
type: 'success',
|
|
@@ -280,7 +302,7 @@ async function loadPaymentConfig(params) {
|
|
|
280
302
|
},
|
|
281
303
|
};
|
|
282
304
|
}
|
|
283
|
-
const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, totalParticipants);
|
|
305
|
+
const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, totalParticipants, selectedPaymentOption);
|
|
284
306
|
return {
|
|
285
307
|
type: 'success',
|
|
286
308
|
config: {
|
|
@@ -24,16 +24,18 @@ export function createMockSignalsService() {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
export function createMockBookingService(config = {}) {
|
|
27
|
-
const { serviceSelections = [], location = null, timezone = 'UTC', formSubmission = null, } = config;
|
|
27
|
+
const { serviceSelections = [], location = null, timezone = 'UTC', formSubmission = null, selectedPaymentOption, } = config;
|
|
28
28
|
const serviceSelectionsSignal = mockSignal(serviceSelections);
|
|
29
29
|
const locationSignal = mockSignal(location);
|
|
30
30
|
const timezoneSignal = mockSignal(timezone);
|
|
31
31
|
const formSubmissionSignal = mockSignal(formSubmission);
|
|
32
|
+
const selectedPaymentOptionSignal = mockSignal(selectedPaymentOption);
|
|
32
33
|
return {
|
|
33
34
|
serviceSelections: serviceSelectionsSignal,
|
|
34
35
|
location: locationSignal,
|
|
35
36
|
timezone: timezoneSignal,
|
|
36
37
|
formSubmission: formSubmissionSignal,
|
|
38
|
+
selectedPaymentOption: selectedPaymentOptionSignal,
|
|
37
39
|
actions: {
|
|
38
40
|
updateItem: vi.fn(),
|
|
39
41
|
setItem: vi.fn(),
|
|
@@ -48,6 +50,7 @@ export function createMockBookingService(config = {}) {
|
|
|
48
50
|
setTimezone: vi.fn(),
|
|
49
51
|
setFormSubmission: vi.fn(),
|
|
50
52
|
clearFormSubmission: vi.fn(),
|
|
53
|
+
setSelectedPaymentOption: vi.fn(),
|
|
51
54
|
clearAll: vi.fn(),
|
|
52
55
|
book: vi.fn(),
|
|
53
56
|
},
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Provides low-level access to BookingService via render props and service setup
|
|
4
4
|
*/
|
|
5
5
|
import React from 'react';
|
|
6
|
-
import { type BookingServiceConfig, type ServiceSelection, type BookingServiceActions } from '../../../services/booking/booking.js';
|
|
6
|
+
import { type BookingServiceConfig, type ServiceSelection, type BookingServiceActions, type SelectedPaymentOption } from '../../../services/booking/booking.js';
|
|
7
7
|
import type { FormValues } from '@wix/forms/components';
|
|
8
8
|
import type { TimeSlot, Resource } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
9
9
|
import { type Service, type V2Location as ServiceLocationType } from '@wix/auto_sdk_bookings_services';
|
|
@@ -40,6 +40,11 @@ export interface BookingData {
|
|
|
40
40
|
location: ServiceLocationType | null;
|
|
41
41
|
timezone: string | undefined;
|
|
42
42
|
formSubmission: FormValues | null;
|
|
43
|
+
/**
|
|
44
|
+
* Consumer-selected payment option. `undefined` means the service-config
|
|
45
|
+
* heuristic applies. Set via `actions.setSelectedPaymentOption`.
|
|
46
|
+
*/
|
|
47
|
+
selectedPaymentOption: SelectedPaymentOption | undefined;
|
|
43
48
|
actions: BookingServiceActions;
|
|
44
49
|
}
|
|
45
50
|
/**
|
|
@@ -105,6 +105,7 @@ export function Data(props) {
|
|
|
105
105
|
const bookingLocation = bookingService.location.get();
|
|
106
106
|
const timezone = bookingService.timezone.get();
|
|
107
107
|
const formSubmission = bookingService.formSubmission.get();
|
|
108
|
+
const selectedPaymentOption = bookingService.selectedPaymentOption.get();
|
|
108
109
|
const { actions } = bookingService;
|
|
109
110
|
const location = resolveLocation(bookingLocation, serviceSelections);
|
|
110
111
|
return children({
|
|
@@ -112,6 +113,7 @@ export function Data(props) {
|
|
|
112
113
|
location,
|
|
113
114
|
timezone,
|
|
114
115
|
formSubmission,
|
|
116
|
+
selectedPaymentOption,
|
|
115
117
|
actions,
|
|
116
118
|
});
|
|
117
119
|
}
|
|
@@ -55,8 +55,10 @@ export function buildBookingRequest(params) {
|
|
|
55
55
|
const slotLocation = item.timeSlot.location ?? location;
|
|
56
56
|
return {
|
|
57
57
|
booking: {
|
|
58
|
-
// Payment option
|
|
59
|
-
|
|
58
|
+
// Payment option: consumer override (if set) wins over the
|
|
59
|
+
// service-config heuristic.
|
|
60
|
+
selectedPaymentOption: params.selectedPaymentOption ??
|
|
61
|
+
deriveSelectedPaymentOption(item.service),
|
|
60
62
|
// Participants
|
|
61
63
|
totalParticipants: item.totalParticipants || 1,
|
|
62
64
|
// Booked entity slot
|
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
import type { ServiceSelection } from '../booking.js';
|
|
5
5
|
import type { FormValues } from '@wix/forms/components';
|
|
6
6
|
import type { Location as TimeSlotLocationType } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
7
|
+
import type { SelectedPaymentOptionWithLiterals } from '@wix/auto_sdk_bookings_bookings';
|
|
8
|
+
/**
|
|
9
|
+
* Narrowed booking-API `selectedPaymentOption` to the two values a consumer
|
|
10
|
+
* can pick in the UI. Derived from the SDK enum (`SelectedPaymentOption`) so
|
|
11
|
+
* the type stays in sync; MEMBERSHIP variants are intentionally excluded
|
|
12
|
+
* because they require pricing-plan plumbing not exposed here.
|
|
13
|
+
*/
|
|
14
|
+
export type SelectedPaymentOption = Extract<SelectedPaymentOptionWithLiterals, 'ONLINE' | 'OFFLINE'>;
|
|
7
15
|
/**
|
|
8
16
|
* Enum for book result types
|
|
9
17
|
*/
|
|
@@ -38,6 +46,13 @@ export interface BookActionParams {
|
|
|
38
46
|
timezone?: string;
|
|
39
47
|
/** Optional app ID override for the catalog reference. Defaults to the bookings app ID. */
|
|
40
48
|
appId?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Optional consumer-selected payment option. When provided, overrides the
|
|
51
|
+
* heuristic derived from `service.payment.options` in `buildBookingRequest`
|
|
52
|
+
* and matches the line item's `paymentOption` produced by the payment
|
|
53
|
+
* service. When omitted, the existing service-config heuristic applies.
|
|
54
|
+
*/
|
|
55
|
+
selectedPaymentOption?: SelectedPaymentOption;
|
|
41
56
|
}
|
|
42
57
|
/**
|
|
43
58
|
* Props passed to children of Book component
|
|
@@ -7,7 +7,8 @@ import { type Signal } from '@wix/services-definitions/core-services/signals';
|
|
|
7
7
|
import type { Service, V2Location as ServiceLocationType } from '@wix/auto_sdk_bookings_services';
|
|
8
8
|
import type { Resource, EventInfo, Location as TimeSlotLocationType } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
9
9
|
import type { FormValues } from '@wix/forms/components';
|
|
10
|
-
import type { BookResult } from './book-action/types.js';
|
|
10
|
+
import type { BookResult, SelectedPaymentOption } from './book-action/types.js';
|
|
11
|
+
export type { SelectedPaymentOption } from './book-action/types.js';
|
|
11
12
|
/**
|
|
12
13
|
* Extended Resource type with a type field for categorization
|
|
13
14
|
*/
|
|
@@ -80,6 +81,13 @@ export interface BookingServiceActions {
|
|
|
80
81
|
setTimezone(timezone: string): void;
|
|
81
82
|
setFormSubmission(values: FormValues | null): void;
|
|
82
83
|
clearFormSubmission(): void;
|
|
84
|
+
/**
|
|
85
|
+
* Set (or clear with `null`) the consumer-selected payment option. When set
|
|
86
|
+
* to `'ONLINE'` or `'OFFLINE'`, this overrides the service-config heuristic
|
|
87
|
+
* used by `book()` and the payment preview. When set to `null`, the override
|
|
88
|
+
* is cleared and the heuristic applies again.
|
|
89
|
+
*/
|
|
90
|
+
setSelectedPaymentOption(value: SelectedPaymentOption | null): void;
|
|
83
91
|
clearAll(): void;
|
|
84
92
|
book(): Promise<BookResult>;
|
|
85
93
|
}
|
|
@@ -96,6 +104,12 @@ export interface BookingService {
|
|
|
96
104
|
timezone: Signal<string | undefined>;
|
|
97
105
|
/** Signal containing the form submission */
|
|
98
106
|
formSubmission: Signal<FormValues | null>;
|
|
107
|
+
/**
|
|
108
|
+
* Signal containing the consumer-selected payment option. `undefined` means
|
|
109
|
+
* "not chosen" — `book()` and the payment preview fall back to the
|
|
110
|
+
* service-config heuristic. Set to `'ONLINE'` or `'OFFLINE'` to override.
|
|
111
|
+
*/
|
|
112
|
+
selectedPaymentOption: Signal<SelectedPaymentOption | undefined>;
|
|
99
113
|
/** Actions for modifying booking state */
|
|
100
114
|
actions: BookingServiceActions;
|
|
101
115
|
}
|
|
@@ -120,6 +134,11 @@ export interface BookingServiceConfig {
|
|
|
120
134
|
timezone?: string;
|
|
121
135
|
/** Optional initial form submission */
|
|
122
136
|
formSubmission?: FormValues | null;
|
|
137
|
+
/**
|
|
138
|
+
* Optional initial consumer-selected payment option. Omit to use the
|
|
139
|
+
* service-config heuristic; set to `'ONLINE'` or `'OFFLINE'` to override.
|
|
140
|
+
*/
|
|
141
|
+
selectedPaymentOption?: SelectedPaymentOption;
|
|
123
142
|
/** Optional app ID override for the catalog reference. Defaults to the bookings app ID. */
|
|
124
143
|
appId?: string;
|
|
125
144
|
}
|
|
@@ -70,6 +70,7 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
70
70
|
const locationSignal = signalsService.signal(config.location || null);
|
|
71
71
|
const timezoneSignal = signalsService.signal(config.timezone ?? getClientTimezone());
|
|
72
72
|
const formSubmissionSignal = signalsService.signal(config.formSubmission || null);
|
|
73
|
+
const selectedPaymentOptionSignal = signalsService.signal(config.selectedPaymentOption);
|
|
73
74
|
// ========================================================================
|
|
74
75
|
// Helpers
|
|
75
76
|
// ========================================================================
|
|
@@ -164,6 +165,10 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
164
165
|
clearFormSubmission() {
|
|
165
166
|
formSubmissionSignal.set(null);
|
|
166
167
|
},
|
|
168
|
+
// Payment option action
|
|
169
|
+
setSelectedPaymentOption(value) {
|
|
170
|
+
selectedPaymentOptionSignal.set(value ?? undefined);
|
|
171
|
+
},
|
|
167
172
|
// Clear all
|
|
168
173
|
clearAll() {
|
|
169
174
|
this.clearItems();
|
|
@@ -177,6 +182,7 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
177
182
|
const formSubmission = formSubmissionSignal.get();
|
|
178
183
|
const location = locationSignal.get();
|
|
179
184
|
const timezone = timezoneSignal.get();
|
|
185
|
+
const selectedPaymentOption = selectedPaymentOptionSignal.get();
|
|
180
186
|
// Validation happens inside executeBookAction
|
|
181
187
|
return executeBookAction({
|
|
182
188
|
serviceSelections,
|
|
@@ -184,6 +190,7 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
184
190
|
location,
|
|
185
191
|
timezone,
|
|
186
192
|
appId: config.appId,
|
|
193
|
+
selectedPaymentOption,
|
|
187
194
|
});
|
|
188
195
|
},
|
|
189
196
|
};
|
|
@@ -192,6 +199,7 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
192
199
|
location: locationSignal,
|
|
193
200
|
timezone: timezoneSignal,
|
|
194
201
|
formSubmission: formSubmissionSignal,
|
|
202
|
+
selectedPaymentOption: selectedPaymentOptionSignal,
|
|
195
203
|
actions,
|
|
196
204
|
};
|
|
197
205
|
});
|
|
@@ -4,6 +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
|
+
import type { SelectedPaymentOption } from '../booking/book-action/types.js';
|
|
7
8
|
export { PaymentServiceDefinition, type PaymentServiceConfig, type PaymentDetails, type LineItemAdditionalInfo, type PricingServiceSelection, type ServicePaymentOptions, } from './payment.def.js';
|
|
8
9
|
export type { PaymentServiceAPI, PaymentServiceInternalConfig, } from './payment.def.js';
|
|
9
10
|
export declare const PaymentService: import("@wix/services-definitions").ServiceFactory<string & {
|
|
@@ -25,8 +26,11 @@ export type ErrorPaymentConfigResult = {
|
|
|
25
26
|
*
|
|
26
27
|
* @param params.pricingServiceSelections - Pricing service selections for calculation
|
|
27
28
|
* @param params.totalParticipants - Number of participants (default: 1)
|
|
29
|
+
* @param params.selectedPaymentOption - Optional consumer-selected payment option;
|
|
30
|
+
* overrides the service-config heuristic on each line item's `paymentOption`.
|
|
28
31
|
*/
|
|
29
32
|
export declare function loadPaymentConfig(params: {
|
|
30
33
|
pricingServiceSelections: PricingServiceSelection[];
|
|
31
34
|
totalParticipants?: number;
|
|
35
|
+
selectedPaymentOption?: SelectedPaymentOption;
|
|
32
36
|
}): Promise<SuccessPaymentConfigResult | ErrorPaymentConfigResult>;
|
|
@@ -5,6 +5,7 @@
|
|
|
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
|
+
import type { SelectedPaymentOption } from '../booking/book-action/types.js';
|
|
8
9
|
/**
|
|
9
10
|
* Service payment options derived from service.payment.options.
|
|
10
11
|
* Describes which payment methods the service supports.
|
|
@@ -74,6 +75,11 @@ export interface PaymentServiceInternalConfig extends PaymentServiceConfig {
|
|
|
74
75
|
pricingServiceSelections?: PricingServiceSelection[];
|
|
75
76
|
/** Number of participants for lazy loading (internal use) */
|
|
76
77
|
totalParticipants?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Optional consumer-selected payment option used for lazy loading. When
|
|
80
|
+
* set, overrides the service-config heuristic in line item `paymentOption`.
|
|
81
|
+
*/
|
|
82
|
+
selectedPaymentOption?: SelectedPaymentOption;
|
|
77
83
|
}
|
|
78
84
|
export interface PaymentServiceAPI {
|
|
79
85
|
paymentDetailsSignal: Signal<PaymentDetails | null>;
|
|
@@ -52,7 +52,21 @@ function getAdditionalInfo(service, lineItemId) {
|
|
|
52
52
|
: undefined;
|
|
53
53
|
return { lineItemId, customPrice };
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Resolves the line item's `paymentOption`.
|
|
57
|
+
*
|
|
58
|
+
* Precedence: explicit consumer choice (`selectedPaymentOption`) wins over the
|
|
59
|
+
* service-config heuristic so the preview always agrees with what `book()`
|
|
60
|
+
* will send. When unset, the existing heuristic is preserved for backward
|
|
61
|
+
* compat.
|
|
62
|
+
*/
|
|
63
|
+
function getPaymentOption(service, hasDeposit, selectedPaymentOption) {
|
|
64
|
+
if (selectedPaymentOption === 'OFFLINE') {
|
|
65
|
+
return 'FULL_PAYMENT_OFFLINE';
|
|
66
|
+
}
|
|
67
|
+
if (selectedPaymentOption === 'ONLINE') {
|
|
68
|
+
return hasDeposit ? 'DEPOSIT_ONLINE' : 'FULL_PAYMENT_ONLINE';
|
|
69
|
+
}
|
|
56
70
|
const options = service.payment?.options;
|
|
57
71
|
if (options?.inPerson) {
|
|
58
72
|
return 'FULL_PAYMENT_OFFLINE';
|
|
@@ -63,7 +77,7 @@ function getPaymentOption(service, hasDeposit) {
|
|
|
63
77
|
}
|
|
64
78
|
return 'FULL_PAYMENT_ONLINE';
|
|
65
79
|
}
|
|
66
|
-
function buildLineItem(service, totalParticipants, lineItemId) {
|
|
80
|
+
function buildLineItem(service, totalParticipants, lineItemId, selectedPaymentOption) {
|
|
67
81
|
// Get fixed price from service
|
|
68
82
|
const servicePrice = service.payment?.fixed?.price?.value;
|
|
69
83
|
const price = Number(servicePrice || 0) * totalParticipants;
|
|
@@ -74,7 +88,7 @@ function buildLineItem(service, totalParticipants, lineItemId) {
|
|
|
74
88
|
? Number(depositValue) * totalParticipants
|
|
75
89
|
: undefined;
|
|
76
90
|
// Get payment option from service configuration (pass hasDeposit for correct API behavior)
|
|
77
|
-
const paymentOption = getPaymentOption(service, hasDeposit);
|
|
91
|
+
const paymentOption = getPaymentOption(service, hasDeposit, selectedPaymentOption);
|
|
78
92
|
return {
|
|
79
93
|
_id: lineItemId,
|
|
80
94
|
catalogReference: {
|
|
@@ -112,7 +126,7 @@ async function getPreviewedPrice(service, localStartDate, localEndDate, timeZone
|
|
|
112
126
|
deposit: result.priceInfo?.deposit ?? null,
|
|
113
127
|
};
|
|
114
128
|
}
|
|
115
|
-
async function buildLineItemWithPreview(item, totalParticipants) {
|
|
129
|
+
async function buildLineItemWithPreview(item, totalParticipants, selectedPaymentOption) {
|
|
116
130
|
if (item.localStartDate &&
|
|
117
131
|
item.localEndDate &&
|
|
118
132
|
getDurationRange(item.service)) {
|
|
@@ -137,18 +151,18 @@ async function buildLineItemWithPreview(item, totalParticipants) {
|
|
|
137
151
|
price: String(previewedPrice.price),
|
|
138
152
|
...(deposit ? { depositAmount: String(deposit) } : {}),
|
|
139
153
|
quantity: 1,
|
|
140
|
-
paymentOption: getPaymentOption(item.service, hasDeposit),
|
|
154
|
+
paymentOption: getPaymentOption(item.service, hasDeposit, selectedPaymentOption),
|
|
141
155
|
};
|
|
142
156
|
}
|
|
143
157
|
}
|
|
144
|
-
return buildLineItem(item.service, totalParticipants, item.lineItemId);
|
|
158
|
+
return buildLineItem(item.service, totalParticipants, item.lineItemId, selectedPaymentOption);
|
|
145
159
|
}
|
|
146
|
-
async function calculatePaymentDetails(pricingServiceSelections, totalParticipants) {
|
|
160
|
+
async function calculatePaymentDetails(pricingServiceSelections, totalParticipants, selectedPaymentOption) {
|
|
147
161
|
const data = pricingServiceSelections.map((selection) => ({
|
|
148
162
|
...selection,
|
|
149
163
|
lineItemId: selection.lineItemId || generateLineItemId(),
|
|
150
164
|
}));
|
|
151
|
-
const lineItems = await Promise.all(data.map((item) => buildLineItemWithPreview(item, totalParticipants)));
|
|
165
|
+
const lineItems = await Promise.all(data.map((item) => buildLineItemWithPreview(item, totalParticipants, selectedPaymentOption)));
|
|
152
166
|
const response = await calculateTotals({
|
|
153
167
|
lineItems,
|
|
154
168
|
});
|
|
@@ -179,7 +193,7 @@ export const PaymentService = implementService.withConfig()(PaymentServiceDefini
|
|
|
179
193
|
const errorSignal = signalsService.signal(null);
|
|
180
194
|
const serviceOptionsSignal = signalsService.signal(config.serviceOptions || DEFAULT_SERVICE_OPTIONS);
|
|
181
195
|
// Internal calculate function
|
|
182
|
-
const calculate = async (pricingServiceSelections, totalParticipants) => {
|
|
196
|
+
const calculate = async (pricingServiceSelections, totalParticipants, selectedPaymentOption) => {
|
|
183
197
|
if (!pricingServiceSelections.length) {
|
|
184
198
|
paymentDetailsSignal.set(null);
|
|
185
199
|
return;
|
|
@@ -187,7 +201,7 @@ export const PaymentService = implementService.withConfig()(PaymentServiceDefini
|
|
|
187
201
|
isLoadingSignal.set(true);
|
|
188
202
|
errorSignal.set(null);
|
|
189
203
|
try {
|
|
190
|
-
const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, totalParticipants);
|
|
204
|
+
const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, totalParticipants, selectedPaymentOption);
|
|
191
205
|
paymentDetailsSignal.set(paymentDetails);
|
|
192
206
|
}
|
|
193
207
|
catch (error) {
|
|
@@ -215,24 +229,30 @@ export const PaymentService = implementService.withConfig()(PaymentServiceDefini
|
|
|
215
229
|
// Mode 2: Lazy loading - pricingServiceSelections provided via internal config
|
|
216
230
|
else if (config.pricingServiceSelections?.length) {
|
|
217
231
|
serviceOptionsSignal.set(deriveServiceOptions(config.pricingServiceSelections));
|
|
218
|
-
void calculate(config.pricingServiceSelections, config.totalParticipants ?? 1);
|
|
232
|
+
void calculate(config.pricingServiceSelections, config.totalParticipants ?? 1, config.selectedPaymentOption);
|
|
219
233
|
}
|
|
220
234
|
// Mode 3: Reactive - observe BookingService signals (client-side only)
|
|
221
235
|
else if (typeof window !== 'undefined' && bookingService) {
|
|
222
|
-
// Computed signal that changes when services
|
|
236
|
+
// Computed signal that changes when services, selected time slots, or
|
|
237
|
+
// the consumer-selected payment option change (not participants).
|
|
223
238
|
const servicesKey = signalsService.computed(() => {
|
|
224
239
|
const selections = bookingService?.serviceSelections.get() || [];
|
|
225
240
|
const timezone = bookingService?.timezone.get();
|
|
241
|
+
const selectedPaymentOption = bookingService?.selectedPaymentOption.get();
|
|
226
242
|
return (selections
|
|
227
243
|
.map((s) => `${s.service._id}:${s.timeSlot?.startDate ?? ''}:${s.timeSlot?.endDate ?? ''}`)
|
|
228
|
-
.join(',') +
|
|
244
|
+
.join(',') +
|
|
245
|
+
`:${timezone ?? ''}` +
|
|
246
|
+
`:${selectedPaymentOption ?? ''}`);
|
|
229
247
|
});
|
|
230
248
|
signalsService.effect(() => {
|
|
231
|
-
// Subscribe to servicesKey to trigger effect when service
|
|
249
|
+
// Subscribe to servicesKey to trigger effect when service, time
|
|
250
|
+
// slot, or selected payment option changes.
|
|
232
251
|
servicesKey.get();
|
|
233
252
|
// Read current selections without subscribing (peek)
|
|
234
253
|
const selections = bookingService?.serviceSelections.peek() || [];
|
|
235
254
|
const timezone = bookingService?.timezone.peek();
|
|
255
|
+
const selectedPaymentOption = bookingService?.selectedPaymentOption.peek();
|
|
236
256
|
// For single service mode, take the first selection
|
|
237
257
|
const firstSelection = selections[0];
|
|
238
258
|
const numParticipants = firstSelection?.totalParticipants || 1;
|
|
@@ -246,7 +266,7 @@ export const PaymentService = implementService.withConfig()(PaymentServiceDefini
|
|
|
246
266
|
if (pricingServiceSelections.length) {
|
|
247
267
|
serviceOptionsSignal.set(deriveServiceOptions(pricingServiceSelections));
|
|
248
268
|
}
|
|
249
|
-
void calculate(pricingServiceSelections, numParticipants);
|
|
269
|
+
void calculate(pricingServiceSelections, numParticipants, selectedPaymentOption);
|
|
250
270
|
});
|
|
251
271
|
}
|
|
252
272
|
return {
|
|
@@ -262,10 +282,12 @@ export const PaymentService = implementService.withConfig()(PaymentServiceDefini
|
|
|
262
282
|
*
|
|
263
283
|
* @param params.pricingServiceSelections - Pricing service selections for calculation
|
|
264
284
|
* @param params.totalParticipants - Number of participants (default: 1)
|
|
285
|
+
* @param params.selectedPaymentOption - Optional consumer-selected payment option;
|
|
286
|
+
* overrides the service-config heuristic on each line item's `paymentOption`.
|
|
265
287
|
*/
|
|
266
288
|
export async function loadPaymentConfig(params) {
|
|
267
289
|
try {
|
|
268
|
-
const { pricingServiceSelections, totalParticipants = 1 } = params;
|
|
290
|
+
const { pricingServiceSelections, totalParticipants = 1, selectedPaymentOption, } = params;
|
|
269
291
|
if (!pricingServiceSelections.length) {
|
|
270
292
|
return {
|
|
271
293
|
type: 'success',
|
|
@@ -275,7 +297,7 @@ export async function loadPaymentConfig(params) {
|
|
|
275
297
|
},
|
|
276
298
|
};
|
|
277
299
|
}
|
|
278
|
-
const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, totalParticipants);
|
|
300
|
+
const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, totalParticipants, selectedPaymentOption);
|
|
279
301
|
return {
|
|
280
302
|
type: 'success',
|
|
281
303
|
config: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/headless-bookings",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"scripts": {
|
|
@@ -75,5 +75,5 @@
|
|
|
75
75
|
"groupId": "com.wixpress.headless-components"
|
|
76
76
|
}
|
|
77
77
|
},
|
|
78
|
-
"falconPackageHash": "
|
|
78
|
+
"falconPackageHash": "de3338c8d4e43446a69ef7116ba391beea6642f2f39551d997fe0ca1"
|
|
79
79
|
}
|