@tagadapay/plugin-sdk 2.4.39 → 2.5.2
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/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/react/hooks/useCheckout.js +19 -2
- package/dist/react/hooks/useCheckoutSession.d.ts +19 -0
- package/dist/react/hooks/useCheckoutSession.js +108 -0
- package/dist/react/hooks/useCheckoutToken.d.ts +17 -0
- package/dist/react/hooks/useCheckoutToken.js +80 -0
- package/dist/react/hooks/useOrderBump.js +92 -13
- package/dist/react/hooks/useOrderBumpV2.d.ts +17 -0
- package/dist/react/hooks/useOrderBumpV2.js +95 -0
- package/dist/react/hooks/useOrderBumpV3.d.ts +23 -0
- package/dist/react/hooks/useOrderBumpV3.js +109 -0
- package/dist/react/hooks/usePostPurchases.js +11 -5
- package/dist/react/index.d.ts +8 -0
- package/dist/react/index.js +4 -0
- package/dist/react/services/apiService.d.ts +1 -0
- package/dist/react/services/apiService.js +3 -0
- package/dist/v2/core/googleAutocomplete.d.ts +65 -0
- package/dist/v2/core/googleAutocomplete.js +94 -0
- package/dist/v2/core/index.d.ts +8 -0
- package/dist/v2/core/index.js +11 -0
- package/dist/v2/core/isoData.d.ts +50 -0
- package/dist/v2/core/isoData.js +103 -0
- package/dist/v2/core/resources/apiClient.d.ts +25 -0
- package/dist/v2/core/resources/apiClient.js +95 -0
- package/dist/v2/core/resources/checkout.d.ts +189 -0
- package/dist/v2/core/resources/checkout.js +119 -0
- package/dist/v2/core/resources/index.d.ts +13 -0
- package/dist/v2/core/resources/index.js +13 -0
- package/dist/v2/core/resources/offers.d.ts +98 -0
- package/dist/v2/core/resources/offers.js +115 -0
- package/dist/v2/core/resources/orders.d.ts +40 -0
- package/dist/v2/core/resources/orders.js +59 -0
- package/dist/v2/core/resources/payments.d.ts +140 -0
- package/dist/v2/core/resources/payments.js +126 -0
- package/dist/v2/core/resources/postPurchases.d.ts +182 -0
- package/dist/v2/core/resources/postPurchases.js +116 -0
- package/dist/v2/core/resources/products.d.ts +29 -0
- package/dist/v2/core/resources/products.js +49 -0
- package/dist/v2/core/resources/promotions.d.ts +45 -0
- package/dist/v2/core/resources/promotions.js +87 -0
- package/dist/v2/core/resources/threeds.d.ts +23 -0
- package/dist/v2/core/resources/threeds.js +15 -0
- package/dist/v2/core/utils/checkout.d.ts +24 -0
- package/dist/v2/core/utils/checkout.js +30 -0
- package/dist/v2/core/utils/currency.d.ts +28 -0
- package/dist/v2/core/utils/currency.js +272 -0
- package/dist/v2/core/utils/index.d.ts +12 -0
- package/dist/v2/core/utils/index.js +12 -0
- package/dist/v2/core/utils/order.d.ts +159 -0
- package/dist/v2/core/utils/order.js +42 -0
- package/dist/v2/core/utils/orderBump.d.ts +40 -0
- package/dist/v2/core/utils/orderBump.js +47 -0
- package/dist/v2/core/utils/pluginConfig.d.ts +43 -0
- package/dist/v2/core/utils/pluginConfig.js +155 -0
- package/dist/v2/core/utils/postPurchases.d.ts +32 -0
- package/dist/v2/core/utils/postPurchases.js +42 -0
- package/dist/v2/core/utils/products.d.ts +58 -0
- package/dist/v2/core/utils/products.js +64 -0
- package/dist/v2/core/utils/promotions.d.ts +24 -0
- package/dist/v2/core/utils/promotions.js +30 -0
- package/dist/v2/index.d.ts +19 -0
- package/dist/v2/index.js +15 -0
- package/dist/v2/react/components/DebugDrawer.d.ts +7 -0
- package/dist/v2/react/components/DebugDrawer.js +383 -0
- package/dist/v2/react/hooks/useApiQuery.d.ts +28 -0
- package/dist/v2/react/hooks/useApiQuery.js +84 -0
- package/dist/v2/react/hooks/useCheckoutQuery.d.ts +39 -0
- package/dist/v2/react/hooks/useCheckoutQuery.js +194 -0
- package/dist/v2/react/hooks/useCheckoutToken.d.ts +17 -0
- package/dist/v2/react/hooks/useCheckoutToken.js +65 -0
- package/dist/v2/react/hooks/useCurrency.d.ts +9 -0
- package/dist/v2/react/hooks/useCurrency.js +21 -0
- package/dist/v2/react/hooks/useGeoLocation.d.ts +138 -0
- package/dist/v2/react/hooks/useGeoLocation.js +123 -0
- package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +74 -0
- package/dist/v2/react/hooks/useGoogleAutocomplete.js +196 -0
- package/dist/v2/react/hooks/useISOData.d.ts +61 -0
- package/dist/v2/react/hooks/useISOData.js +175 -0
- package/dist/v2/react/hooks/useOffersQuery.d.ts +65 -0
- package/dist/v2/react/hooks/useOffersQuery.js +342 -0
- package/dist/v2/react/hooks/useOrderBumpQuery.d.ts +20 -0
- package/dist/v2/react/hooks/useOrderBumpQuery.js +92 -0
- package/dist/v2/react/hooks/useOrderQuery.d.ts +29 -0
- package/dist/v2/react/hooks/useOrderQuery.js +98 -0
- package/dist/v2/react/hooks/usePaymentPolling.d.ts +45 -0
- package/dist/v2/react/hooks/usePaymentPolling.js +140 -0
- package/dist/v2/react/hooks/usePaymentQuery.d.ts +19 -0
- package/dist/v2/react/hooks/usePaymentQuery.js +272 -0
- package/dist/v2/react/hooks/usePluginConfig.d.ts +16 -0
- package/dist/v2/react/hooks/usePluginConfig.js +35 -0
- package/dist/v2/react/hooks/usePostPurchasesQuery.d.ts +63 -0
- package/dist/v2/react/hooks/usePostPurchasesQuery.js +343 -0
- package/dist/v2/react/hooks/useProductsQuery.d.ts +31 -0
- package/dist/v2/react/hooks/useProductsQuery.js +102 -0
- package/dist/v2/react/hooks/usePromotionsQuery.d.ts +28 -0
- package/dist/v2/react/hooks/usePromotionsQuery.js +97 -0
- package/dist/v2/react/hooks/useThreeds.d.ts +36 -0
- package/dist/v2/react/hooks/useThreeds.js +150 -0
- package/dist/v2/react/hooks/useThreedsModal.d.ts +13 -0
- package/dist/v2/react/hooks/useThreedsModal.js +343 -0
- package/dist/v2/react/index.d.ts +38 -0
- package/dist/v2/react/index.js +27 -0
- package/dist/v2/react/providers/TagadaProvider.d.ts +63 -0
- package/dist/v2/react/providers/TagadaProvider.js +680 -0
- package/package.json +10 -3
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { useCallback, useRef, useEffect, useMemo } from 'react';
|
|
2
|
+
import { PaymentsResource } from '../../core/resources/payments';
|
|
3
|
+
import { getGlobalApiClient } from './useApiQuery';
|
|
4
|
+
export function usePaymentPolling() {
|
|
5
|
+
const pollIntervalRef = useRef(null);
|
|
6
|
+
const attemptsRef = useRef(0);
|
|
7
|
+
const isPollingRef = useRef(false);
|
|
8
|
+
const isMountedRef = useRef(true);
|
|
9
|
+
const currentPaymentIdRef = useRef(null);
|
|
10
|
+
// Create payments resource client
|
|
11
|
+
const paymentsResource = useMemo(() => {
|
|
12
|
+
try {
|
|
13
|
+
return new PaymentsResource(getGlobalApiClient());
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
throw new Error('Failed to initialize payments resource: ' + (error instanceof Error ? error.message : 'Unknown error'));
|
|
17
|
+
}
|
|
18
|
+
}, []);
|
|
19
|
+
// Track mounted state and cleanup
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
isMountedRef.current = true;
|
|
22
|
+
return () => {
|
|
23
|
+
isMountedRef.current = false;
|
|
24
|
+
stopPolling();
|
|
25
|
+
};
|
|
26
|
+
}, []);
|
|
27
|
+
const stopPolling = useCallback(() => {
|
|
28
|
+
if (pollIntervalRef.current) {
|
|
29
|
+
clearInterval(pollIntervalRef.current);
|
|
30
|
+
pollIntervalRef.current = null;
|
|
31
|
+
}
|
|
32
|
+
isPollingRef.current = false;
|
|
33
|
+
currentPaymentIdRef.current = null;
|
|
34
|
+
if (isMountedRef.current) {
|
|
35
|
+
// Component is still mounted
|
|
36
|
+
}
|
|
37
|
+
}, []);
|
|
38
|
+
const startPolling = useCallback((paymentId, options = {}) => {
|
|
39
|
+
if (!paymentId) {
|
|
40
|
+
return { stop: stopPolling, isPolling: () => false };
|
|
41
|
+
}
|
|
42
|
+
// Prevent multiple polling sessions for the same payment
|
|
43
|
+
if (currentPaymentIdRef.current === paymentId && isPollingRef.current) {
|
|
44
|
+
return { stop: stopPolling, isPolling: () => isPollingRef.current };
|
|
45
|
+
}
|
|
46
|
+
// Clean up any existing polling
|
|
47
|
+
stopPolling();
|
|
48
|
+
// Don't start polling if component is unmounted
|
|
49
|
+
if (!isMountedRef.current) {
|
|
50
|
+
return { stop: stopPolling, isPolling: () => false };
|
|
51
|
+
}
|
|
52
|
+
// Reset attempts counter and set current payment
|
|
53
|
+
attemptsRef.current = 0;
|
|
54
|
+
isPollingRef.current = true;
|
|
55
|
+
currentPaymentIdRef.current = paymentId;
|
|
56
|
+
const { onRequireAction, onSuccess, onFailure, maxAttempts = 20, pollInterval = 1500 } = options;
|
|
57
|
+
const checkPaymentStatus = async () => {
|
|
58
|
+
// Stop if component was unmounted or polling was stopped
|
|
59
|
+
if (!isMountedRef.current || !isPollingRef.current) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
attemptsRef.current++;
|
|
64
|
+
const payment = await paymentsResource.getPaymentStatus(paymentId);
|
|
65
|
+
// Check again after async operation
|
|
66
|
+
if (!isMountedRef.current || !isPollingRef.current) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// Type guard and validation
|
|
70
|
+
if (!payment?.id) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Check if payment requires action
|
|
74
|
+
if (payment.requireAction !== 'none' && payment.requireActionData) {
|
|
75
|
+
stopPolling();
|
|
76
|
+
if (isMountedRef.current && onRequireAction) {
|
|
77
|
+
onRequireAction(payment, stopPolling);
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Check for successful payment
|
|
82
|
+
if (payment.status === 'succeeded' ||
|
|
83
|
+
(payment.status === 'pending' && payment.subStatus === 'authorized')) {
|
|
84
|
+
stopPolling();
|
|
85
|
+
if (isMountedRef.current && onSuccess) {
|
|
86
|
+
onSuccess(payment);
|
|
87
|
+
}
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// Check for failed payment (non-succeeded and not pending)
|
|
91
|
+
if (payment.status !== 'succeeded' && payment.status !== 'pending') {
|
|
92
|
+
stopPolling();
|
|
93
|
+
if (isMountedRef.current && onFailure) {
|
|
94
|
+
onFailure(payment.status || 'Payment failed');
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// Stop after max attempts
|
|
99
|
+
if (attemptsRef.current >= maxAttempts) {
|
|
100
|
+
stopPolling();
|
|
101
|
+
if (isMountedRef.current && onFailure) {
|
|
102
|
+
onFailure('Payment verification timeout');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (_error) {
|
|
107
|
+
// Stop polling on repeated errors to prevent infinite loops
|
|
108
|
+
if (attemptsRef.current >= 3) {
|
|
109
|
+
stopPolling();
|
|
110
|
+
if (isMountedRef.current && onFailure) {
|
|
111
|
+
onFailure('Payment verification failed due to network errors');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Continue polling for occasional errors
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
// Start polling immediately, but check if still mounted
|
|
118
|
+
if (isMountedRef.current && isPollingRef.current) {
|
|
119
|
+
void checkPaymentStatus();
|
|
120
|
+
pollIntervalRef.current = setInterval(() => {
|
|
121
|
+
if (isMountedRef.current && isPollingRef.current) {
|
|
122
|
+
void checkPaymentStatus();
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
stopPolling();
|
|
126
|
+
}
|
|
127
|
+
}, pollInterval);
|
|
128
|
+
}
|
|
129
|
+
// Return control object
|
|
130
|
+
return {
|
|
131
|
+
stop: stopPolling,
|
|
132
|
+
isPolling: () => isPollingRef.current && isMountedRef.current,
|
|
133
|
+
};
|
|
134
|
+
}, [paymentsResource, stopPolling]);
|
|
135
|
+
return {
|
|
136
|
+
startPolling,
|
|
137
|
+
stopPolling,
|
|
138
|
+
isPolling: () => isPollingRef.current && isMountedRef.current,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment Hook using TanStack Query (V2)
|
|
3
|
+
* Matches the old usePayment.ts implementation exactly for easy migration
|
|
4
|
+
*/
|
|
5
|
+
import type { PaymentResponse, PaymentOptions, CardPaymentMethod, ApplePayToken, PaymentInstrumentResponse, PaymentInstrumentCustomerResponse } from '../../core/resources/payments';
|
|
6
|
+
export type { Payment as PaymentType, PaymentResponse, PaymentOptions, CardPaymentMethod, ApplePayToken, PaymentInstrumentResponse, PaymentInstrumentCustomerResponse, PaymentInstrumentCustomer } from '../../core/resources/payments';
|
|
7
|
+
export interface PaymentHook {
|
|
8
|
+
processCardPayment: (checkoutSessionId: string, cardData: CardPaymentMethod, options?: PaymentOptions) => Promise<PaymentResponse>;
|
|
9
|
+
processApplePayPayment: (checkoutSessionId: string, applePayToken: ApplePayToken, options?: PaymentOptions) => Promise<PaymentResponse>;
|
|
10
|
+
processPaymentWithInstrument: (checkoutSessionId: string, paymentInstrumentId: string, options?: PaymentOptions) => Promise<PaymentResponse>;
|
|
11
|
+
createCardPaymentInstrument: (cardData: CardPaymentMethod) => Promise<PaymentInstrumentResponse>;
|
|
12
|
+
createApplePayPaymentInstrument: (applePayToken: ApplePayToken) => Promise<PaymentInstrumentResponse>;
|
|
13
|
+
getCardPaymentInstruments: () => Promise<PaymentInstrumentCustomerResponse>;
|
|
14
|
+
isLoading: boolean;
|
|
15
|
+
error: string | null;
|
|
16
|
+
clearError: () => void;
|
|
17
|
+
currentPaymentId: string | null;
|
|
18
|
+
}
|
|
19
|
+
export declare function usePaymentQuery(): PaymentHook;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment Hook using TanStack Query (V2)
|
|
3
|
+
* Matches the old usePayment.ts implementation exactly for easy migration
|
|
4
|
+
*/
|
|
5
|
+
import { useState, useCallback, useMemo, useRef, useEffect } from 'react';
|
|
6
|
+
import { useBasisTheory } from '@basis-theory/basis-theory-react';
|
|
7
|
+
import { getBasisTheoryApiKey } from '../../../react/config/payment';
|
|
8
|
+
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
9
|
+
import { PaymentsResource } from '../../core/resources/payments';
|
|
10
|
+
import { usePaymentPolling } from './usePaymentPolling';
|
|
11
|
+
import { useThreeds } from './useThreeds';
|
|
12
|
+
import { getGlobalApiClient } from './useApiQuery';
|
|
13
|
+
export function usePaymentQuery() {
|
|
14
|
+
const { environment } = useTagadaContext();
|
|
15
|
+
// Create payments resource client
|
|
16
|
+
const paymentsResource = useMemo(() => {
|
|
17
|
+
try {
|
|
18
|
+
return new PaymentsResource(getGlobalApiClient());
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
throw new Error('Failed to initialize payments resource: ' + (error instanceof Error ? error.message : 'Unknown error'));
|
|
22
|
+
}
|
|
23
|
+
}, []);
|
|
24
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
25
|
+
const [error, setError] = useState(null);
|
|
26
|
+
const [currentPaymentId, setCurrentPaymentId] = useState(null);
|
|
27
|
+
const { startPolling, stopPolling } = usePaymentPolling();
|
|
28
|
+
const { createSession, startChallenge } = useThreeds();
|
|
29
|
+
// Track challenge in progress to prevent multiple challenges
|
|
30
|
+
const challengeInProgressRef = useRef(false);
|
|
31
|
+
// Stabilize environment value to prevent re-renders
|
|
32
|
+
const currentEnvironment = useMemo(() => environment?.environment || 'local', [environment?.environment]);
|
|
33
|
+
// Get API key from embedded configuration with proper environment detection
|
|
34
|
+
const apiKey = useMemo(() => getBasisTheoryApiKey(currentEnvironment), [currentEnvironment]);
|
|
35
|
+
// Initialize BasisTheory using React wrapper
|
|
36
|
+
const { bt: basisTheory, error: btError } = useBasisTheory(apiKey, {
|
|
37
|
+
elements: false,
|
|
38
|
+
});
|
|
39
|
+
// Handle BasisTheory initialization errors (only log once when state changes)
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (btError) {
|
|
42
|
+
setError('Failed to initialize payment processor: ' + btError.message);
|
|
43
|
+
}
|
|
44
|
+
else if (basisTheory && !error) {
|
|
45
|
+
setError(null); // Clear any previous errors
|
|
46
|
+
}
|
|
47
|
+
}, [basisTheory, btError]); // Removed error from dependency to prevent loops
|
|
48
|
+
// Clean up polling when component unmounts
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
return () => {
|
|
51
|
+
stopPolling();
|
|
52
|
+
};
|
|
53
|
+
}, [stopPolling]);
|
|
54
|
+
// Handle payment actions (3DS, redirects, etc.) - matches old implementation
|
|
55
|
+
const handlePaymentAction = useCallback(async (payment, options = {}) => {
|
|
56
|
+
if (payment.requireAction === 'none')
|
|
57
|
+
return;
|
|
58
|
+
if (payment?.requireActionData?.processed)
|
|
59
|
+
return;
|
|
60
|
+
const actionData = payment.requireActionData;
|
|
61
|
+
if (!actionData)
|
|
62
|
+
return;
|
|
63
|
+
// Mark action as processed
|
|
64
|
+
try {
|
|
65
|
+
await paymentsResource.markPaymentActionProcessed(payment.id);
|
|
66
|
+
}
|
|
67
|
+
catch (_error) {
|
|
68
|
+
// Error handling removed
|
|
69
|
+
}
|
|
70
|
+
switch (actionData.type) {
|
|
71
|
+
case 'threeds_auth':
|
|
72
|
+
if (actionData.metadata?.threedsSession && !challengeInProgressRef.current) {
|
|
73
|
+
try {
|
|
74
|
+
challengeInProgressRef.current = true;
|
|
75
|
+
await startChallenge({
|
|
76
|
+
sessionId: actionData.metadata.threedsSession.externalSessionId,
|
|
77
|
+
acsChallengeUrl: actionData.metadata.threedsSession.acsChallengeUrl,
|
|
78
|
+
acsTransactionId: actionData.metadata.threedsSession.acsTransID,
|
|
79
|
+
threeDSVersion: actionData.metadata.threedsSession.messageVersion,
|
|
80
|
+
}, { provider: options.threedsProvider || 'basis_theory' });
|
|
81
|
+
challengeInProgressRef.current = false;
|
|
82
|
+
// Start polling after challenge completion
|
|
83
|
+
if (payment.id) {
|
|
84
|
+
startPolling(payment.id, {
|
|
85
|
+
onRequireAction: (updatedPayment) => {
|
|
86
|
+
void handlePaymentAction(updatedPayment, options);
|
|
87
|
+
},
|
|
88
|
+
onSuccess: (successPayment) => {
|
|
89
|
+
setIsLoading(false);
|
|
90
|
+
options.onSuccess?.({
|
|
91
|
+
paymentId: successPayment.id,
|
|
92
|
+
payment: successPayment,
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
onFailure: (errorMsg) => {
|
|
96
|
+
setError(errorMsg);
|
|
97
|
+
setIsLoading(false);
|
|
98
|
+
options.onFailure?.(errorMsg);
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (_error) {
|
|
104
|
+
challengeInProgressRef.current = false;
|
|
105
|
+
const errorMsg = _error instanceof Error ? _error.message : 'Failed to start 3DS challenge';
|
|
106
|
+
setError(errorMsg);
|
|
107
|
+
setIsLoading(false);
|
|
108
|
+
options.onFailure?.(errorMsg);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
case 'processor_auth':
|
|
113
|
+
case 'redirect': {
|
|
114
|
+
if (actionData.metadata?.redirect?.redirectUrl) {
|
|
115
|
+
window.location.href = actionData.metadata.redirect.redirectUrl;
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
case 'error': {
|
|
120
|
+
const errorMsg = actionData.message || 'Payment processing failed';
|
|
121
|
+
setError(errorMsg);
|
|
122
|
+
setIsLoading(false);
|
|
123
|
+
options.onFailure?.(errorMsg);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
options.onRequireAction?.(payment);
|
|
128
|
+
}, [paymentsResource, startPolling, startChallenge]);
|
|
129
|
+
// Create card payment instrument - matches old implementation
|
|
130
|
+
const createCardPaymentInstrument = useCallback((cardData) => {
|
|
131
|
+
return paymentsResource.createCardPaymentInstrument(basisTheory, cardData);
|
|
132
|
+
}, [basisTheory, paymentsResource]);
|
|
133
|
+
// Create Apple Pay payment instrument - matches old implementation
|
|
134
|
+
const createApplePayPaymentInstrument = useCallback((applePayToken) => {
|
|
135
|
+
return paymentsResource.createApplePayPaymentInstrument(basisTheory, applePayToken);
|
|
136
|
+
}, [basisTheory, paymentsResource]);
|
|
137
|
+
// Process payment directly with checkout session - matches old implementation
|
|
138
|
+
const processPaymentDirect = useCallback(async (checkoutSessionId, paymentInstrumentId, threedsSessionId, options = {}) => {
|
|
139
|
+
try {
|
|
140
|
+
const response = await paymentsResource.processPaymentDirect(checkoutSessionId, paymentInstrumentId, threedsSessionId, {
|
|
141
|
+
initiatedBy: options.initiatedBy,
|
|
142
|
+
source: options.source,
|
|
143
|
+
});
|
|
144
|
+
setCurrentPaymentId(response.payment?.id);
|
|
145
|
+
if (response.payment.requireAction !== 'none') {
|
|
146
|
+
await handlePaymentAction(response.payment, options);
|
|
147
|
+
}
|
|
148
|
+
else if (response.payment.status === 'succeeded') {
|
|
149
|
+
setIsLoading(false);
|
|
150
|
+
options.onSuccess?.(response);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
// Start polling for payment status
|
|
154
|
+
startPolling(response.payment?.id, {
|
|
155
|
+
onRequireAction: (payment) => {
|
|
156
|
+
void handlePaymentAction(payment, options);
|
|
157
|
+
},
|
|
158
|
+
onSuccess: (payment) => {
|
|
159
|
+
setIsLoading(false);
|
|
160
|
+
options.onSuccess?.({
|
|
161
|
+
paymentId: payment.id,
|
|
162
|
+
payment,
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
onFailure: (errorMsg) => {
|
|
166
|
+
setError(errorMsg);
|
|
167
|
+
setIsLoading(false);
|
|
168
|
+
options.onFailure?.(errorMsg);
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return response;
|
|
173
|
+
}
|
|
174
|
+
catch (_error) {
|
|
175
|
+
const errorMsg = _error instanceof Error ? _error.message : 'Payment failed';
|
|
176
|
+
setError(errorMsg);
|
|
177
|
+
setIsLoading(false);
|
|
178
|
+
options.onFailure?.(errorMsg);
|
|
179
|
+
throw _error;
|
|
180
|
+
}
|
|
181
|
+
}, [paymentsResource, handlePaymentAction, startPolling]);
|
|
182
|
+
// Process card payment - matches old implementation
|
|
183
|
+
const processCardPayment = useCallback(async (checkoutSessionId, cardData, options = {}) => {
|
|
184
|
+
setIsLoading(true);
|
|
185
|
+
setError(null);
|
|
186
|
+
try {
|
|
187
|
+
// 1. Create payment instrument
|
|
188
|
+
const paymentInstrument = await createCardPaymentInstrument(cardData);
|
|
189
|
+
// 2. Create 3DS session if enabled
|
|
190
|
+
let threedsSessionId;
|
|
191
|
+
if (options.enableThreeds !== false) {
|
|
192
|
+
try {
|
|
193
|
+
const threedsSession = await createSession(paymentInstrument, {
|
|
194
|
+
provider: options.threedsProvider || 'basis_theory',
|
|
195
|
+
});
|
|
196
|
+
threedsSessionId = threedsSession.id;
|
|
197
|
+
}
|
|
198
|
+
catch (_error) {
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// 3. Process payment directly
|
|
202
|
+
return await processPaymentDirect(checkoutSessionId, paymentInstrument.id, threedsSessionId, options);
|
|
203
|
+
}
|
|
204
|
+
catch (_error) {
|
|
205
|
+
setIsLoading(false);
|
|
206
|
+
const errorMsg = _error instanceof Error ? _error.message : 'Payment failed';
|
|
207
|
+
setError(errorMsg);
|
|
208
|
+
options.onFailure?.(errorMsg);
|
|
209
|
+
throw _error;
|
|
210
|
+
}
|
|
211
|
+
}, [createCardPaymentInstrument, createSession, processPaymentDirect]);
|
|
212
|
+
// Process Apple Pay payment - matches old implementation
|
|
213
|
+
const processApplePayPayment = useCallback(async (checkoutSessionId, applePayToken, options = {}) => {
|
|
214
|
+
setIsLoading(true);
|
|
215
|
+
setError(null);
|
|
216
|
+
try {
|
|
217
|
+
// 1. Create payment instrument
|
|
218
|
+
const paymentInstrument = await createApplePayPaymentInstrument(applePayToken);
|
|
219
|
+
// 2. Process payment directly (Apple Pay typically doesn't require 3DS)
|
|
220
|
+
return await processPaymentDirect(checkoutSessionId, paymentInstrument.id, undefined, options);
|
|
221
|
+
}
|
|
222
|
+
catch (_error) {
|
|
223
|
+
setIsLoading(false);
|
|
224
|
+
const errorMsg = _error instanceof Error ? _error.message : 'Apple Pay payment failed';
|
|
225
|
+
setError(errorMsg);
|
|
226
|
+
options.onFailure?.(errorMsg);
|
|
227
|
+
throw _error;
|
|
228
|
+
}
|
|
229
|
+
}, [createApplePayPaymentInstrument, processPaymentDirect]);
|
|
230
|
+
// Process payment with existing instrument - matches old implementation
|
|
231
|
+
const processPaymentWithInstrument = useCallback(async (checkoutSessionId, paymentInstrumentId, options = {}) => {
|
|
232
|
+
setIsLoading(true);
|
|
233
|
+
setError(null);
|
|
234
|
+
try {
|
|
235
|
+
return await processPaymentDirect(checkoutSessionId, paymentInstrumentId, undefined, options);
|
|
236
|
+
}
|
|
237
|
+
catch (_error) {
|
|
238
|
+
setIsLoading(false);
|
|
239
|
+
const errorMsg = _error instanceof Error ? _error.message : 'Payment failed';
|
|
240
|
+
setError(errorMsg);
|
|
241
|
+
options.onFailure?.(errorMsg);
|
|
242
|
+
throw _error;
|
|
243
|
+
}
|
|
244
|
+
}, [processPaymentDirect]);
|
|
245
|
+
// Get card payment instruments - matches old implementation
|
|
246
|
+
const getCardPaymentInstruments = useCallback(async () => {
|
|
247
|
+
try {
|
|
248
|
+
const response = await paymentsResource.getCardPaymentInstruments();
|
|
249
|
+
return response;
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
const errorMsg = error instanceof Error ? error.message : 'Failed to fetch payment instruments';
|
|
253
|
+
setError(errorMsg);
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
}, [paymentsResource]);
|
|
257
|
+
const clearError = useCallback(() => {
|
|
258
|
+
setError(null);
|
|
259
|
+
}, []);
|
|
260
|
+
return {
|
|
261
|
+
processCardPayment,
|
|
262
|
+
processApplePayPayment,
|
|
263
|
+
processPaymentWithInstrument,
|
|
264
|
+
createCardPaymentInstrument,
|
|
265
|
+
createApplePayPaymentInstrument,
|
|
266
|
+
getCardPaymentInstruments,
|
|
267
|
+
isLoading: isLoading || !basisTheory, // Indicate loading if BasisTheory is not initialized
|
|
268
|
+
error,
|
|
269
|
+
clearError,
|
|
270
|
+
currentPaymentId,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Configuration Hook
|
|
3
|
+
* Uses PluginConfigUtils for business logic
|
|
4
|
+
*/
|
|
5
|
+
import { PluginConfig, RawPluginConfig } from '../../core/utils/pluginConfig';
|
|
6
|
+
export interface UsePluginConfigOptions {
|
|
7
|
+
rawConfig?: RawPluginConfig;
|
|
8
|
+
}
|
|
9
|
+
export interface UsePluginConfigResult<TConfig = Record<string, any>> {
|
|
10
|
+
config: PluginConfig<TConfig>;
|
|
11
|
+
storeId?: string;
|
|
12
|
+
accountId?: string;
|
|
13
|
+
basePath?: string;
|
|
14
|
+
isValid: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function usePluginConfig<TConfig = Record<string, any>>(options?: UsePluginConfigOptions): UsePluginConfigResult<TConfig>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Configuration Hook
|
|
3
|
+
* Uses PluginConfigUtils for business logic
|
|
4
|
+
*/
|
|
5
|
+
import { useMemo } from 'react';
|
|
6
|
+
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
7
|
+
import { PluginConfigUtils } from '../../core/utils/pluginConfig';
|
|
8
|
+
export function usePluginConfig(options = {}) {
|
|
9
|
+
const { pluginConfig } = useTagadaContext();
|
|
10
|
+
const config = useMemo(() => {
|
|
11
|
+
const baseConfig = PluginConfigUtils.getPluginConfig(options.rawConfig, {
|
|
12
|
+
storeId: pluginConfig.storeId,
|
|
13
|
+
accountId: pluginConfig.accountId,
|
|
14
|
+
basePath: pluginConfig.basePath,
|
|
15
|
+
});
|
|
16
|
+
// Merge config properties from the loaded plugin config
|
|
17
|
+
return {
|
|
18
|
+
...baseConfig,
|
|
19
|
+
...pluginConfig.config,
|
|
20
|
+
storeId: pluginConfig.storeId,
|
|
21
|
+
accountId: pluginConfig.accountId,
|
|
22
|
+
basePath: pluginConfig.basePath,
|
|
23
|
+
};
|
|
24
|
+
}, [options.rawConfig, pluginConfig]);
|
|
25
|
+
const isValid = useMemo(() => {
|
|
26
|
+
return PluginConfigUtils.validateConfig(config);
|
|
27
|
+
}, [config]);
|
|
28
|
+
return {
|
|
29
|
+
config,
|
|
30
|
+
storeId: config.storeId,
|
|
31
|
+
accountId: config.accountId,
|
|
32
|
+
basePath: config.basePath,
|
|
33
|
+
isValid,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post Purchases Hook using TanStack Query
|
|
3
|
+
* Handles post-purchase offers with automatic caching
|
|
4
|
+
*/
|
|
5
|
+
import { PostPurchaseOffer, PostPurchaseOfferItem, PostPurchaseOfferSummary, CheckoutSessionState, OrderSummary, CurrencyOptions } from '../../core/resources/postPurchases';
|
|
6
|
+
export interface UsePostPurchasesQueryOptions {
|
|
7
|
+
orderId: string;
|
|
8
|
+
enabled?: boolean;
|
|
9
|
+
autoInitializeCheckout?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface UsePostPurchasesQueryResult {
|
|
12
|
+
offers: PostPurchaseOffer[];
|
|
13
|
+
isLoading: boolean;
|
|
14
|
+
error: Error | null;
|
|
15
|
+
acceptOffer: (offerId: string, items: PostPurchaseOfferItem[]) => Promise<{
|
|
16
|
+
success: boolean;
|
|
17
|
+
summary?: PostPurchaseOfferSummary;
|
|
18
|
+
error?: string;
|
|
19
|
+
}>;
|
|
20
|
+
declineOffer: (offerId: string) => Promise<{
|
|
21
|
+
success: boolean;
|
|
22
|
+
error?: string;
|
|
23
|
+
}>;
|
|
24
|
+
previewOffer: (offerId: string, items: PostPurchaseOfferItem[]) => Promise<{
|
|
25
|
+
success: boolean;
|
|
26
|
+
summary?: PostPurchaseOfferSummary;
|
|
27
|
+
error?: string;
|
|
28
|
+
}>;
|
|
29
|
+
refresh: () => void;
|
|
30
|
+
payWithCheckoutSession: (checkoutSessionId: string, orderId?: string) => Promise<void>;
|
|
31
|
+
initCheckoutSession: (offerId: string, orderId: string) => Promise<{
|
|
32
|
+
checkoutSessionId: string;
|
|
33
|
+
}>;
|
|
34
|
+
initCheckoutSessionWithVariants: (offerId: string, orderId: string, lineItems: {
|
|
35
|
+
variantId: string;
|
|
36
|
+
quantity: number;
|
|
37
|
+
}[]) => Promise<{
|
|
38
|
+
checkoutSessionId: string;
|
|
39
|
+
}>;
|
|
40
|
+
getOffer: (offerId: string) => PostPurchaseOffer | undefined;
|
|
41
|
+
getTotalValue: () => number;
|
|
42
|
+
getTotalSavings: () => number;
|
|
43
|
+
getCheckoutSessionState: (offerId: string) => CheckoutSessionState | null;
|
|
44
|
+
initializeOfferCheckout: (offerId: string) => Promise<void>;
|
|
45
|
+
getAvailableVariants: (offerId: string, productId: string) => {
|
|
46
|
+
variantId: string;
|
|
47
|
+
variantName: string;
|
|
48
|
+
variantSku: string | null;
|
|
49
|
+
variantDefault: boolean | null;
|
|
50
|
+
variantExternalId: string | null;
|
|
51
|
+
priceId: string;
|
|
52
|
+
currencyOptions: CurrencyOptions;
|
|
53
|
+
}[];
|
|
54
|
+
selectVariant: (offerId: string, productId: string, variantId: string) => Promise<void>;
|
|
55
|
+
getOrderSummary: (offerId: string) => OrderSummary | null;
|
|
56
|
+
isLoadingVariants: (offerId: string, productId: string) => boolean;
|
|
57
|
+
isUpdatingOrderSummary: (offerId: string) => boolean;
|
|
58
|
+
confirmPurchase: (offerId: string, options?: {
|
|
59
|
+
draft?: boolean;
|
|
60
|
+
returnUrl?: string;
|
|
61
|
+
}) => Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
export declare function usePostPurchasesQuery(options: UsePostPurchasesQueryOptions): UsePostPurchasesQueryResult;
|