@blocklet/payment-react-headless 1.26.1 → 1.26.3
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/es/checkout/context/CheckoutProvider.js +26 -10
- package/es/checkout/context/SessionContext.d.ts +3 -1
- package/es/checkout/core/crossSell.d.ts +3 -2
- package/es/checkout/core/crossSell.js +24 -8
- package/es/checkout/core/customerForm.js +0 -6
- package/es/checkout/core/lineItems.d.ts +6 -5
- package/es/checkout/core/lineItems.js +48 -17
- package/es/checkout/core/paymentMethod.js +15 -7
- package/es/checkout/core/pricing.js +1 -2
- package/es/checkout/core/promotion.d.ts +1 -1
- package/es/checkout/core/promotion.js +2 -1
- package/es/checkout/hooks/useBillingInterval.js +8 -5
- package/es/checkout/hooks/useCheckout.js +17 -11
- package/es/checkout/hooks/useCheckoutSession.d.ts +2 -2
- package/es/checkout/hooks/useCheckoutSession.js +7 -0
- package/es/checkout/hooks/useCheckoutStatus.d.ts +1 -1
- package/es/checkout/hooks/useCrossSell.js +11 -3
- package/es/checkout/hooks/useCustomerForm.d.ts +4 -0
- package/es/checkout/hooks/useCustomerForm.js +6 -0
- package/es/checkout/hooks/useLineItems.js +36 -9
- package/es/checkout/hooks/usePaymentMethod.d.ts +1 -1
- package/es/checkout/hooks/usePaymentMethod.js +8 -4
- package/es/checkout/hooks/usePricing.d.ts +1 -1
- package/es/checkout/hooks/usePricing.js +4 -4
- package/es/checkout/hooks/useSubmit.js +6 -1
- package/es/checkout/hooks/useUpsell.js +3 -3
- package/es/checkout/types.d.ts +5 -3
- package/lib/checkout/context/CheckoutProvider.js +21 -12
- package/lib/checkout/context/SessionContext.d.ts +3 -1
- package/lib/checkout/core/crossSell.d.ts +3 -2
- package/lib/checkout/core/crossSell.js +36 -8
- package/lib/checkout/core/customerForm.js +0 -5
- package/lib/checkout/core/lineItems.d.ts +6 -5
- package/lib/checkout/core/lineItems.js +72 -18
- package/lib/checkout/core/paymentMethod.js +14 -8
- package/lib/checkout/core/pricing.js +1 -2
- package/lib/checkout/core/promotion.d.ts +1 -1
- package/lib/checkout/core/promotion.js +4 -1
- package/lib/checkout/hooks/useBillingInterval.js +10 -5
- package/lib/checkout/hooks/useCheckout.js +16 -11
- package/lib/checkout/hooks/useCheckoutSession.d.ts +2 -2
- package/lib/checkout/hooks/useCheckoutSession.js +7 -0
- package/lib/checkout/hooks/useCheckoutStatus.d.ts +1 -1
- package/lib/checkout/hooks/useCrossSell.js +5 -3
- package/lib/checkout/hooks/useCustomerForm.d.ts +4 -0
- package/lib/checkout/hooks/useCustomerForm.js +6 -0
- package/lib/checkout/hooks/useLineItems.js +10 -8
- package/lib/checkout/hooks/usePaymentMethod.d.ts +1 -1
- package/lib/checkout/hooks/usePaymentMethod.js +14 -4
- package/lib/checkout/hooks/usePricing.d.ts +1 -1
- package/lib/checkout/hooks/usePricing.js +4 -4
- package/lib/checkout/hooks/useSubmit.js +8 -1
- package/lib/checkout/hooks/useUpsell.js +5 -3
- package/lib/checkout/types.d.ts +5 -3
- package/package.json +3 -3
- package/src/checkout/context/CheckoutProvider.tsx +38 -17
- package/src/checkout/context/SessionContext.ts +3 -1
- package/src/checkout/core/crossSell.ts +29 -8
- package/src/checkout/core/customerForm.ts +0 -6
- package/src/checkout/core/lineItems.ts +62 -18
- package/src/checkout/core/paymentMethod.ts +24 -7
- package/src/checkout/core/pricing.ts +1 -2
- package/src/checkout/core/promotion.ts +6 -2
- package/src/checkout/hooks/useBillingInterval.ts +8 -5
- package/src/checkout/hooks/useCheckout.ts +20 -12
- package/src/checkout/hooks/useCheckoutSession.ts +12 -3
- package/src/checkout/hooks/useCheckoutStatus.ts +1 -1
- package/src/checkout/hooks/useCrossSell.ts +11 -3
- package/src/checkout/hooks/useCustomerForm.ts +12 -0
- package/src/checkout/hooks/useLineItems.ts +42 -9
- package/src/checkout/hooks/usePaymentMethod.ts +13 -5
- package/src/checkout/hooks/usePricing.ts +5 -4
- package/src/checkout/hooks/useSubmit.ts +13 -1
- package/src/checkout/hooks/useUpsell.ts +3 -3
- package/src/checkout/types.ts +4 -2
|
@@ -17,7 +17,7 @@ export interface UseCrossSellReturn {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export function useCrossSell(): UseCrossSellReturn {
|
|
20
|
-
const { items, session, effectiveSessionId, refresh } = useSessionContext();
|
|
20
|
+
const { items, session, effectiveSessionId, refresh, sessionData, setSessionData } = useSessionContext();
|
|
21
21
|
const { currency } = usePaymentMethodContext();
|
|
22
22
|
const currencyId = currency?.id || null;
|
|
23
23
|
|
|
@@ -61,7 +61,15 @@ export function useCrossSell(): UseCrossSellReturn {
|
|
|
61
61
|
const crossSellItemPrice = getCrossSellItem(items);
|
|
62
62
|
if (!crossSellItemPrice) return;
|
|
63
63
|
try {
|
|
64
|
-
await addCrossSellItem(
|
|
64
|
+
await addCrossSellItem(
|
|
65
|
+
effectiveSessionId,
|
|
66
|
+
crossSellItemPrice.id,
|
|
67
|
+
session,
|
|
68
|
+
currencyId,
|
|
69
|
+
refresh,
|
|
70
|
+
sessionData,
|
|
71
|
+
setSessionData
|
|
72
|
+
);
|
|
65
73
|
} catch (err: unknown) {
|
|
66
74
|
console.error('Failed to add cross-sell:', getErrorMessage(err));
|
|
67
75
|
}
|
|
@@ -70,7 +78,7 @@ export function useCrossSell(): UseCrossSellReturn {
|
|
|
70
78
|
const remove = useMemoizedFn(async () => {
|
|
71
79
|
if (session?.status === 'complete') return;
|
|
72
80
|
try {
|
|
73
|
-
await removeCrossSellItem(effectiveSessionId, session, currencyId, refresh);
|
|
81
|
+
await removeCrossSellItem(effectiveSessionId, session, currencyId, refresh, sessionData, setSessionData);
|
|
74
82
|
} catch (err: unknown) {
|
|
75
83
|
console.error('Failed to remove cross-sell:', getErrorMessage(err));
|
|
76
84
|
}
|
|
@@ -14,7 +14,11 @@ export interface UseCustomerFormReturn {
|
|
|
14
14
|
errors: Partial<Record<string, string>>;
|
|
15
15
|
touched: Record<string, boolean>;
|
|
16
16
|
validate: () => Promise<boolean>;
|
|
17
|
+
/** Silent validation — returns valid/invalid without setting error messages on the form */
|
|
18
|
+
checkValid: () => Promise<boolean>;
|
|
17
19
|
validateField: (field: string) => Promise<void>;
|
|
20
|
+
/** Whether customer info has been prefetched from backend */
|
|
21
|
+
prefetched: boolean;
|
|
18
22
|
/** Re-fetch customer info from backend and update form values (e.g. after login) */
|
|
19
23
|
refetchCustomer: () => Promise<void>;
|
|
20
24
|
}
|
|
@@ -102,6 +106,12 @@ export function useCustomerForm(
|
|
|
102
106
|
return result.valid;
|
|
103
107
|
});
|
|
104
108
|
|
|
109
|
+
// Silent validation — check validity without setting error messages on the form
|
|
110
|
+
const checkValid = useMemoizedFn(async () => {
|
|
111
|
+
const result = await validateForm(values, getValidateOptions());
|
|
112
|
+
return result.valid;
|
|
113
|
+
});
|
|
114
|
+
|
|
105
115
|
// Single-field blur validation — matches V1's trigger(fieldName)
|
|
106
116
|
const validateField = useMemoizedFn(async (field: string) => {
|
|
107
117
|
const result = await validateForm(values, getValidateOptions());
|
|
@@ -150,7 +160,9 @@ export function useCustomerForm(
|
|
|
150
160
|
errors,
|
|
151
161
|
touched,
|
|
152
162
|
validate,
|
|
163
|
+
checkValid,
|
|
153
164
|
validateField,
|
|
165
|
+
prefetched,
|
|
154
166
|
refetchCustomer,
|
|
155
167
|
};
|
|
156
168
|
}
|
|
@@ -39,14 +39,21 @@ export interface UseLineItemsReturn {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export function useLineItems(): UseLineItemsReturn {
|
|
42
|
-
const { items, session, effectiveSessionId, isDonation, refresh } = useSessionContext();
|
|
42
|
+
const { items, session, effectiveSessionId, isDonation, refresh, sessionData, setSessionData } = useSessionContext();
|
|
43
43
|
const { currency } = usePaymentMethodContext();
|
|
44
44
|
const currencyId = currency?.id || null;
|
|
45
45
|
|
|
46
46
|
// Default quantity from URL params: ?qty=5 or ?qty_price_xxx=10 (matching original product-item.tsx)
|
|
47
47
|
const defaultQtyApplied = useRef(false);
|
|
48
48
|
useEffect(() => {
|
|
49
|
-
if (
|
|
49
|
+
if (
|
|
50
|
+
defaultQtyApplied.current ||
|
|
51
|
+
!effectiveSessionId ||
|
|
52
|
+
!items.length ||
|
|
53
|
+
!currencyId ||
|
|
54
|
+
session?.status === 'complete'
|
|
55
|
+
)
|
|
56
|
+
return;
|
|
50
57
|
try {
|
|
51
58
|
const params = new URLSearchParams(window.location.search);
|
|
52
59
|
for (const item of items) {
|
|
@@ -55,7 +62,16 @@ export function useLineItems(): UseLineItemsReturn {
|
|
|
55
62
|
const qty = Math.max(1, parseInt(qtyStr, 10));
|
|
56
63
|
if (Number.isFinite(qty) && qty !== item.quantity) {
|
|
57
64
|
defaultQtyApplied.current = true;
|
|
58
|
-
adjustQuantity(
|
|
65
|
+
adjustQuantity(
|
|
66
|
+
effectiveSessionId,
|
|
67
|
+
item.price_id,
|
|
68
|
+
qty,
|
|
69
|
+
currencyId,
|
|
70
|
+
session,
|
|
71
|
+
refresh,
|
|
72
|
+
sessionData,
|
|
73
|
+
setSessionData
|
|
74
|
+
);
|
|
59
75
|
break; // only apply to the first matching item for ?qty
|
|
60
76
|
}
|
|
61
77
|
}
|
|
@@ -68,7 +84,7 @@ export function useLineItems(): UseLineItemsReturn {
|
|
|
68
84
|
const updateQuantity = useMemoizedFn(async (itemId: string, qty: number) => {
|
|
69
85
|
if (session?.status === 'complete') return;
|
|
70
86
|
try {
|
|
71
|
-
await adjustQuantity(effectiveSessionId, itemId, qty, currencyId, session, refresh);
|
|
87
|
+
await adjustQuantity(effectiveSessionId, itemId, qty, currencyId, session, refresh, sessionData, setSessionData);
|
|
72
88
|
} catch (err: unknown) {
|
|
73
89
|
console.error('Failed to update quantity:', getErrorMessage(err));
|
|
74
90
|
}
|
|
@@ -76,12 +92,12 @@ export function useLineItems(): UseLineItemsReturn {
|
|
|
76
92
|
|
|
77
93
|
const upsell = useMemoizedFn(async (fromId: string, toId: string) => {
|
|
78
94
|
if (session?.status === 'complete') return;
|
|
79
|
-
await performUpsell(effectiveSessionId, fromId, toId, session, currencyId, refresh);
|
|
95
|
+
await performUpsell(effectiveSessionId, fromId, toId, session, currencyId, refresh, sessionData, setSessionData);
|
|
80
96
|
});
|
|
81
97
|
|
|
82
98
|
const downsell = useMemoizedFn(async (priceId: string) => {
|
|
83
99
|
if (session?.status === 'complete') return;
|
|
84
|
-
await performDownsell(effectiveSessionId, priceId, session, currencyId, refresh);
|
|
100
|
+
await performDownsell(effectiveSessionId, priceId, session, currencyId, refresh, sessionData, setSessionData);
|
|
85
101
|
});
|
|
86
102
|
|
|
87
103
|
// Cross-sell item detection (must be before addCrossSell so it can reference crossSellItem)
|
|
@@ -124,7 +140,15 @@ export function useLineItems(): UseLineItemsReturn {
|
|
|
124
140
|
if (session?.status === 'complete') return;
|
|
125
141
|
if (!crossSellItem) return;
|
|
126
142
|
try {
|
|
127
|
-
await addCrossSellItem(
|
|
143
|
+
await addCrossSellItem(
|
|
144
|
+
effectiveSessionId,
|
|
145
|
+
crossSellItem.id,
|
|
146
|
+
session,
|
|
147
|
+
currencyId,
|
|
148
|
+
refresh,
|
|
149
|
+
sessionData,
|
|
150
|
+
setSessionData
|
|
151
|
+
);
|
|
128
152
|
} catch (err: unknown) {
|
|
129
153
|
console.error('Failed to add cross-sell:', getErrorMessage(err));
|
|
130
154
|
}
|
|
@@ -133,7 +157,7 @@ export function useLineItems(): UseLineItemsReturn {
|
|
|
133
157
|
const removeCrossSell = useMemoizedFn(async () => {
|
|
134
158
|
if (session?.status === 'complete') return;
|
|
135
159
|
try {
|
|
136
|
-
await removeCrossSellItem(effectiveSessionId, session, currencyId, refresh);
|
|
160
|
+
await removeCrossSellItem(effectiveSessionId, session, currencyId, refresh, sessionData, setSessionData);
|
|
137
161
|
} catch (err: unknown) {
|
|
138
162
|
console.error('Failed to remove cross-sell:', getErrorMessage(err));
|
|
139
163
|
}
|
|
@@ -143,7 +167,16 @@ export function useLineItems(): UseLineItemsReturn {
|
|
|
143
167
|
if (session?.status === 'complete') return;
|
|
144
168
|
if (!isDonation) return;
|
|
145
169
|
try {
|
|
146
|
-
await changeDonationAmount(
|
|
170
|
+
await changeDonationAmount(
|
|
171
|
+
effectiveSessionId,
|
|
172
|
+
priceId,
|
|
173
|
+
amount,
|
|
174
|
+
session,
|
|
175
|
+
currencyId,
|
|
176
|
+
refresh,
|
|
177
|
+
sessionData,
|
|
178
|
+
setSessionData
|
|
179
|
+
);
|
|
147
180
|
} catch (err: unknown) {
|
|
148
181
|
console.error('Failed to change amount:', getErrorMessage(err));
|
|
149
182
|
}
|
|
@@ -42,9 +42,10 @@ export interface UsePaymentMethodReturn {
|
|
|
42
42
|
export function usePaymentMethod(
|
|
43
43
|
sessionData: SessionData | null,
|
|
44
44
|
sessionId: string,
|
|
45
|
-
refreshSession: (force?: boolean) => Promise<void
|
|
45
|
+
refreshSession: (force?: boolean) => Promise<void>,
|
|
46
|
+
setSessionData?: (data: SessionData) => void
|
|
46
47
|
): UsePaymentMethodReturn {
|
|
47
|
-
const methods = sessionData?.paymentMethods || [];
|
|
48
|
+
const methods = useMemo(() => sessionData?.paymentMethods || [], [sessionData?.paymentMethods]);
|
|
48
49
|
const session = sessionData?.checkoutSession;
|
|
49
50
|
|
|
50
51
|
const [currencyId, setCurrencyId] = useState<string | null>(() => getInitialCurrencyId(session, methods));
|
|
@@ -88,7 +89,7 @@ export function usePaymentMethod(
|
|
|
88
89
|
|
|
89
90
|
setSwitching(true);
|
|
90
91
|
try {
|
|
91
|
-
await api.put(API.SWITCH_CURRENCY(sessionId), {
|
|
92
|
+
const { data } = await api.put(API.SWITCH_CURRENCY(sessionId), {
|
|
92
93
|
currency_id: newCurrencyId,
|
|
93
94
|
payment_method_id: method.id,
|
|
94
95
|
});
|
|
@@ -105,8 +106,14 @@ export function usePaymentMethod(
|
|
|
105
106
|
// Ignore
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
//
|
|
109
|
-
|
|
109
|
+
// Clear quotes — they are currency-specific and stale after switch.
|
|
110
|
+
// Do NOT recalculate promotion here; CheckoutProvider's useEffect handles it
|
|
111
|
+
// after currency change settles, avoiding stale-closure overwrites.
|
|
112
|
+
if (sessionData && setSessionData) {
|
|
113
|
+
setSessionData({ ...sessionData, checkoutSession: data, quotes: undefined });
|
|
114
|
+
} else {
|
|
115
|
+
await refreshSession(true);
|
|
116
|
+
}
|
|
110
117
|
} catch (err: unknown) {
|
|
111
118
|
console.error('Failed to switch currency:', getErrorMessage(err));
|
|
112
119
|
// Fallback: align with backend currency to resolve currencyMismatch
|
|
@@ -131,6 +138,7 @@ export function usePaymentMethod(
|
|
|
131
138
|
|
|
132
139
|
const sessionCurrencyId = session.currency_id;
|
|
133
140
|
if (session.status === 'complete') return;
|
|
141
|
+
|
|
134
142
|
if (sessionCurrencyId && sessionCurrencyId !== currencyId) {
|
|
135
143
|
switchCurrency(currencyId);
|
|
136
144
|
}
|
|
@@ -71,7 +71,8 @@ export function usePricing(
|
|
|
71
71
|
currency: TPaymentCurrency | null,
|
|
72
72
|
isStripe: boolean,
|
|
73
73
|
refreshSession: (force?: boolean) => Promise<void>,
|
|
74
|
-
paymentMethodType?: string | null
|
|
74
|
+
paymentMethodType?: string | null,
|
|
75
|
+
switching?: boolean
|
|
75
76
|
): UsePricingReturn {
|
|
76
77
|
const session = sessionData?.checkoutSession;
|
|
77
78
|
const items: TLineItemExpanded[] = (session?.line_items || []) as TLineItemExpanded[];
|
|
@@ -98,7 +99,7 @@ export function usePricing(
|
|
|
98
99
|
|
|
99
100
|
// Fetch exchange rate
|
|
100
101
|
const fetchRate = useMemoizedFn(async () => {
|
|
101
|
-
if (!sessionId || !hasDynamicPricing || isStripe) {
|
|
102
|
+
if (!sessionId || !hasDynamicPricing || isStripe || switching) {
|
|
102
103
|
setRateStatus(hasDynamicPricing ? 'unavailable' : 'available');
|
|
103
104
|
if (isStripe) {
|
|
104
105
|
setExchangeRate(null);
|
|
@@ -136,7 +137,7 @@ export function usePricing(
|
|
|
136
137
|
useEffect(() => {
|
|
137
138
|
mountedRef.current = true;
|
|
138
139
|
|
|
139
|
-
if (!hasDynamicPricing || isStripe || !sessionId || session?.status === 'complete') {
|
|
140
|
+
if (!hasDynamicPricing || isStripe || switching || !sessionId || session?.status === 'complete') {
|
|
140
141
|
// Clear stale rate when switching to Stripe
|
|
141
142
|
if (isStripe) {
|
|
142
143
|
setExchangeRate(null);
|
|
@@ -178,7 +179,7 @@ export function usePricing(
|
|
|
178
179
|
if (pollingRef.current) clearTimeout(pollingRef.current);
|
|
179
180
|
document.removeEventListener('visibilitychange', handleVisibility);
|
|
180
181
|
};
|
|
181
|
-
}, [hasDynamicPricing, isStripe, sessionId, currency?.id, session?.status]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
182
|
+
}, [hasDynamicPricing, isStripe, switching, sessionId, currency?.id, session?.status]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
182
183
|
|
|
183
184
|
// Calculate amounts
|
|
184
185
|
const amounts = useMemo(
|
|
@@ -479,6 +479,13 @@ export function useSubmit(
|
|
|
479
479
|
return;
|
|
480
480
|
}
|
|
481
481
|
|
|
482
|
+
// STOP_ACCEPTING_ORDERS — show dedicated dialog instead of toast
|
|
483
|
+
if (errorCode === 'STOP_ACCEPTING_ORDERS') {
|
|
484
|
+
setStatus('service_suspended');
|
|
485
|
+
setContext({ type: 'service_suspended' });
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
|
|
482
489
|
// UNIFIED_APP_REQUIRED / CUSTOMER_LIMITED
|
|
483
490
|
if (errorCode === 'UNIFIED_APP_REQUIRED' || errorCode === 'CUSTOMER_LIMITED') {
|
|
484
491
|
setStatus('failed');
|
|
@@ -625,7 +632,12 @@ export function useSubmit(
|
|
|
625
632
|
|
|
626
633
|
// Cancel
|
|
627
634
|
const cancel = useMemoizedFn(() => {
|
|
628
|
-
if (
|
|
635
|
+
if (
|
|
636
|
+
status === 'confirming_price' ||
|
|
637
|
+
status === 'confirming_fast_pay' ||
|
|
638
|
+
status === 'credit_insufficient' ||
|
|
639
|
+
status === 'service_suspended'
|
|
640
|
+
) {
|
|
629
641
|
setStatus('idle');
|
|
630
642
|
setContext(null);
|
|
631
643
|
unlock();
|
|
@@ -11,13 +11,13 @@ export interface UseUpsellReturn {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function useUpsell(): UseUpsellReturn {
|
|
14
|
-
const { session, effectiveSessionId, refresh } = useSessionContext();
|
|
14
|
+
const { session, effectiveSessionId, refresh, sessionData, setSessionData } = useSessionContext();
|
|
15
15
|
const { currency } = usePaymentMethodContext();
|
|
16
16
|
const currencyId = currency?.id || null;
|
|
17
17
|
|
|
18
18
|
const upsell = useMemoizedFn(async (fromId: string, toId: string) => {
|
|
19
19
|
try {
|
|
20
|
-
await performUpsell(effectiveSessionId, fromId, toId, session, currencyId, refresh);
|
|
20
|
+
await performUpsell(effectiveSessionId, fromId, toId, session, currencyId, refresh, sessionData, setSessionData);
|
|
21
21
|
} catch (err: unknown) {
|
|
22
22
|
console.error('Failed to upsell:', getErrorMessage(err));
|
|
23
23
|
}
|
|
@@ -25,7 +25,7 @@ export function useUpsell(): UseUpsellReturn {
|
|
|
25
25
|
|
|
26
26
|
const downsell = useMemoizedFn(async (priceId: string) => {
|
|
27
27
|
try {
|
|
28
|
-
await performDownsell(effectiveSessionId, priceId, session, currencyId, refresh);
|
|
28
|
+
await performDownsell(effectiveSessionId, priceId, session, currencyId, refresh, sessionData, setSessionData);
|
|
29
29
|
} catch (err: unknown) {
|
|
30
30
|
console.error('Failed to downsell:', getErrorMessage(err));
|
|
31
31
|
}
|
package/src/checkout/types.ts
CHANGED
|
@@ -16,6 +16,7 @@ export type SubmitStatus =
|
|
|
16
16
|
| 'confirming_price'
|
|
17
17
|
| 'confirming_fast_pay'
|
|
18
18
|
| 'credit_insufficient'
|
|
19
|
+
| 'service_suspended'
|
|
19
20
|
| 'waiting_did'
|
|
20
21
|
| 'waiting_stripe'
|
|
21
22
|
| 'completed'
|
|
@@ -28,6 +29,7 @@ export type SubmitContext =
|
|
|
28
29
|
| { type: 'stripe'; clientSecret: string; intentType?: 'payment_intent' | 'setup_intent' }
|
|
29
30
|
| { type: 'did_connect'; action: string; checkpointId: string; extraParams: Record<string, unknown> }
|
|
30
31
|
| { type: 'error'; message: string; code?: string }
|
|
32
|
+
| { type: 'service_suspended' }
|
|
31
33
|
| null;
|
|
32
34
|
|
|
33
35
|
// ── Form ──
|
|
@@ -83,8 +85,8 @@ export interface UseCheckoutReturn {
|
|
|
83
85
|
// Loading state
|
|
84
86
|
isLoading: boolean;
|
|
85
87
|
error: string | null;
|
|
86
|
-
/** Structured error code
|
|
87
|
-
errorCode: 'SESSION_EXPIRED' | 'EMPTY_LINE_ITEMS' | null;
|
|
88
|
+
/** Structured error code */
|
|
89
|
+
errorCode: 'SESSION_EXPIRED' | 'EMPTY_LINE_ITEMS' | 'STOP_ACCEPTING_ORDERS' | null;
|
|
88
90
|
refresh: () => Promise<void>;
|
|
89
91
|
|
|
90
92
|
// Vendor count (for post-payment vendor order polling)
|