@wix/headless-bookings 0.0.117 → 1.0.0
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/api/calculate-cart/index.d.ts +3 -0
- package/cjs/dist/api/calculate-cart/index.js +7 -0
- package/cjs/dist/api/create-cart/index.d.ts +3 -0
- package/cjs/dist/api/create-cart/index.js +10 -0
- package/cjs/dist/api/index.d.ts +3 -9
- package/cjs/dist/api/index.js +12 -17
- package/cjs/dist/api/place-order/index.d.ts +3 -0
- package/cjs/dist/api/place-order/index.js +7 -0
- package/cjs/dist/react/booking/Book.js +1 -1
- package/cjs/dist/react/core/booking/Book.d.ts +1 -1
- package/cjs/dist/react/core/booking/Book.js +1 -1
- package/cjs/dist/react/core/payment/Payment.d.ts +14 -1
- package/cjs/dist/react/core/payment/Payment.js +12 -0
- package/cjs/dist/react/payment/Payment.d.ts +37 -1
- package/cjs/dist/react/payment/Payment.js +32 -1
- package/cjs/dist/services/booking/book-action/bookAction.d.ts +0 -22
- package/cjs/dist/services/booking/book-action/bookAction.js +20 -39
- package/cjs/dist/services/booking/book-action/buildCartRequest.d.ts +10 -0
- package/cjs/dist/services/booking/book-action/buildCartRequest.js +37 -0
- package/cjs/dist/services/booking/book-action/index.d.ts +1 -1
- package/cjs/dist/services/booking/book-action/index.js +3 -3
- package/cjs/dist/services/booking/book-action/isCheckoutRequired.d.ts +3 -19
- package/cjs/dist/services/booking/book-action/isCheckoutRequired.js +11 -29
- package/cjs/dist/services/booking/book-action/types.d.ts +1 -1
- package/cjs/dist/services/index.d.ts +2 -2
- package/cjs/dist/services/index.js +2 -2
- package/cjs/dist/services/payment/payment.d.ts +1 -1
- package/cjs/dist/services/payment/payment.def.d.ts +15 -0
- package/cjs/dist/services/payment/payment.js +51 -3
- package/cjs/dist/utils/navigation.d.ts +2 -2
- package/cjs/dist/utils/navigation.js +2 -2
- package/dist/api/calculate-cart/index.d.ts +3 -0
- package/dist/api/calculate-cart/index.js +4 -0
- package/dist/api/create-cart/index.d.ts +3 -0
- package/dist/api/create-cart/index.js +5 -0
- package/dist/api/index.d.ts +3 -9
- package/dist/api/index.js +3 -9
- package/dist/api/place-order/index.d.ts +3 -0
- package/dist/api/place-order/index.js +4 -0
- package/dist/react/booking/Book.js +1 -1
- package/dist/react/core/booking/Book.d.ts +1 -1
- package/dist/react/core/booking/Book.js +1 -1
- package/dist/react/core/payment/Payment.d.ts +14 -1
- package/dist/react/core/payment/Payment.js +11 -0
- package/dist/react/payment/Payment.d.ts +37 -1
- package/dist/react/payment/Payment.js +31 -0
- package/dist/services/booking/book-action/bookAction.d.ts +0 -22
- package/dist/services/booking/book-action/bookAction.js +20 -39
- package/dist/services/booking/book-action/buildCartRequest.d.ts +10 -0
- package/dist/services/booking/book-action/buildCartRequest.js +34 -0
- package/dist/services/booking/book-action/index.d.ts +1 -1
- package/dist/services/booking/book-action/index.js +1 -1
- package/dist/services/booking/book-action/isCheckoutRequired.d.ts +3 -19
- package/dist/services/booking/book-action/isCheckoutRequired.js +11 -29
- package/dist/services/booking/book-action/types.d.ts +1 -1
- package/dist/services/index.d.ts +2 -2
- package/dist/services/index.js +1 -1
- package/dist/services/payment/payment.d.ts +1 -1
- package/dist/services/payment/payment.def.d.ts +15 -0
- package/dist/services/payment/payment.js +51 -3
- package/dist/utils/navigation.d.ts +2 -2
- package/dist/utils/navigation.js +2 -2
- package/package.json +3 -3
- package/cjs/dist/api/create-checkout/index.d.ts +0 -40
- package/cjs/dist/api/create-checkout/index.js +0 -42
- package/cjs/dist/api/create-order/index.d.ts +0 -20
- package/cjs/dist/api/create-order/index.js +0 -24
- package/cjs/dist/services/booking/book-action/buildCheckoutRequest.d.ts +0 -28
- package/cjs/dist/services/booking/book-action/buildCheckoutRequest.js +0 -65
- package/dist/api/create-checkout/index.d.ts +0 -40
- package/dist/api/create-checkout/index.js +0 -37
- package/dist/api/create-order/index.d.ts +0 -20
- package/dist/api/create-order/index.js +0 -21
- package/dist/services/booking/book-action/buildCheckoutRequest.d.ts +0 -28
- package/dist/services/booking/book-action/buildCheckoutRequest.js +0 -62
|
@@ -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
|
-
|
|
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: {
|
|
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: {
|
|
223
|
+
config: {
|
|
224
|
+
paymentDetails,
|
|
225
|
+
serviceOptions: deriveServiceOptions(pricingServiceSelections),
|
|
226
|
+
},
|
|
179
227
|
};
|
|
180
228
|
}
|
|
181
229
|
catch (error) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export interface NavigationInfo {
|
|
2
2
|
postFlowUrl: string;
|
|
3
3
|
}
|
|
4
|
-
export declare function navigateToCheckout({
|
|
5
|
-
|
|
4
|
+
export declare function navigateToCheckout({ cartId, navigationInfo, }: {
|
|
5
|
+
cartId: string;
|
|
6
6
|
navigationInfo: NavigationInfo;
|
|
7
7
|
}): Promise<void>;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.navigateToCheckout = navigateToCheckout;
|
|
4
4
|
const redirects_1 = require("@wix/redirects");
|
|
5
|
-
async function navigateToCheckout({
|
|
5
|
+
async function navigateToCheckout({ cartId, navigationInfo, }) {
|
|
6
6
|
const { redirectSession } = await redirects_1.redirects.createRedirectSession({
|
|
7
|
-
ecomCheckout: { checkoutId },
|
|
7
|
+
ecomCheckout: { checkoutId: cartId },
|
|
8
8
|
callbacks: {
|
|
9
9
|
postFlowUrl: navigationInfo.postFlowUrl,
|
|
10
10
|
},
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type CalculateCartOptions, type CalculateCartResponse } from '@wix/auto_sdk_ecom_cart-v-2';
|
|
2
|
+
export declare function calculateCart(cartId: string, options?: CalculateCartOptions): Promise<CalculateCartResponse>;
|
|
3
|
+
export type { CalculateCartResponse, CartSummary, } from '@wix/auto_sdk_ecom_cart-v-2';
|
package/dist/api/index.d.ts
CHANGED
|
@@ -7,14 +7,8 @@
|
|
|
7
7
|
export { getServiceById, getServiceBySlug, queryServices, convertPlatformFilterToApiFilter, type PagingMetadata, type QueryServicesRequest, SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, } from './query-services/index.js';
|
|
8
8
|
export { fetchAvailability } from './fetch-availability/index.js';
|
|
9
9
|
export { createBooking, type CreateBookingOptions, type Booking, type CreateBookingRequest, type CreateBookingResponse, type ParticipantNotification, type ContactDetails, } from './create-booking/index.js';
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
10
|
+
export { createCart, ChannelType, type CreateCartOptions, type Cart, } from './create-cart/index.js';
|
|
11
|
+
export { calculateCart, type CalculateCartResponse, type CartSummary, } from './calculate-cart/index.js';
|
|
12
|
+
export { placeOrder, type PlaceOrderOptions, type PlaceOrderResponse, } from './place-order/index.js';
|
|
12
13
|
export { queryLocations, getLocationById, type QueryLocationsResult, type LocationData, } from './query-locations/index.js';
|
|
13
14
|
export { queryCategories, type QueryCategoriesResult, type Category, } from './query-categories/index.js';
|
|
14
|
-
/**
|
|
15
|
-
* PagingMetadata includes:
|
|
16
|
-
* - limit: Number of items per page
|
|
17
|
-
* - offset: Number of items to skip
|
|
18
|
-
* - count: Number of items returned in the current page
|
|
19
|
-
* - hasNext: Whether there are more services available to load
|
|
20
|
-
*/
|
package/dist/api/index.js
CHANGED
|
@@ -7,14 +7,8 @@
|
|
|
7
7
|
export { getServiceById, getServiceBySlug, queryServices, convertPlatformFilterToApiFilter, SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, } from './query-services/index.js';
|
|
8
8
|
export { fetchAvailability } from './fetch-availability/index.js';
|
|
9
9
|
export { createBooking, } from './create-booking/index.js';
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
10
|
+
export { createCart, ChannelType, } from './create-cart/index.js';
|
|
11
|
+
export { calculateCart, } from './calculate-cart/index.js';
|
|
12
|
+
export { placeOrder, } from './place-order/index.js';
|
|
12
13
|
export { queryLocations, getLocationById, } from './query-locations/index.js';
|
|
13
14
|
export { queryCategories, } from './query-categories/index.js';
|
|
14
|
-
/**
|
|
15
|
-
* PagingMetadata includes:
|
|
16
|
-
* - limit: Number of items per page
|
|
17
|
-
* - offset: Number of items to skip
|
|
18
|
-
* - count: Number of items returned in the current page
|
|
19
|
-
* - hasNext: Whether there are more services available to load
|
|
20
|
-
*/
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type PlaceOrderOptions, type PlaceOrderResponse } from '@wix/auto_sdk_ecom_cart-v-2';
|
|
2
|
+
export declare function placeOrder(cartId: string, options?: PlaceOrderOptions): Promise<PlaceOrderResponse>;
|
|
3
|
+
export type { PlaceOrderOptions, PlaceOrderResponse, } from '@wix/auto_sdk_ecom_cart-v-2';
|
|
@@ -60,7 +60,7 @@ export const Book = React.forwardRef((props, ref) => {
|
|
|
60
60
|
return (_jsx(CoreBook.Book, { onCheckout: (result) => {
|
|
61
61
|
// Create navigateToCheckout with checkoutId captured
|
|
62
62
|
const navigateToCheckout = (options) => navigateToCheckoutUtil({
|
|
63
|
-
|
|
63
|
+
cartId: result.cartId,
|
|
64
64
|
navigationInfo: { postFlowUrl: options.postFlowUrl },
|
|
65
65
|
});
|
|
66
66
|
// Return user's callback result so core can await it
|
|
@@ -24,7 +24,7 @@ export interface BookProps {
|
|
|
24
24
|
* Return a Promise to keep loading state active until it resolves.
|
|
25
25
|
*/
|
|
26
26
|
onCheckout?: (result: {
|
|
27
|
-
|
|
27
|
+
cartId: string;
|
|
28
28
|
}) => void | Promise<unknown>;
|
|
29
29
|
/**
|
|
30
30
|
* Called when booking completes without requiring checkout.
|
|
@@ -49,7 +49,7 @@ export function Book(props) {
|
|
|
49
49
|
try {
|
|
50
50
|
const result = await bookingService.actions.book();
|
|
51
51
|
if (result.type === BookResultType.CheckoutRequired) {
|
|
52
|
-
await onCheckout?.({
|
|
52
|
+
await onCheckout?.({ cartId: result.cartId });
|
|
53
53
|
}
|
|
54
54
|
else {
|
|
55
55
|
await onComplete?.({ orderId: result.orderId });
|
|
@@ -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.
|
|
@@ -1,24 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Book Action - Orchestrates the booking flow
|
|
3
|
-
*
|
|
4
|
-
* 1. Validates booking data with canBook()
|
|
5
|
-
* 2. Creates a booking via createBooking() from API layer
|
|
6
|
-
* 3. Creates a checkout via createCheckout() from API layer
|
|
7
|
-
* 4. Determines if checkout is required or can be skipped
|
|
8
|
-
* 5. Returns appropriate result for navigation
|
|
9
|
-
*/
|
|
10
1
|
import { type BookActionParams, type BookResult } from './types.js';
|
|
11
|
-
/**
|
|
12
|
-
* Executes the complete booking flow:
|
|
13
|
-
* 1. Validates that all required data is present
|
|
14
|
-
* 2. Creates a booking with the selected service, time slot, and form data
|
|
15
|
-
* 3. Creates a checkout session for payment (includes contact details from booking)
|
|
16
|
-
* 4. Determines if checkout page is required or can be skipped
|
|
17
|
-
* 5. If checkout can be skipped (free/offline), creates order directly
|
|
18
|
-
* 6. Returns appropriate result for navigation
|
|
19
|
-
*
|
|
20
|
-
* @param params - The booking action parameters
|
|
21
|
-
* @returns BookResult with either checkoutId or orderId
|
|
22
|
-
* @throws Error if validation fails, or booking/checkout/order creation fails
|
|
23
|
-
*/
|
|
24
2
|
export declare function executeBookAction(params: BookActionParams): Promise<BookResult>;
|
|
@@ -1,35 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Book Action - Orchestrates the booking flow
|
|
3
|
-
*
|
|
4
|
-
* 1. Validates booking data with canBook()
|
|
5
|
-
* 2. Creates a booking via createBooking() from API layer
|
|
6
|
-
* 3. Creates a checkout via createCheckout() from API layer
|
|
7
|
-
* 4. Determines if checkout is required or can be skipped
|
|
8
|
-
* 5. Returns appropriate result for navigation
|
|
9
|
-
*/
|
|
10
1
|
import { createBooking } from '../../../api/create-booking/index.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
2
|
+
import { createCart } from '../../../api/create-cart/index.js';
|
|
3
|
+
import { calculateCart } from '../../../api/calculate-cart/index.js';
|
|
4
|
+
import { placeOrder } from '../../../api/place-order/index.js';
|
|
13
5
|
import { buildBookingRequest } from './buildBookingRequest.js';
|
|
14
|
-
import {
|
|
6
|
+
import { buildCartRequest } from './buildCartRequest.js';
|
|
15
7
|
import { canBook } from './canBook.js';
|
|
16
8
|
import { isCheckoutRequired } from './isCheckoutRequired.js';
|
|
17
9
|
import { BookResultType, } from './types.js';
|
|
18
|
-
/**
|
|
19
|
-
* Executes the complete booking flow:
|
|
20
|
-
* 1. Validates that all required data is present
|
|
21
|
-
* 2. Creates a booking with the selected service, time slot, and form data
|
|
22
|
-
* 3. Creates a checkout session for payment (includes contact details from booking)
|
|
23
|
-
* 4. Determines if checkout page is required or can be skipped
|
|
24
|
-
* 5. If checkout can be skipped (free/offline), creates order directly
|
|
25
|
-
* 6. Returns appropriate result for navigation
|
|
26
|
-
*
|
|
27
|
-
* @param params - The booking action parameters
|
|
28
|
-
* @returns BookResult with either checkoutId or orderId
|
|
29
|
-
* @throws Error if validation fails, or booking/checkout/order creation fails
|
|
30
|
-
*/
|
|
31
10
|
export async function executeBookAction(params) {
|
|
32
|
-
// Step 0: Validate
|
|
33
11
|
if (!canBook({
|
|
34
12
|
serviceSelections: params.serviceSelections,
|
|
35
13
|
formSubmission: params.formSubmission,
|
|
@@ -48,34 +26,37 @@ export async function executeBookAction(params) {
|
|
|
48
26
|
if (!bookingId) {
|
|
49
27
|
throw new Error('Failed to create booking');
|
|
50
28
|
}
|
|
51
|
-
// Step 2: Create
|
|
52
|
-
// businessLocationId: prefer timeSlot.location, fallback to global location
|
|
29
|
+
// Step 2: Create cart
|
|
53
30
|
const firstSelection = params.serviceSelections[0];
|
|
54
31
|
if (!firstSelection) {
|
|
55
32
|
throw new Error('No service selection found');
|
|
56
33
|
}
|
|
57
34
|
const businessLocationId = firstSelection.timeSlot?.location?._id ?? undefined;
|
|
58
|
-
const
|
|
35
|
+
const cartRequest = buildCartRequest({
|
|
59
36
|
bookingId,
|
|
60
37
|
contactDetails,
|
|
61
38
|
businessLocationId,
|
|
62
39
|
appId: params.appId,
|
|
63
40
|
});
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
throw new Error('Failed to create checkout');
|
|
41
|
+
const cart = await createCart(cartRequest);
|
|
42
|
+
if (!cart._id) {
|
|
43
|
+
throw new Error('Failed to create cart');
|
|
68
44
|
}
|
|
69
|
-
// Step 3:
|
|
45
|
+
// Step 3: Calculate cart to get totals (not stored on Cart V2 entity)
|
|
46
|
+
const { cart: calculatedCart, summary } = await calculateCart(cart._id);
|
|
47
|
+
if (!calculatedCart || !summary) {
|
|
48
|
+
throw new Error('Failed to calculate cart');
|
|
49
|
+
}
|
|
50
|
+
// Step 4: Determine if checkout is required or can be skipped
|
|
70
51
|
const service = firstSelection.service;
|
|
71
|
-
if (isCheckoutRequired(
|
|
72
|
-
return { type: BookResultType.CheckoutRequired,
|
|
52
|
+
if (isCheckoutRequired(calculatedCart, summary, service)) {
|
|
53
|
+
return { type: BookResultType.CheckoutRequired, cartId: cart._id };
|
|
73
54
|
}
|
|
74
|
-
// Step
|
|
75
|
-
const orderResponse = await
|
|
55
|
+
// Step 5: Skip checkout — place order directly
|
|
56
|
+
const orderResponse = await placeOrder(cart._id);
|
|
76
57
|
const orderId = orderResponse.orderId;
|
|
77
58
|
if (!orderId) {
|
|
78
|
-
throw new Error('Failed to
|
|
59
|
+
throw new Error('Failed to place order');
|
|
79
60
|
}
|
|
80
61
|
return { type: BookResultType.CheckoutSkipped, orderId };
|
|
81
62
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type CreateCartOptions } from '../../../api/create-cart/index.js';
|
|
2
|
+
import type { ContactDetails } from '../../../api/create-booking/index.js';
|
|
3
|
+
export interface BuildCartParams {
|
|
4
|
+
bookingId: string;
|
|
5
|
+
contactDetails?: ContactDetails;
|
|
6
|
+
businessLocationId?: string;
|
|
7
|
+
/** Optional app ID override for the catalog reference. Defaults to BOOKING_APP_ID. */
|
|
8
|
+
appId?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function buildCartRequest(params: BuildCartParams): CreateCartOptions;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ChannelType, } from '../../../api/create-cart/index.js';
|
|
2
|
+
import { BOOKING_APP_ID } from '../../constants.js';
|
|
3
|
+
export function buildCartRequest(params) {
|
|
4
|
+
const { bookingId, contactDetails, businessLocationId, appId } = params;
|
|
5
|
+
const cart = {
|
|
6
|
+
source: { channelType: ChannelType.WEB },
|
|
7
|
+
};
|
|
8
|
+
if (businessLocationId) {
|
|
9
|
+
cart.businessInfo = { locationId: businessLocationId };
|
|
10
|
+
}
|
|
11
|
+
if (contactDetails) {
|
|
12
|
+
cart.customerInfo = {
|
|
13
|
+
...(contactDetails.email ? { email: contactDetails.email } : {}),
|
|
14
|
+
};
|
|
15
|
+
if (contactDetails.fullAddress?.country) {
|
|
16
|
+
cart.deliveryInfo = { address: { ...contactDetails.fullAddress } };
|
|
17
|
+
cart.paymentInfo = {
|
|
18
|
+
billingAddress: { ...contactDetails.fullAddress },
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
catalogItems: [
|
|
24
|
+
{
|
|
25
|
+
quantity: 1,
|
|
26
|
+
catalogReference: {
|
|
27
|
+
catalogItemId: bookingId,
|
|
28
|
+
appId: appId ?? BOOKING_APP_ID,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
cart,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
export { executeBookAction } from './bookAction.js';
|
|
5
5
|
export { canBook, type CanBookParams } from './canBook.js';
|
|
6
6
|
export { buildBookingRequest } from './buildBookingRequest.js';
|
|
7
|
-
export {
|
|
7
|
+
export { buildCartRequest, type BuildCartParams } from './buildCartRequest.js';
|
|
8
8
|
export { isCheckoutRequired } from './isCheckoutRequired.js';
|
|
9
9
|
export { BookResultType, type BookProps, type BookResult, type BookingError, type BookActionParams, type BookChildProps, } from './types.js';
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
export { executeBookAction } from './bookAction.js';
|
|
5
5
|
export { canBook } from './canBook.js';
|
|
6
6
|
export { buildBookingRequest } from './buildBookingRequest.js';
|
|
7
|
-
export {
|
|
7
|
+
export { buildCartRequest } from './buildCartRequest.js';
|
|
8
8
|
export { isCheckoutRequired } from './isCheckoutRequired.js';
|
|
9
9
|
export { BookResultType, } from './types.js';
|
|
@@ -1,20 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*/
|
|
4
|
-
import type { Checkout } from '@wix/auto_sdk_ecom_checkout';
|
|
1
|
+
import type { Cart } from '../../../api/create-cart/index.js';
|
|
2
|
+
import type { CartSummary } from '../../../api/calculate-cart/index.js';
|
|
5
3
|
import type { Service } from '@wix/auto_sdk_bookings_services';
|
|
6
|
-
|
|
7
|
-
* Determines if checkout page is required based on payment amounts.
|
|
8
|
-
*
|
|
9
|
-
* Skip checkout when:
|
|
10
|
-
* - Free booking: payNow = 0 AND payLater = 0
|
|
11
|
-
* - Offline payment: payNow = 0
|
|
12
|
-
*
|
|
13
|
-
* Force checkout when:
|
|
14
|
-
* - Service has cancellation fee policy
|
|
15
|
-
*
|
|
16
|
-
* @param checkout - The checkout response from createCheckout
|
|
17
|
-
* @param service - The service being booked
|
|
18
|
-
* @returns true if checkout page is required, false if can skip to order
|
|
19
|
-
*/
|
|
20
|
-
export declare function isCheckoutRequired(checkout: Checkout, service: Service): boolean;
|
|
4
|
+
export declare function isCheckoutRequired(cart: Cart, summary: CartSummary, service: Service): boolean;
|