@tagadapay/plugin-sdk 2.4.3 → 2.4.5
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/dist/react/hooks/useExpressPayment.d.ts +71 -0
- package/dist/react/hooks/useExpressPayment.js +158 -0
- package/dist/react/hooks/useProducts.js +3 -1
- package/dist/react/hooks/useShippingRates.d.ts +6 -23
- package/dist/react/hooks/useShippingRates.js +52 -71
- package/dist/react/index.d.ts +4 -1
- package/dist/react/index.js +4 -1
- package/package.json +1 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { CheckoutData } from './useCheckout';
|
|
3
|
+
export interface Address {
|
|
4
|
+
address1: string;
|
|
5
|
+
address2?: string;
|
|
6
|
+
lastName?: string;
|
|
7
|
+
firstName?: string;
|
|
8
|
+
city?: string;
|
|
9
|
+
state?: string;
|
|
10
|
+
country?: string;
|
|
11
|
+
postal?: string;
|
|
12
|
+
phone?: string;
|
|
13
|
+
email?: string;
|
|
14
|
+
}
|
|
15
|
+
type PaymentMethod = {
|
|
16
|
+
id: string;
|
|
17
|
+
type: string;
|
|
18
|
+
title: string;
|
|
19
|
+
iconUrl: string;
|
|
20
|
+
default: boolean;
|
|
21
|
+
metadata?: Record<string, unknown>;
|
|
22
|
+
};
|
|
23
|
+
type ExpressOrderLineItem = {
|
|
24
|
+
label: string;
|
|
25
|
+
amount: string;
|
|
26
|
+
};
|
|
27
|
+
type ExpressShippingMethod = {
|
|
28
|
+
label: string;
|
|
29
|
+
amount: string;
|
|
30
|
+
identifier: string;
|
|
31
|
+
detail: string;
|
|
32
|
+
};
|
|
33
|
+
export interface ExpressPaymentContextType {
|
|
34
|
+
applePayPaymentMethod?: PaymentMethod;
|
|
35
|
+
googlePayPaymentMethod?: PaymentMethod;
|
|
36
|
+
reComputeOrderSummary: () => Promise<{
|
|
37
|
+
lineItems: ExpressOrderLineItem[];
|
|
38
|
+
total: {
|
|
39
|
+
label: string;
|
|
40
|
+
amount: string;
|
|
41
|
+
};
|
|
42
|
+
shippingMethods: ExpressShippingMethod[];
|
|
43
|
+
} | undefined>;
|
|
44
|
+
loading?: boolean;
|
|
45
|
+
availableExpressPaymentMethodIds: string[];
|
|
46
|
+
setAvailableExpressPaymentMethodIds: (value: string[]) => void;
|
|
47
|
+
shippingMethods: ExpressShippingMethod[];
|
|
48
|
+
lineItems: ExpressOrderLineItem[];
|
|
49
|
+
handleAddExpressId: (id: string) => void;
|
|
50
|
+
updateCheckoutSessionValues: (input: {
|
|
51
|
+
data: {
|
|
52
|
+
shippingAddress: Address;
|
|
53
|
+
billingAddress?: Address | null;
|
|
54
|
+
};
|
|
55
|
+
}) => Promise<void>;
|
|
56
|
+
updateCustomerEmail: (input: {
|
|
57
|
+
data: {
|
|
58
|
+
email: string;
|
|
59
|
+
};
|
|
60
|
+
}) => Promise<void>;
|
|
61
|
+
error: string | null;
|
|
62
|
+
setError: (error: string | null) => void;
|
|
63
|
+
}
|
|
64
|
+
interface ExpressPaymentProviderProps {
|
|
65
|
+
children: ReactNode;
|
|
66
|
+
customerId?: string;
|
|
67
|
+
checkout?: CheckoutData;
|
|
68
|
+
}
|
|
69
|
+
export declare const ExpressPaymentProvider: React.FC<ExpressPaymentProviderProps>;
|
|
70
|
+
export declare const useExpressPayment: () => ExpressPaymentContextType;
|
|
71
|
+
export {};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
|
3
|
+
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
4
|
+
import { useOrderSummary } from './useOrderSummary';
|
|
5
|
+
import { useShippingRates } from './useShippingRates';
|
|
6
|
+
const ExpressPaymentContext = createContext(undefined);
|
|
7
|
+
export const ExpressPaymentProvider = ({ children, customerId, checkout, }) => {
|
|
8
|
+
const { apiService } = useTagadaContext();
|
|
9
|
+
const [availableExpressPaymentMethodIds, setAvailableExpressPaymentMethodIds] = useState([]);
|
|
10
|
+
const [error, setError] = useState(null);
|
|
11
|
+
const [paymentMethods, setPaymentMethods] = useState(null);
|
|
12
|
+
const [isLoadingPaymentMethods, setIsLoadingPaymentMethods] = useState(false);
|
|
13
|
+
const checkoutSessionId = checkout?.checkoutSession.id;
|
|
14
|
+
// Fetch enabled payment methods for this checkout session
|
|
15
|
+
React.useEffect(() => {
|
|
16
|
+
let mounted = true;
|
|
17
|
+
const fetchPaymentMethods = async () => {
|
|
18
|
+
try {
|
|
19
|
+
if (!checkoutSessionId)
|
|
20
|
+
return;
|
|
21
|
+
setIsLoadingPaymentMethods(true);
|
|
22
|
+
const response = await apiService.fetch(`/api/v1/payment-methods?checkoutSessionId=${encodeURIComponent(checkoutSessionId)}`);
|
|
23
|
+
if (mounted)
|
|
24
|
+
setPaymentMethods(response);
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
if (mounted)
|
|
28
|
+
setPaymentMethods([]);
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
if (mounted)
|
|
32
|
+
setIsLoadingPaymentMethods(false);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
if (checkout)
|
|
36
|
+
void fetchPaymentMethods();
|
|
37
|
+
return () => {
|
|
38
|
+
mounted = false;
|
|
39
|
+
};
|
|
40
|
+
}, [apiService, checkoutSessionId]);
|
|
41
|
+
const handleAddExpressId = (id) => {
|
|
42
|
+
setAvailableExpressPaymentMethodIds((prev) => (prev.includes(id) ? prev : [...prev, id]));
|
|
43
|
+
};
|
|
44
|
+
// Base data hooks
|
|
45
|
+
const { orderSummary, isLoading: isLoadingOrderSummary, isRefetching: isRefetchingOrderSummary, refetch: refetchOrderSummary, } = useOrderSummary({ sessionId: checkoutSessionId });
|
|
46
|
+
const { shippingRates, refetch: refetchRates } = useShippingRates({ checkout });
|
|
47
|
+
const minorUnitsToCurrencyString = (amountMinor, currency) => {
|
|
48
|
+
if (!amountMinor || !currency)
|
|
49
|
+
return '0.00';
|
|
50
|
+
return (amountMinor / 100).toFixed(2);
|
|
51
|
+
};
|
|
52
|
+
const shippingMethods = useMemo(() => (shippingRates || []).map((rate) => ({
|
|
53
|
+
label: rate.shippingRateName,
|
|
54
|
+
amount: minorUnitsToCurrencyString(rate.amount, rate.currency),
|
|
55
|
+
identifier: rate.id,
|
|
56
|
+
detail: rate.description || '',
|
|
57
|
+
})), [shippingRates]);
|
|
58
|
+
const lineItems = useMemo(() => [
|
|
59
|
+
{
|
|
60
|
+
label: 'Subtotal',
|
|
61
|
+
amount: minorUnitsToCurrencyString(orderSummary?.subtotalAdjustedAmount, orderSummary?.currency),
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
label: 'Shipping',
|
|
65
|
+
amount: minorUnitsToCurrencyString(orderSummary?.shippingCost ?? 0, orderSummary?.currency),
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
label: 'Tax',
|
|
69
|
+
amount: minorUnitsToCurrencyString(orderSummary?.totalTaxAmount, orderSummary?.currency),
|
|
70
|
+
},
|
|
71
|
+
], [
|
|
72
|
+
orderSummary?.subtotalAdjustedAmount,
|
|
73
|
+
orderSummary?.shippingCost,
|
|
74
|
+
orderSummary?.totalTaxAmount,
|
|
75
|
+
orderSummary?.currency,
|
|
76
|
+
]);
|
|
77
|
+
const reComputeOrderSummary = useCallback(async () => {
|
|
78
|
+
try {
|
|
79
|
+
await refetchOrderSummary();
|
|
80
|
+
await refetchRates();
|
|
81
|
+
if (!orderSummary || !shippingRates)
|
|
82
|
+
return;
|
|
83
|
+
const recomputedLineItems = [
|
|
84
|
+
{
|
|
85
|
+
label: 'Subtotal',
|
|
86
|
+
amount: minorUnitsToCurrencyString(orderSummary.subtotalAdjustedAmount, orderSummary.currency),
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
label: 'Shipping',
|
|
90
|
+
amount: minorUnitsToCurrencyString(orderSummary.shippingCost ?? 0, orderSummary.currency),
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
label: 'Tax',
|
|
94
|
+
amount: minorUnitsToCurrencyString(orderSummary.totalTaxAmount, orderSummary.currency),
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
const total = {
|
|
98
|
+
label: 'Order Total',
|
|
99
|
+
amount: minorUnitsToCurrencyString(orderSummary.totalAdjustedAmount, orderSummary.currency),
|
|
100
|
+
};
|
|
101
|
+
const recomputedShippingMethods = (shippingRates || []).map((rate) => ({
|
|
102
|
+
label: rate.shippingRateName,
|
|
103
|
+
amount: minorUnitsToCurrencyString(rate.amount, rate.currency),
|
|
104
|
+
identifier: rate.id,
|
|
105
|
+
detail: rate.description || '',
|
|
106
|
+
}));
|
|
107
|
+
return {
|
|
108
|
+
lineItems: recomputedLineItems,
|
|
109
|
+
total,
|
|
110
|
+
shippingMethods: recomputedShippingMethods,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
}, [orderSummary, shippingRates, refetchOrderSummary, refetchRates]);
|
|
117
|
+
const updateCheckoutSessionValues = useCallback(async (input) => {
|
|
118
|
+
await apiService.fetch(`/api/v1/checkout-sessions/${checkoutSessionId}/address`, {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
body: input,
|
|
121
|
+
});
|
|
122
|
+
}, [apiService, checkoutSessionId]);
|
|
123
|
+
const updateCustomerEmail = useCallback(async (input) => {
|
|
124
|
+
if (!customerId)
|
|
125
|
+
return;
|
|
126
|
+
await apiService.fetch(`/api/v1/customers/${customerId}`, {
|
|
127
|
+
method: 'POST',
|
|
128
|
+
body: input,
|
|
129
|
+
});
|
|
130
|
+
}, [apiService, customerId]);
|
|
131
|
+
const enabledApplePayPaymentMethod = useMemo(() => paymentMethods?.find((p) => p.type === 'apple_pay'), [paymentMethods]);
|
|
132
|
+
const enabledGooglePayPaymentMethod = useMemo(() => paymentMethods?.find((p) => p.type === 'google_pay'), [paymentMethods]);
|
|
133
|
+
const loading = !paymentMethods || isLoadingPaymentMethods || isLoadingOrderSummary;
|
|
134
|
+
const contextValue = {
|
|
135
|
+
availableExpressPaymentMethodIds,
|
|
136
|
+
setAvailableExpressPaymentMethodIds,
|
|
137
|
+
applePayPaymentMethod: enabledApplePayPaymentMethod,
|
|
138
|
+
googlePayPaymentMethod: enabledGooglePayPaymentMethod,
|
|
139
|
+
shippingMethods,
|
|
140
|
+
lineItems,
|
|
141
|
+
reComputeOrderSummary,
|
|
142
|
+
loading,
|
|
143
|
+
handleAddExpressId,
|
|
144
|
+
updateCheckoutSessionValues,
|
|
145
|
+
updateCustomerEmail,
|
|
146
|
+
error,
|
|
147
|
+
setError,
|
|
148
|
+
};
|
|
149
|
+
const hasAnyEnabled = Boolean(enabledApplePayPaymentMethod || enabledGooglePayPaymentMethod);
|
|
150
|
+
return (_jsx(ExpressPaymentContext.Provider, { value: contextValue, children: hasAnyEnabled ? _jsx(_Fragment, { children: children }) : _jsx(_Fragment, {}) }));
|
|
151
|
+
};
|
|
152
|
+
export const useExpressPayment = () => {
|
|
153
|
+
const context = useContext(ExpressPaymentContext);
|
|
154
|
+
if (context === undefined) {
|
|
155
|
+
throw new Error('useExpressPayment must be used within an ExpressPaymentProvider');
|
|
156
|
+
}
|
|
157
|
+
return context;
|
|
158
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
3
3
|
import { usePluginConfig } from './usePluginConfig';
|
|
4
4
|
export function useProducts(options = {}) {
|
|
@@ -42,6 +42,7 @@ export function useProducts(options = {}) {
|
|
|
42
42
|
productId,
|
|
43
43
|
storeId,
|
|
44
44
|
},
|
|
45
|
+
skipAuth: true,
|
|
45
46
|
});
|
|
46
47
|
});
|
|
47
48
|
const fetchedProducts = await Promise.all(fetchPromises);
|
|
@@ -56,6 +57,7 @@ export function useProducts(options = {}) {
|
|
|
56
57
|
includeVariants,
|
|
57
58
|
includePrices,
|
|
58
59
|
},
|
|
60
|
+
skipAuth: true,
|
|
59
61
|
});
|
|
60
62
|
setProducts(Array.isArray(data) ? data : (data.items ?? []));
|
|
61
63
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CheckoutData } from './useCheckout';
|
|
2
2
|
export interface ShippingRate {
|
|
3
3
|
id: string;
|
|
4
4
|
shippingRateName: string;
|
|
@@ -13,36 +13,19 @@ export interface ShippingRate {
|
|
|
13
13
|
}
|
|
14
14
|
export interface ShippingRatesResponse {
|
|
15
15
|
rates: ShippingRate[];
|
|
16
|
-
pickupPoint?: PickupPoint;
|
|
17
16
|
forceCheckoutSessionRefetch?: boolean;
|
|
18
17
|
}
|
|
19
18
|
export interface UseShippingRatesOptions {
|
|
20
19
|
onSuccess?: () => void;
|
|
21
|
-
|
|
22
|
-
checkoutSessionId?: string;
|
|
20
|
+
checkout?: CheckoutData;
|
|
23
21
|
}
|
|
24
22
|
export interface UseShippingRatesResult {
|
|
25
23
|
shippingRates: ShippingRate[] | undefined;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
isPickupPointSelected: boolean;
|
|
29
|
-
selectedRateId: string | null;
|
|
30
|
-
setSelectedRateId: (rateId: string | null) => void;
|
|
31
|
-
setShippingRate: (data: {
|
|
32
|
-
shippingRateId: string;
|
|
33
|
-
}) => Promise<void>;
|
|
34
|
-
setShippingRateAsync: (data: {
|
|
35
|
-
shippingRateId: string;
|
|
36
|
-
}) => Promise<void>;
|
|
37
|
-
isPending: boolean;
|
|
38
|
-
isFetching: boolean;
|
|
24
|
+
selectedRate: ShippingRate | undefined;
|
|
25
|
+
selectRate: (rateId: string) => Promise<void> | undefined;
|
|
39
26
|
isLoading: boolean;
|
|
40
27
|
error: Error | null;
|
|
28
|
+
clearError: () => void;
|
|
41
29
|
refetch: () => Promise<void>;
|
|
42
|
-
setPickupPoint: (data: {
|
|
43
|
-
checkoutSessionId: string;
|
|
44
|
-
data: PickupPoint;
|
|
45
|
-
}) => Promise<void>;
|
|
46
|
-
selectedRate: ShippingRate | undefined;
|
|
47
30
|
}
|
|
48
|
-
export declare const useShippingRates: ({ onSuccess,
|
|
31
|
+
export declare const useShippingRates: ({ onSuccess, checkout }?: UseShippingRatesOptions) => UseShippingRatesResult;
|
|
@@ -26,31 +26,34 @@ function useDebounce(callback, delay) {
|
|
|
26
26
|
}, delay);
|
|
27
27
|
}), [callback, delay]);
|
|
28
28
|
}
|
|
29
|
-
export const useShippingRates = ({ onSuccess,
|
|
29
|
+
export const useShippingRates = ({ onSuccess, checkout } = {}) => {
|
|
30
30
|
const { apiService } = useTagadaContext();
|
|
31
31
|
const [selectedRateId, setSelectedRateId] = useState(null);
|
|
32
|
+
// At very first init, set the shipping rate from the checkout if available
|
|
32
33
|
const [shippingRates, setShippingRates] = useState();
|
|
33
|
-
const [forceCheckoutSessionRefetch, setForceCheckoutSessionRefetch] = useState();
|
|
34
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
35
34
|
const [isFetching, setIsFetching] = useState(false);
|
|
36
35
|
const [isPending, setIsPending] = useState(false);
|
|
37
36
|
const [error, setError] = useState(null);
|
|
38
37
|
const isMountedRef = useRef(true);
|
|
39
38
|
const isUpdatingRef = useRef(false);
|
|
40
|
-
|
|
41
|
-
const
|
|
39
|
+
const previousSessionIdRef = useRef(undefined);
|
|
40
|
+
const isFetchingRef = useRef(false);
|
|
41
|
+
const hasSyncedSelectionFromCheckoutRef = useRef(false);
|
|
42
|
+
const sessionId = checkout?.checkoutSession.id;
|
|
42
43
|
// Track mounted state
|
|
43
44
|
useEffect(() => {
|
|
44
45
|
isMountedRef.current = true;
|
|
45
46
|
return () => {
|
|
46
47
|
isMountedRef.current = false;
|
|
48
|
+
isFetchingRef.current = false;
|
|
47
49
|
};
|
|
48
50
|
}, []);
|
|
49
51
|
// Fetch shipping rates
|
|
50
52
|
const fetchShippingRates = useCallback(async () => {
|
|
51
|
-
if (!sessionId || !apiService)
|
|
53
|
+
if (!sessionId || !apiService || isFetchingRef.current)
|
|
52
54
|
return;
|
|
53
55
|
try {
|
|
56
|
+
isFetchingRef.current = true;
|
|
54
57
|
setIsFetching(true);
|
|
55
58
|
setError(null);
|
|
56
59
|
const response = await apiService.fetch(`/api/v1/checkout-sessions/${sessionId}/shipping-rates`, {
|
|
@@ -61,7 +64,6 @@ export const useShippingRates = ({ onSuccess, disabled, checkoutSessionId } = {}
|
|
|
61
64
|
});
|
|
62
65
|
if (isMountedRef.current) {
|
|
63
66
|
setShippingRates(response.rates);
|
|
64
|
-
setForceCheckoutSessionRefetch(response.forceCheckoutSessionRefetch);
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
69
|
catch (err) {
|
|
@@ -71,17 +73,17 @@ export const useShippingRates = ({ onSuccess, disabled, checkoutSessionId } = {}
|
|
|
71
73
|
}
|
|
72
74
|
finally {
|
|
73
75
|
if (isMountedRef.current) {
|
|
76
|
+
isFetchingRef.current = false;
|
|
74
77
|
setIsFetching(false);
|
|
75
78
|
}
|
|
76
79
|
}
|
|
77
80
|
}, [sessionId, apiService]);
|
|
78
|
-
// Initial fetch of shipping rates when sessionId is available
|
|
79
81
|
useEffect(() => {
|
|
80
|
-
if (sessionId &&
|
|
82
|
+
if (sessionId && isMountedRef.current && sessionId !== previousSessionIdRef.current) {
|
|
83
|
+
previousSessionIdRef.current = sessionId;
|
|
81
84
|
fetchShippingRates();
|
|
82
85
|
}
|
|
83
|
-
}, [sessionId
|
|
84
|
-
// Debounced success handler to prevent query invalidation storms
|
|
86
|
+
}, [sessionId]);
|
|
85
87
|
const debouncedOnSuccess = useDebounce(useCallback(async () => {
|
|
86
88
|
if (!isMountedRef.current || isUpdatingRef.current)
|
|
87
89
|
return;
|
|
@@ -101,14 +103,15 @@ export const useShippingRates = ({ onSuccess, disabled, checkoutSessionId } = {}
|
|
|
101
103
|
isUpdatingRef.current = false;
|
|
102
104
|
}
|
|
103
105
|
}
|
|
104
|
-
}, [
|
|
106
|
+
}, [onSuccess]), // Removed fetchShippingRates from dependencies
|
|
107
|
+
300);
|
|
105
108
|
// Set shipping rate mutation
|
|
106
109
|
const setShippingRate = useCallback(async (data) => {
|
|
107
110
|
if (!sessionId || !apiService)
|
|
108
111
|
return;
|
|
109
112
|
try {
|
|
110
113
|
setIsPending(true);
|
|
111
|
-
|
|
114
|
+
clearError();
|
|
112
115
|
await apiService.fetch(`/api/v1/checkout-sessions/${sessionId}/shipping-rate`, {
|
|
113
116
|
method: 'POST',
|
|
114
117
|
body: data,
|
|
@@ -129,76 +132,54 @@ export const useShippingRates = ({ onSuccess, disabled, checkoutSessionId } = {}
|
|
|
129
132
|
}
|
|
130
133
|
}
|
|
131
134
|
}, [sessionId, apiService, debouncedOnSuccess, selectedRateId]);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const setPickupPoint = useCallback(async (data) => {
|
|
137
|
-
if (!apiService)
|
|
138
|
-
return;
|
|
139
|
-
try {
|
|
140
|
-
setIsPending(true);
|
|
141
|
-
setError(null);
|
|
142
|
-
await apiService.fetch(`/api/v1/checkout-sessions/${data.checkoutSessionId}/pickup-point`, {
|
|
143
|
-
method: 'POST',
|
|
144
|
-
body: { data: data.data },
|
|
145
|
-
});
|
|
146
|
-
if (isMountedRef.current) {
|
|
147
|
-
await debouncedOnSuccess();
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
catch (err) {
|
|
151
|
-
if (isMountedRef.current) {
|
|
152
|
-
setSelectedRateId(selectedRateId); // Revert to previous selection
|
|
153
|
-
setError(err instanceof Error ? err : new Error('Failed to update pickup point'));
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
finally {
|
|
157
|
-
if (isMountedRef.current) {
|
|
158
|
-
setIsPending(false);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}, [apiService, debouncedOnSuccess, selectedRateId]);
|
|
135
|
+
// Stable refetch function that can be called externally
|
|
136
|
+
const refetch = useCallback(() => {
|
|
137
|
+
return fetchShippingRates();
|
|
138
|
+
}, [fetchShippingRates]);
|
|
162
139
|
const handleSelectRate = useCallback((rateId) => {
|
|
163
|
-
if (isPending ||
|
|
140
|
+
if (isPending || !isMountedRef.current)
|
|
164
141
|
return;
|
|
165
|
-
const selectedRate = shippingRates?.find((rate) => rate.id === rateId);
|
|
166
142
|
if (isMountedRef.current) {
|
|
167
143
|
setSelectedRateId(rateId);
|
|
168
|
-
setShippingRate({ shippingRateId: rateId });
|
|
144
|
+
return setShippingRate({ shippingRateId: rateId });
|
|
145
|
+
}
|
|
146
|
+
}, [setShippingRate, isPending]);
|
|
147
|
+
const findCheapestShippingRate = useCallback(() => {
|
|
148
|
+
if (!shippingRates || shippingRates.length === 0)
|
|
149
|
+
return;
|
|
150
|
+
if (selectedRateId) {
|
|
151
|
+
const exists = shippingRates.some((r) => r.id === selectedRateId);
|
|
152
|
+
if (!exists) {
|
|
153
|
+
// Find the cheapest shipping rate and select it
|
|
154
|
+
const cheapest = shippingRates.reduce((min, rate) => {
|
|
155
|
+
return rate.amount < min.amount ? rate : min;
|
|
156
|
+
}, shippingRates[0]);
|
|
157
|
+
handleSelectRate(cheapest.id);
|
|
158
|
+
}
|
|
169
159
|
}
|
|
170
|
-
}, [
|
|
171
|
-
//
|
|
160
|
+
}, [shippingRates, selectedRateId, setShippingRate]);
|
|
161
|
+
// Sync selectedRateId from checkout only once when checkout data becomes available
|
|
172
162
|
useEffect(() => {
|
|
173
|
-
if (!
|
|
163
|
+
if (!hasSyncedSelectionFromCheckoutRef.current && checkout?.checkoutSession.shippingRate?.id) {
|
|
164
|
+
setSelectedRateId(checkout.checkoutSession.shippingRate.id);
|
|
165
|
+
hasSyncedSelectionFromCheckoutRef.current = true;
|
|
174
166
|
return;
|
|
175
|
-
const nonPickupRates = shippingRates.filter((rate) => !rate.isPickupPoint);
|
|
176
|
-
if (nonPickupRates.length === 1) {
|
|
177
|
-
handleSelectRate(nonPickupRates[0].id);
|
|
178
167
|
}
|
|
179
|
-
else if (
|
|
180
|
-
|
|
181
|
-
handleSelectRate(cheapestRate.id);
|
|
168
|
+
else if (shippingRates && shippingRates.length > 0 && !shippingRates.find((rate) => rate.id === selectedRateId)) {
|
|
169
|
+
findCheapestShippingRate();
|
|
182
170
|
}
|
|
183
|
-
}, [
|
|
184
|
-
// Get the currently selected shipping rate
|
|
171
|
+
}, [checkout?.checkoutSession.shippingRate?.id, shippingRates]);
|
|
185
172
|
const selectedRate = shippingRates?.find((rate) => rate.id === selectedRateId);
|
|
186
|
-
const
|
|
173
|
+
const clearError = useCallback(() => {
|
|
174
|
+
setError(null);
|
|
175
|
+
}, []);
|
|
187
176
|
return {
|
|
188
177
|
shippingRates,
|
|
189
|
-
forceCheckoutSessionRefetch,
|
|
190
|
-
handleSelectRate,
|
|
191
|
-
isPickupPointSelected,
|
|
192
|
-
selectedRateId,
|
|
193
|
-
setSelectedRateId,
|
|
194
|
-
setShippingRate,
|
|
195
|
-
setShippingRateAsync,
|
|
196
|
-
isPending,
|
|
197
|
-
isFetching,
|
|
198
|
-
isLoading,
|
|
199
|
-
error,
|
|
200
|
-
refetch: fetchShippingRates,
|
|
201
|
-
setPickupPoint,
|
|
202
178
|
selectedRate,
|
|
179
|
+
selectRate: handleSelectRate,
|
|
180
|
+
isLoading: isFetching || isPending,
|
|
181
|
+
error,
|
|
182
|
+
clearError,
|
|
183
|
+
refetch,
|
|
203
184
|
};
|
|
204
185
|
};
|
package/dist/react/index.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ export { useOrderBump } from './hooks/useOrderBump';
|
|
|
16
16
|
export { usePostPurchases } from './hooks/usePostPurchases';
|
|
17
17
|
export { useProducts } from './hooks/useProducts';
|
|
18
18
|
export { useSession } from './hooks/useSession';
|
|
19
|
+
export { useShippingRates } from './hooks/useShippingRates';
|
|
20
|
+
export type { UseShippingRatesOptions, UseShippingRatesResult } from './hooks/useShippingRates';
|
|
19
21
|
export { useTranslations } from './hooks/useTranslations';
|
|
20
22
|
export { useVipOffers } from './hooks/useVipOffers';
|
|
21
23
|
export { useTagadaContext } from './providers/TagadaProvider';
|
|
@@ -31,6 +33,7 @@ export { usePaymentPolling } from './hooks/usePaymentPolling';
|
|
|
31
33
|
export { useThreeds } from './hooks/useThreeds';
|
|
32
34
|
export { useThreedsModal } from './hooks/useThreedsModal';
|
|
33
35
|
export { useApplePay } from './hooks/useApplePay';
|
|
36
|
+
export { ExpressPaymentProvider, useExpressPayment } from './hooks/useExpressPayment';
|
|
34
37
|
export type { AuthState, Currency, Customer, Environment, EnvironmentConfig, Locale, Order, OrderAddress, OrderItem, OrderSummary, PickupPoint, Session, Store } from './types';
|
|
35
38
|
export type { CheckoutData, CheckoutInitParams, CheckoutLineItem, CheckoutSession, CheckoutSessionPreview, Promotion, UseCheckoutOptions, UseCheckoutResult } from './hooks/useCheckout';
|
|
36
39
|
export type { Discount, DiscountCodeValidation, UseDiscountsOptions, UseDiscountsResult } from './hooks/useDiscounts';
|
|
@@ -41,5 +44,5 @@ export type { Payment, PaymentPollingHook, PollingOptions } from './hooks/usePay
|
|
|
41
44
|
export type { PaymentInstrument, ThreedsChallenge, ThreedsHook, ThreedsOptions, ThreedsProvider, ThreedsSession } from './hooks/useThreeds';
|
|
42
45
|
export type { ApplePayToken, CardPaymentMethod, PaymentHook, PaymentInstrumentResponse, PaymentOptions, PaymentResponse } from './hooks/usePayment';
|
|
43
46
|
export type { ApplePayAddress, ApplePayConfig, ApplePayLineItem, ApplePayPaymentAuthorizedEvent, ApplePayPaymentRequest, ApplePayPaymentToken, ApplePayValidateMerchantEvent, BasisTheorySessionRequest, BasisTheoryTokenizeRequest, PayToken, UseApplePayOptions, UseApplePayResult } from './types/apple-pay';
|
|
44
|
-
export {
|
|
47
|
+
export { ApplePayButton, Button } from './components';
|
|
45
48
|
export { convertCurrency, formatMoney, formatMoneyWithoutSymbol, formatSimpleMoney, getCurrencyInfo, minorUnitsToMajorUnits, moneyStringOrNumberToMinorUnits } from './utils/money';
|
package/dist/react/index.js
CHANGED
|
@@ -19,6 +19,7 @@ export { useOrderBump } from './hooks/useOrderBump';
|
|
|
19
19
|
export { usePostPurchases } from './hooks/usePostPurchases';
|
|
20
20
|
export { useProducts } from './hooks/useProducts';
|
|
21
21
|
export { useSession } from './hooks/useSession';
|
|
22
|
+
export { useShippingRates } from './hooks/useShippingRates';
|
|
22
23
|
export { useTranslations } from './hooks/useTranslations';
|
|
23
24
|
export { useVipOffers } from './hooks/useVipOffers';
|
|
24
25
|
export { useTagadaContext } from './providers/TagadaProvider';
|
|
@@ -35,7 +36,9 @@ export { useThreeds } from './hooks/useThreeds';
|
|
|
35
36
|
export { useThreedsModal } from './hooks/useThreedsModal';
|
|
36
37
|
// Apple Pay hooks exports
|
|
37
38
|
export { useApplePay } from './hooks/useApplePay';
|
|
39
|
+
// Express Payment context exports
|
|
40
|
+
export { ExpressPaymentProvider, useExpressPayment } from './hooks/useExpressPayment';
|
|
38
41
|
// Component exports
|
|
39
|
-
export {
|
|
42
|
+
export { ApplePayButton, Button } from './components';
|
|
40
43
|
// Utility exports
|
|
41
44
|
export { convertCurrency, formatMoney, formatMoneyWithoutSymbol, formatSimpleMoney, getCurrencyInfo, minorUnitsToMajorUnits, moneyStringOrNumberToMinorUnits } from './utils/money';
|