@wix/headless-bookings 0.0.30 → 0.0.32
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/react/core/payment/Payment.d.ts +193 -0
- package/cjs/dist/react/core/payment/Payment.js +202 -0
- package/cjs/dist/react/index.d.ts +2 -0
- package/cjs/dist/react/index.js +2 -0
- package/cjs/dist/react/payment/Payment.d.ts +342 -0
- package/cjs/dist/react/payment/Payment.js +294 -0
- package/cjs/dist/react/payment/PaymentLineItem.d.ts +144 -0
- package/cjs/dist/react/payment/PaymentLineItem.js +141 -0
- package/cjs/dist/react/time-slot-list/TimeSlot.d.ts +23 -11
- package/cjs/dist/react/time-slot-list/TimeSlot.js +34 -16
- package/cjs/dist/react/time-slot-list/TimeSlotList.d.ts +6 -0
- package/cjs/dist/react/time-slot-list/TimeSlotList.js +23 -5
- package/cjs/dist/services/index.d.ts +1 -0
- package/cjs/dist/services/index.js +1 -0
- package/cjs/dist/services/payment/payment.d.ts +32 -0
- package/cjs/dist/services/payment/payment.def.d.ts +66 -0
- package/cjs/dist/services/payment/payment.def.js +9 -0
- package/cjs/dist/services/payment/payment.js +167 -0
- package/cjs/dist/utils/money.d.ts +11 -0
- package/cjs/dist/utils/money.js +16 -0
- package/dist/react/core/payment/Payment.d.ts +193 -0
- package/dist/react/core/payment/Payment.js +202 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +2 -0
- package/dist/react/payment/Payment.d.ts +342 -0
- package/dist/react/payment/Payment.js +294 -0
- package/dist/react/payment/PaymentLineItem.d.ts +144 -0
- package/dist/react/payment/PaymentLineItem.js +141 -0
- package/dist/react/time-slot-list/TimeSlot.d.ts +23 -11
- package/dist/react/time-slot-list/TimeSlot.js +34 -16
- package/dist/react/time-slot-list/TimeSlotList.d.ts +6 -0
- package/dist/react/time-slot-list/TimeSlotList.js +23 -5
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/payment/payment.d.ts +32 -0
- package/dist/services/payment/payment.def.d.ts +66 -0
- package/dist/services/payment/payment.def.js +9 -0
- package/dist/services/payment/payment.js +167 -0
- package/dist/utils/money.d.ts +11 -0
- package/dist/utils/money.js +16 -0
- package/package.json +2 -2
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment Service Definition
|
|
3
|
+
* Defines interfaces and service contract for payment calculation management
|
|
4
|
+
*/
|
|
5
|
+
import type { Signal } from '@wix/services-definitions/core-services/signals';
|
|
6
|
+
import type { CalculateTotalsResponse } from '@wix/auto_sdk_ecom_totals-calculator';
|
|
7
|
+
import type { Service } from '@wix/auto_sdk_bookings_services';
|
|
8
|
+
/**
|
|
9
|
+
* Payment option for line items
|
|
10
|
+
*/
|
|
11
|
+
export type PaymentOption = 'FULL_PAYMENT_ONLINE' | 'FULL_PAYMENT_OFFLINE';
|
|
12
|
+
/**
|
|
13
|
+
* Additional computed info for a line item (not in SDK response)
|
|
14
|
+
*/
|
|
15
|
+
export interface LineItemAdditionalInfo {
|
|
16
|
+
/** Line item ID to match with SDK's calculatedLineItems */
|
|
17
|
+
lineItemId: string;
|
|
18
|
+
/** Custom price text (for CUSTOM rate type services) */
|
|
19
|
+
priceText?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Payment calculation result with SDK response and additional computed info
|
|
23
|
+
*/
|
|
24
|
+
export interface PaymentDetails {
|
|
25
|
+
/** The raw SDK CalculateTotals response */
|
|
26
|
+
response: CalculateTotalsResponse;
|
|
27
|
+
/** Additional computed information per line item */
|
|
28
|
+
additionalInfo: {
|
|
29
|
+
lineItems: LineItemAdditionalInfo[];
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Input for calculating payment for a slot/service
|
|
34
|
+
*/
|
|
35
|
+
export interface SlotService {
|
|
36
|
+
service: Service;
|
|
37
|
+
lineItemId: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Public config for PaymentService (SSR mode only):
|
|
41
|
+
* Pass `paymentDetails` pre-fetched via loadPaymentConfig
|
|
42
|
+
*/
|
|
43
|
+
export interface PaymentServiceConfig {
|
|
44
|
+
/** Pre-calculated payment details for SSR (no API call) */
|
|
45
|
+
paymentDetails?: PaymentDetails;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Internal config used by Core component to pass lazy loading data
|
|
49
|
+
* Not exposed publicly - Core merges this with PaymentServiceConfig
|
|
50
|
+
*/
|
|
51
|
+
export interface PaymentServiceInternalConfig extends PaymentServiceConfig {
|
|
52
|
+
/** Slot services for lazy loading (internal use) */
|
|
53
|
+
slotServices?: SlotService[];
|
|
54
|
+
/** Number of participants for lazy loading (internal use) */
|
|
55
|
+
numberOfParticipants?: number;
|
|
56
|
+
}
|
|
57
|
+
export interface PaymentServiceAPI {
|
|
58
|
+
paymentDetailsSignal: Signal<PaymentDetails | null>;
|
|
59
|
+
isLoadingSignal: Signal<boolean>;
|
|
60
|
+
errorSignal: Signal<string | null>;
|
|
61
|
+
}
|
|
62
|
+
export declare const PaymentServiceDefinition: string & {
|
|
63
|
+
__api: PaymentServiceAPI;
|
|
64
|
+
__config: {};
|
|
65
|
+
isServiceDefinition?: boolean;
|
|
66
|
+
} & PaymentServiceAPI;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment Service Definition
|
|
3
|
+
* Defines interfaces and service contract for payment calculation management
|
|
4
|
+
*/
|
|
5
|
+
import { defineService } from '@wix/services-definitions';
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Service Definition
|
|
8
|
+
// ============================================================================
|
|
9
|
+
export const PaymentServiceDefinition = defineService('payment');
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment Service - Pricing calculation and payment details management
|
|
3
|
+
* Handles calculating total prices, taxes, discounts, and deposit information
|
|
4
|
+
* Supports two modes: SSR (config with pre-fetched data) and lazy loading (reactive)
|
|
5
|
+
*/
|
|
6
|
+
import { implementService } from '@wix/services-definitions';
|
|
7
|
+
import { SignalsServiceDefinition } from '@wix/services-definitions/core-services/signals';
|
|
8
|
+
import { calculateTotals, } from '@wix/auto_sdk_ecom_totals-calculator';
|
|
9
|
+
import { BookingServiceDefinition } from '../booking/booking.js';
|
|
10
|
+
import { RateType } from '@wix/auto_sdk_bookings_services';
|
|
11
|
+
import { PaymentServiceDefinition, } from './payment.def.js';
|
|
12
|
+
export { PaymentServiceDefinition, } from './payment.def.js';
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Constants
|
|
15
|
+
// ============================================================================
|
|
16
|
+
const BOOKINGS_APP_DEF_ID = '13d21c63-b5ec-5912-8397-c3a5ddb27a97';
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Helper Functions
|
|
19
|
+
// ============================================================================
|
|
20
|
+
function getAdditionalInfo(service, lineItemId) {
|
|
21
|
+
const payment = service.payment;
|
|
22
|
+
const priceText = payment?.rateType === RateType.CUSTOM && payment.custom?.description
|
|
23
|
+
? payment.custom.description
|
|
24
|
+
: undefined;
|
|
25
|
+
return { lineItemId, priceText };
|
|
26
|
+
}
|
|
27
|
+
function getPaymentOption(service) {
|
|
28
|
+
const options = service.payment?.options;
|
|
29
|
+
if (options?.inPerson) {
|
|
30
|
+
return 'FULL_PAYMENT_OFFLINE';
|
|
31
|
+
}
|
|
32
|
+
return 'FULL_PAYMENT_ONLINE';
|
|
33
|
+
}
|
|
34
|
+
function buildLineItem(service, numberOfParticipants, lineItemId) {
|
|
35
|
+
// Get fixed price from service
|
|
36
|
+
const servicePrice = service.payment?.fixed?.price?.value;
|
|
37
|
+
const price = Number(servicePrice || 0) * numberOfParticipants;
|
|
38
|
+
// Get deposit if configured
|
|
39
|
+
const deposit = service.payment?.fixed?.deposit?.value
|
|
40
|
+
? Number(service.payment.fixed.deposit.value) * numberOfParticipants
|
|
41
|
+
: undefined;
|
|
42
|
+
// Get payment option from service configuration
|
|
43
|
+
const paymentOption = getPaymentOption(service);
|
|
44
|
+
return {
|
|
45
|
+
_id: lineItemId,
|
|
46
|
+
catalogReference: {
|
|
47
|
+
catalogItemId: service._id || '',
|
|
48
|
+
appId: BOOKINGS_APP_DEF_ID,
|
|
49
|
+
},
|
|
50
|
+
price: String(price),
|
|
51
|
+
...(deposit ? { depositAmount: String(deposit) } : {}),
|
|
52
|
+
quantity: 1,
|
|
53
|
+
paymentOption,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async function calculatePaymentDetails(slotServices, numberOfParticipants) {
|
|
57
|
+
const lineItems = slotServices.map((input) => buildLineItem(input.service, numberOfParticipants, input.lineItemId));
|
|
58
|
+
const response = await calculateTotals({
|
|
59
|
+
lineItems,
|
|
60
|
+
});
|
|
61
|
+
const additionalInfoList = slotServices.map((input) => getAdditionalInfo(input.service, input.lineItemId));
|
|
62
|
+
return {
|
|
63
|
+
response,
|
|
64
|
+
additionalInfo: {
|
|
65
|
+
lineItems: additionalInfoList,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// ============================================================================
|
|
70
|
+
// Service Implementation
|
|
71
|
+
// ============================================================================
|
|
72
|
+
export const PaymentService = implementService.withConfig()(PaymentServiceDefinition, ({ getService, config }) => {
|
|
73
|
+
const signalsService = getService(SignalsServiceDefinition);
|
|
74
|
+
// Try to get BookingService (optional - for reactive mode)
|
|
75
|
+
let bookingService = null;
|
|
76
|
+
try {
|
|
77
|
+
bookingService = getService(BookingServiceDefinition);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
bookingService = null;
|
|
81
|
+
}
|
|
82
|
+
// Initialize signals
|
|
83
|
+
const paymentDetailsSignal = signalsService.signal(config.paymentDetails || null);
|
|
84
|
+
const isLoadingSignal = signalsService.signal(false);
|
|
85
|
+
const errorSignal = signalsService.signal(null);
|
|
86
|
+
// Internal calculate function
|
|
87
|
+
const calculate = async (slotServices, numberOfParticipants) => {
|
|
88
|
+
if (!slotServices.length) {
|
|
89
|
+
paymentDetailsSignal.set(null);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
isLoadingSignal.set(true);
|
|
93
|
+
errorSignal.set(null);
|
|
94
|
+
try {
|
|
95
|
+
const paymentDetails = await calculatePaymentDetails(slotServices, numberOfParticipants);
|
|
96
|
+
paymentDetailsSignal.set(paymentDetails);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
const errorMessage = error instanceof Error
|
|
100
|
+
? error.message
|
|
101
|
+
: 'Failed to calculate payment';
|
|
102
|
+
errorSignal.set(errorMessage);
|
|
103
|
+
console.error('Payment calculation error:', error);
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
isLoadingSignal.set(false);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
// Mode 1: SSR - paymentDetails already set in signal initialization
|
|
110
|
+
if (config.paymentDetails) {
|
|
111
|
+
// No-op - data is pre-fetched
|
|
112
|
+
}
|
|
113
|
+
// Mode 2: Lazy loading - slotServices provided via internal config
|
|
114
|
+
else if (config.slotServices?.length) {
|
|
115
|
+
void calculate(config.slotServices, config.numberOfParticipants ?? 1);
|
|
116
|
+
}
|
|
117
|
+
// Mode 3: Reactive - observe BookingService signals (client-side only)
|
|
118
|
+
else if (typeof window !== 'undefined' && bookingService) {
|
|
119
|
+
signalsService.effect(() => {
|
|
120
|
+
const selections = bookingService?.serviceSelections.get() || [];
|
|
121
|
+
// For single service mode, take the first selection
|
|
122
|
+
const firstSelection = selections[0];
|
|
123
|
+
const numParticipants = firstSelection?.totalParticipants || 1;
|
|
124
|
+
const services = selections.map((selection) => ({
|
|
125
|
+
service: selection.service,
|
|
126
|
+
lineItemId: selection.instanceId,
|
|
127
|
+
}));
|
|
128
|
+
void calculate(services, numParticipants);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
paymentDetailsSignal,
|
|
133
|
+
isLoadingSignal,
|
|
134
|
+
errorSignal,
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
/**
|
|
138
|
+
* Load payment configuration for Server-Side Rendering (SSR).
|
|
139
|
+
* Calls the pricing APIs and returns pre-calculated payment details.
|
|
140
|
+
*
|
|
141
|
+
* @param params.slotServices - Slot/service inputs for calculation
|
|
142
|
+
* @param params.numberOfParticipants - Number of participants (default: 1)
|
|
143
|
+
*/
|
|
144
|
+
export async function loadPaymentConfig(params) {
|
|
145
|
+
try {
|
|
146
|
+
const { slotServices, numberOfParticipants = 1 } = params;
|
|
147
|
+
if (!slotServices.length) {
|
|
148
|
+
return {
|
|
149
|
+
type: 'success',
|
|
150
|
+
config: { paymentDetails: undefined },
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const paymentDetails = await calculatePaymentDetails(slotServices, numberOfParticipants);
|
|
154
|
+
return {
|
|
155
|
+
type: 'success',
|
|
156
|
+
config: { paymentDetails },
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to load payment config';
|
|
161
|
+
console.error('Failed to load payment config:', error);
|
|
162
|
+
return {
|
|
163
|
+
type: 'error',
|
|
164
|
+
error: errorMessage,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -19,3 +19,14 @@ export declare function convertToMoneyData(sdkMoney: {
|
|
|
19
19
|
currency?: string;
|
|
20
20
|
formattedValue?: string | null;
|
|
21
21
|
} | undefined): MoneyData | null;
|
|
22
|
+
/**
|
|
23
|
+
* Converts ecom MultiCurrencyPrice format to MoneyData format
|
|
24
|
+
* MultiCurrencyPrice uses 'amount' as string, while MoneyData uses 'amount' as number
|
|
25
|
+
*
|
|
26
|
+
* @param price - The MultiCurrencyPrice object with amount as string
|
|
27
|
+
* @param currency - The currency code (from response level)
|
|
28
|
+
* @returns MoneyData object or null if conversion fails
|
|
29
|
+
*/
|
|
30
|
+
export declare function convertMultiCurrencyPriceToMoneyData(price: {
|
|
31
|
+
amount?: string;
|
|
32
|
+
} | undefined, currency: string): MoneyData | null;
|
package/cjs/dist/utils/money.js
CHANGED
|
@@ -32,3 +32,19 @@ export function convertToMoneyData(sdkMoney) {
|
|
|
32
32
|
}
|
|
33
33
|
return null;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Converts ecom MultiCurrencyPrice format to MoneyData format
|
|
37
|
+
* MultiCurrencyPrice uses 'amount' as string, while MoneyData uses 'amount' as number
|
|
38
|
+
*
|
|
39
|
+
* @param price - The MultiCurrencyPrice object with amount as string
|
|
40
|
+
* @param currency - The currency code (from response level)
|
|
41
|
+
* @returns MoneyData object or null if conversion fails
|
|
42
|
+
*/
|
|
43
|
+
export function convertMultiCurrencyPriceToMoneyData(price, currency) {
|
|
44
|
+
if (!price?.amount)
|
|
45
|
+
return null;
|
|
46
|
+
const parsed = parseFloat(price.amount);
|
|
47
|
+
if (isNaN(parsed) || parsed < 0)
|
|
48
|
+
return null;
|
|
49
|
+
return { amount: parsed, currency };
|
|
50
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Payment Components
|
|
3
|
+
* Provides low-level access to PaymentService via render props
|
|
4
|
+
* Note: Currently supports single service only
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { type PaymentServiceConfig, type PaymentDetails, type SlotService } from '../../../services/payment/payment.js';
|
|
8
|
+
import type { MoneyData } from '@wix/headless-components/react';
|
|
9
|
+
/**
|
|
10
|
+
* Props for Payment.Root component
|
|
11
|
+
*/
|
|
12
|
+
export interface RootProps {
|
|
13
|
+
children: React.ReactNode | ((props: {
|
|
14
|
+
isLoading: boolean;
|
|
15
|
+
hasError: boolean;
|
|
16
|
+
}) => React.ReactNode);
|
|
17
|
+
/** Service config - SSR only (paymentDetails) */
|
|
18
|
+
paymentServiceConfig?: PaymentServiceConfig;
|
|
19
|
+
/** Slot services for lazy loading (triggers API call) */
|
|
20
|
+
slotServices?: SlotService[];
|
|
21
|
+
/** Number of participants for lazy loading */
|
|
22
|
+
numberOfParticipants?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Root component that provides payment context.
|
|
26
|
+
* Supports 3 modes:
|
|
27
|
+
* 1. SSR: paymentServiceConfig.paymentDetails - pre-fetched data
|
|
28
|
+
* 2. Lazy loading: slotServices + numberOfParticipants props - calls API on init
|
|
29
|
+
* 3. Reactive: no config/props - reads from BookingService signals
|
|
30
|
+
*
|
|
31
|
+
* @component
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* // SSR mode
|
|
35
|
+
* <Payment.Root paymentServiceConfig={{ paymentDetails }}>
|
|
36
|
+
* <Payment.Summary />
|
|
37
|
+
* </Payment.Root>
|
|
38
|
+
*
|
|
39
|
+
* // Lazy loading mode
|
|
40
|
+
* <Payment.Root slotServices={[...]} numberOfParticipants={2}>
|
|
41
|
+
* {({ isLoading }) => isLoading ? <div>Loading...</div> : <Payment.Summary />}
|
|
42
|
+
* </Payment.Root>
|
|
43
|
+
*
|
|
44
|
+
* // Reactive mode
|
|
45
|
+
* <Payment.Root>
|
|
46
|
+
* <Payment.Summary />
|
|
47
|
+
* </Payment.Root>
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare function Root(props: RootProps): React.ReactNode;
|
|
51
|
+
/**
|
|
52
|
+
* Props for Payment render prop component
|
|
53
|
+
*/
|
|
54
|
+
export interface PaymentProps {
|
|
55
|
+
children: (data: {
|
|
56
|
+
paymentDetails: PaymentDetails | null;
|
|
57
|
+
isLoading: boolean;
|
|
58
|
+
error: string | null;
|
|
59
|
+
}) => React.ReactNode;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Core component that provides access to full payment details via render props.
|
|
63
|
+
*
|
|
64
|
+
* @component
|
|
65
|
+
* @example
|
|
66
|
+
* ```tsx
|
|
67
|
+
* <Payment.Payment>
|
|
68
|
+
* {({ paymentDetails, isLoading, error }) => (
|
|
69
|
+
* isLoading ? <div>Loading...</div> : <div>{JSON.stringify(paymentDetails)}</div>
|
|
70
|
+
* )}
|
|
71
|
+
* </Payment.Payment>
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare function Payment(props: PaymentProps): React.ReactNode;
|
|
75
|
+
/**
|
|
76
|
+
* Props for Summary render prop component
|
|
77
|
+
*/
|
|
78
|
+
export interface SummaryProps {
|
|
79
|
+
children: (data: {
|
|
80
|
+
subtotal: MoneyData | null;
|
|
81
|
+
total: MoneyData | null;
|
|
82
|
+
currency: string;
|
|
83
|
+
tax: MoneyData | null;
|
|
84
|
+
hasTax: boolean;
|
|
85
|
+
discount: MoneyData | null;
|
|
86
|
+
hasDiscount: boolean;
|
|
87
|
+
payNow: MoneyData | null;
|
|
88
|
+
payLater: MoneyData | null;
|
|
89
|
+
isDeposit: boolean;
|
|
90
|
+
}) => React.ReactNode;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Core component that provides access to payment summary totals via render props.
|
|
94
|
+
* Returns all pricing totals with MoneyData format for the Money component.
|
|
95
|
+
* Use data-attributes for conditional visibility via CSS.
|
|
96
|
+
*
|
|
97
|
+
* @component
|
|
98
|
+
* @example
|
|
99
|
+
* ```tsx
|
|
100
|
+
* <Payment.Summary>
|
|
101
|
+
* {({ subtotal, tax, total, hasTax, hasDiscount, discount, isDeposit, payNow }) => (
|
|
102
|
+
* <div>
|
|
103
|
+
* <div>Subtotal: <Money money={subtotal} /></div>
|
|
104
|
+
* <div data-has-tax={hasTax}>Tax: <Money money={tax} /></div>
|
|
105
|
+
* <div data-has-discount={hasDiscount}>Discount: <Money money={discount} /></div>
|
|
106
|
+
* <div>Total: <Money money={total} /></div>
|
|
107
|
+
* <div data-has-deposit={isDeposit}>Pay Now: <Money money={payNow} /></div>
|
|
108
|
+
* </div>
|
|
109
|
+
* )}
|
|
110
|
+
* </Payment.Summary>
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export declare function Summary(props: SummaryProps): React.ReactNode;
|
|
114
|
+
import type { CalculatedLineItem as ApiCalculatedLineItem } from '@wix/auto_sdk_ecom_totals-calculator';
|
|
115
|
+
import type { LineItemAdditionalInfo } from '../../../services/payment/payment.js';
|
|
116
|
+
/**
|
|
117
|
+
* Combined line item with SDK data and additional computed info
|
|
118
|
+
*/
|
|
119
|
+
export interface CombinedLineItem {
|
|
120
|
+
/** SDK line item data */
|
|
121
|
+
sdkLineItem: ApiCalculatedLineItem;
|
|
122
|
+
/** Additional computed info */
|
|
123
|
+
additionalInfo: LineItemAdditionalInfo | undefined;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Props for LineItems render prop component
|
|
127
|
+
*/
|
|
128
|
+
export interface LineItemsProps {
|
|
129
|
+
children: (data: {
|
|
130
|
+
lineItems: CombinedLineItem[];
|
|
131
|
+
hasLineItems: boolean;
|
|
132
|
+
currency: string;
|
|
133
|
+
}) => React.ReactNode;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Core component that provides access to calculated line items via render props.
|
|
137
|
+
* Combines SDK line items with additional computed info.
|
|
138
|
+
* Note: Currently supports single service only (always 1 line item).
|
|
139
|
+
*
|
|
140
|
+
* @component
|
|
141
|
+
* @example
|
|
142
|
+
* ```tsx
|
|
143
|
+
* <Payment.LineItems>
|
|
144
|
+
* {({ lineItems, hasLineItems, currency }) => (
|
|
145
|
+
* <ul data-has-items={hasLineItems}>
|
|
146
|
+
* {lineItems.map((item) => (
|
|
147
|
+
* <Payment.LineItem key={item.sdkLineItem.lineItemId} lineItem={item} currency={currency}>
|
|
148
|
+
* {({ totalPriceAfterTaxAndDiscount }) => (
|
|
149
|
+
* <li><Money money={totalPriceAfterTaxAndDiscount} /></li>
|
|
150
|
+
* )}
|
|
151
|
+
* </Payment.LineItem>
|
|
152
|
+
* ))}
|
|
153
|
+
* </ul>
|
|
154
|
+
* )}
|
|
155
|
+
* </Payment.LineItems>
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
export declare function LineItems(props: LineItemsProps): React.ReactNode;
|
|
159
|
+
/**
|
|
160
|
+
* Props for LineItem render prop component
|
|
161
|
+
*/
|
|
162
|
+
export interface LineItemProps {
|
|
163
|
+
lineItem: CombinedLineItem;
|
|
164
|
+
currency: string;
|
|
165
|
+
children: (data: {
|
|
166
|
+
totalPrice: MoneyData | null;
|
|
167
|
+
subtotal: MoneyData | null;
|
|
168
|
+
tax: MoneyData | null;
|
|
169
|
+
discount: MoneyData | null;
|
|
170
|
+
deposit: MoneyData | null;
|
|
171
|
+
priceText: string | null;
|
|
172
|
+
}) => React.ReactNode;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Core component that provides access to a single line item via render props.
|
|
176
|
+
* Use data-attributes for conditional visibility via CSS.
|
|
177
|
+
*
|
|
178
|
+
* @component
|
|
179
|
+
* @example
|
|
180
|
+
* ```tsx
|
|
181
|
+
* <Payment.LineItem lineItem={item} currency="USD">
|
|
182
|
+
* {({ totalPrice, subtotal, tax, discount, deposit, priceText }) => (
|
|
183
|
+
* <div>
|
|
184
|
+
* <span><Money money={totalPrice} /></span>
|
|
185
|
+
* <span data-has-discount={!!discount?.amount}><Money money={discount} /></span>
|
|
186
|
+
* <span data-has-deposit={!!deposit?.amount}><Money money={deposit} /></span>
|
|
187
|
+
* <span data-has-price-text={!!priceText}>{priceText}</span>
|
|
188
|
+
* </div>
|
|
189
|
+
* )}
|
|
190
|
+
* </Payment.LineItem>
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
export declare function LineItem(props: LineItemProps): React.ReactNode;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { WixServices, useService } from '@wix/services-manager-react';
|
|
3
|
+
import { createServicesMap } from '@wix/services-manager';
|
|
4
|
+
import { PaymentServiceDefinition, PaymentService, } from '../../../services/payment/payment.js';
|
|
5
|
+
import { convertMultiCurrencyPriceToMoneyData } from '../../../utils/money.js';
|
|
6
|
+
/**
|
|
7
|
+
* Internal content component that reads from service
|
|
8
|
+
*/
|
|
9
|
+
function RootContent({ children, }) {
|
|
10
|
+
const paymentService = useService(PaymentServiceDefinition);
|
|
11
|
+
const isLoading = paymentService.isLoadingSignal.get();
|
|
12
|
+
const error = paymentService.errorSignal.get();
|
|
13
|
+
const hasError = !!error;
|
|
14
|
+
if (typeof children === 'function') {
|
|
15
|
+
return children({ isLoading, hasError });
|
|
16
|
+
}
|
|
17
|
+
return children;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Root component that provides payment context.
|
|
21
|
+
* Supports 3 modes:
|
|
22
|
+
* 1. SSR: paymentServiceConfig.paymentDetails - pre-fetched data
|
|
23
|
+
* 2. Lazy loading: slotServices + numberOfParticipants props - calls API on init
|
|
24
|
+
* 3. Reactive: no config/props - reads from BookingService signals
|
|
25
|
+
*
|
|
26
|
+
* @component
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* // SSR mode
|
|
30
|
+
* <Payment.Root paymentServiceConfig={{ paymentDetails }}>
|
|
31
|
+
* <Payment.Summary />
|
|
32
|
+
* </Payment.Root>
|
|
33
|
+
*
|
|
34
|
+
* // Lazy loading mode
|
|
35
|
+
* <Payment.Root slotServices={[...]} numberOfParticipants={2}>
|
|
36
|
+
* {({ isLoading }) => isLoading ? <div>Loading...</div> : <Payment.Summary />}
|
|
37
|
+
* </Payment.Root>
|
|
38
|
+
*
|
|
39
|
+
* // Reactive mode
|
|
40
|
+
* <Payment.Root>
|
|
41
|
+
* <Payment.Summary />
|
|
42
|
+
* </Payment.Root>
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export function Root(props) {
|
|
46
|
+
const { children, paymentServiceConfig = {}, slotServices, numberOfParticipants, } = props;
|
|
47
|
+
// Merge public config with lazy loading props into internal config
|
|
48
|
+
const internalConfig = {
|
|
49
|
+
...paymentServiceConfig,
|
|
50
|
+
slotServices,
|
|
51
|
+
numberOfParticipants,
|
|
52
|
+
};
|
|
53
|
+
return (_jsx(WixServices, { servicesMap: createServicesMap().addService(PaymentServiceDefinition, PaymentService, internalConfig), children: _jsx(RootContent, { children: children }) }));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Core component that provides access to full payment details via render props.
|
|
57
|
+
*
|
|
58
|
+
* @component
|
|
59
|
+
* @example
|
|
60
|
+
* ```tsx
|
|
61
|
+
* <Payment.Payment>
|
|
62
|
+
* {({ paymentDetails, isLoading, error }) => (
|
|
63
|
+
* isLoading ? <div>Loading...</div> : <div>{JSON.stringify(paymentDetails)}</div>
|
|
64
|
+
* )}
|
|
65
|
+
* </Payment.Payment>
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export function Payment(props) {
|
|
69
|
+
const paymentService = useService(PaymentServiceDefinition);
|
|
70
|
+
const paymentDetails = paymentService.paymentDetailsSignal.get();
|
|
71
|
+
const isLoading = paymentService.isLoadingSignal.get();
|
|
72
|
+
const error = paymentService.errorSignal.get();
|
|
73
|
+
return props.children({ paymentDetails, isLoading, error });
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Core component that provides access to payment summary totals via render props.
|
|
77
|
+
* Returns all pricing totals with MoneyData format for the Money component.
|
|
78
|
+
* Use data-attributes for conditional visibility via CSS.
|
|
79
|
+
*
|
|
80
|
+
* @component
|
|
81
|
+
* @example
|
|
82
|
+
* ```tsx
|
|
83
|
+
* <Payment.Summary>
|
|
84
|
+
* {({ subtotal, tax, total, hasTax, hasDiscount, discount, isDeposit, payNow }) => (
|
|
85
|
+
* <div>
|
|
86
|
+
* <div>Subtotal: <Money money={subtotal} /></div>
|
|
87
|
+
* <div data-has-tax={hasTax}>Tax: <Money money={tax} /></div>
|
|
88
|
+
* <div data-has-discount={hasDiscount}>Discount: <Money money={discount} /></div>
|
|
89
|
+
* <div>Total: <Money money={total} /></div>
|
|
90
|
+
* <div data-has-deposit={isDeposit}>Pay Now: <Money money={payNow} /></div>
|
|
91
|
+
* </div>
|
|
92
|
+
* )}
|
|
93
|
+
* </Payment.Summary>
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export function Summary(props) {
|
|
97
|
+
const paymentService = useService(PaymentServiceDefinition);
|
|
98
|
+
const paymentDetails = paymentService.paymentDetailsSignal.get();
|
|
99
|
+
const currency = paymentDetails?.response?.currency || 'USD';
|
|
100
|
+
const priceSummary = paymentDetails?.response?.priceSummary;
|
|
101
|
+
// Always present
|
|
102
|
+
const subtotal = convertMultiCurrencyPriceToMoneyData(priceSummary?.subtotal, currency);
|
|
103
|
+
const total = convertMultiCurrencyPriceToMoneyData(priceSummary?.total, currency);
|
|
104
|
+
// May or may not exist
|
|
105
|
+
const tax = convertMultiCurrencyPriceToMoneyData(priceSummary?.tax, currency);
|
|
106
|
+
const hasTax = tax !== null && tax.amount > 0;
|
|
107
|
+
const discount = convertMultiCurrencyPriceToMoneyData(priceSummary?.discount, currency);
|
|
108
|
+
const hasDiscount = discount !== null && discount.amount > 0;
|
|
109
|
+
// Deposit scenarios only
|
|
110
|
+
const payNow = convertMultiCurrencyPriceToMoneyData(paymentDetails?.response?.payNow?.total, currency);
|
|
111
|
+
const payLater = convertMultiCurrencyPriceToMoneyData(paymentDetails?.response?.payLater?.total, currency);
|
|
112
|
+
const isDeposit = payNow !== null && total !== null && payNow.amount < total.amount;
|
|
113
|
+
return props.children({
|
|
114
|
+
subtotal,
|
|
115
|
+
total,
|
|
116
|
+
currency,
|
|
117
|
+
tax,
|
|
118
|
+
hasTax,
|
|
119
|
+
discount,
|
|
120
|
+
hasDiscount,
|
|
121
|
+
payNow,
|
|
122
|
+
payLater,
|
|
123
|
+
isDeposit,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Core component that provides access to calculated line items via render props.
|
|
128
|
+
* Combines SDK line items with additional computed info.
|
|
129
|
+
* Note: Currently supports single service only (always 1 line item).
|
|
130
|
+
*
|
|
131
|
+
* @component
|
|
132
|
+
* @example
|
|
133
|
+
* ```tsx
|
|
134
|
+
* <Payment.LineItems>
|
|
135
|
+
* {({ lineItems, hasLineItems, currency }) => (
|
|
136
|
+
* <ul data-has-items={hasLineItems}>
|
|
137
|
+
* {lineItems.map((item) => (
|
|
138
|
+
* <Payment.LineItem key={item.sdkLineItem.lineItemId} lineItem={item} currency={currency}>
|
|
139
|
+
* {({ totalPriceAfterTaxAndDiscount }) => (
|
|
140
|
+
* <li><Money money={totalPriceAfterTaxAndDiscount} /></li>
|
|
141
|
+
* )}
|
|
142
|
+
* </Payment.LineItem>
|
|
143
|
+
* ))}
|
|
144
|
+
* </ul>
|
|
145
|
+
* )}
|
|
146
|
+
* </Payment.LineItems>
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
export function LineItems(props) {
|
|
150
|
+
const paymentService = useService(PaymentServiceDefinition);
|
|
151
|
+
const paymentDetails = paymentService.paymentDetailsSignal.get();
|
|
152
|
+
const sdkLineItems = paymentDetails?.response?.calculatedLineItems || [];
|
|
153
|
+
const additionalInfoList = paymentDetails?.additionalInfo?.lineItems || [];
|
|
154
|
+
const currency = paymentDetails?.response?.currency || 'USD';
|
|
155
|
+
// Combine SDK line items with additional info
|
|
156
|
+
const lineItems = sdkLineItems.map((sdkLineItem) => ({
|
|
157
|
+
sdkLineItem,
|
|
158
|
+
additionalInfo: additionalInfoList.find((info) => info.lineItemId === sdkLineItem.lineItemId),
|
|
159
|
+
}));
|
|
160
|
+
const hasLineItems = lineItems.length > 0;
|
|
161
|
+
return props.children({ lineItems, hasLineItems, currency });
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Core component that provides access to a single line item via render props.
|
|
165
|
+
* Use data-attributes for conditional visibility via CSS.
|
|
166
|
+
*
|
|
167
|
+
* @component
|
|
168
|
+
* @example
|
|
169
|
+
* ```tsx
|
|
170
|
+
* <Payment.LineItem lineItem={item} currency="USD">
|
|
171
|
+
* {({ totalPrice, subtotal, tax, discount, deposit, priceText }) => (
|
|
172
|
+
* <div>
|
|
173
|
+
* <span><Money money={totalPrice} /></span>
|
|
174
|
+
* <span data-has-discount={!!discount?.amount}><Money money={discount} /></span>
|
|
175
|
+
* <span data-has-deposit={!!deposit?.amount}><Money money={deposit} /></span>
|
|
176
|
+
* <span data-has-price-text={!!priceText}>{priceText}</span>
|
|
177
|
+
* </div>
|
|
178
|
+
* )}
|
|
179
|
+
* </Payment.LineItem>
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
export function LineItem(props) {
|
|
183
|
+
const { lineItem, currency, children } = props;
|
|
184
|
+
const { sdkLineItem, additionalInfo } = lineItem;
|
|
185
|
+
const pricesBreakdown = sdkLineItem?.pricesBreakdown;
|
|
186
|
+
// Prices (from SDK)
|
|
187
|
+
const totalPrice = convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.totalPriceAfterTax, currency);
|
|
188
|
+
const subtotal = convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.fullPrice, currency);
|
|
189
|
+
const tax = convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.taxDetails?.totalTax, currency);
|
|
190
|
+
const discount = convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.totalDiscount, currency);
|
|
191
|
+
const deposit = convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.depositAmount, currency);
|
|
192
|
+
// Custom price text (for CUSTOM rate type)
|
|
193
|
+
const priceText = additionalInfo?.priceText || null;
|
|
194
|
+
return children({
|
|
195
|
+
totalPrice,
|
|
196
|
+
subtotal,
|
|
197
|
+
tax,
|
|
198
|
+
discount,
|
|
199
|
+
deposit,
|
|
200
|
+
priceText,
|
|
201
|
+
});
|
|
202
|
+
}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -5,3 +5,5 @@ export * as ServiceMedia from './service/ServiceMedia.js';
|
|
|
5
5
|
export * as TimeSlotList from './time-slot-list/TimeSlotList.js';
|
|
6
6
|
export * as ServiceList from './service-list/ServiceList.js';
|
|
7
7
|
export * as Location from './location/Location.js';
|
|
8
|
+
export * as Payment from './payment/Payment.js';
|
|
9
|
+
export * as PaymentLineItem from './payment/PaymentLineItem.js';
|
package/dist/react/index.js
CHANGED
|
@@ -5,3 +5,5 @@ export * as ServiceMedia from './service/ServiceMedia.js';
|
|
|
5
5
|
export * as TimeSlotList from './time-slot-list/TimeSlotList.js';
|
|
6
6
|
export * as ServiceList from './service-list/ServiceList.js';
|
|
7
7
|
export * as Location from './location/Location.js';
|
|
8
|
+
export * as Payment from './payment/Payment.js';
|
|
9
|
+
export * as PaymentLineItem from './payment/PaymentLineItem.js';
|