@tagadapay/plugin-sdk 2.4.39 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +208 -0
- package/dist/v2/react/hooks/useCheckoutToken.d.ts +17 -0
- package/dist/v2/react/hooks/useCheckoutToken.js +80 -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 +126 -0
- package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +74 -0
- package/dist/v2/react/hooks/useGoogleAutocomplete.js +207 -0
- package/dist/v2/react/hooks/useISOData.d.ts +61 -0
- package/dist/v2/react/hooks/useISOData.js +176 -0
- package/dist/v2/react/hooks/useOffersQuery.d.ts +65 -0
- package/dist/v2/react/hooks/useOffersQuery.js +353 -0
- package/dist/v2/react/hooks/useOrderBumpQuery.d.ts +20 -0
- package/dist/v2/react/hooks/useOrderBumpQuery.js +88 -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 +153 -0
- package/dist/v2/react/hooks/usePaymentQuery.d.ts +19 -0
- package/dist/v2/react/hooks/usePaymentQuery.js +283 -0
- package/dist/v2/react/hooks/usePluginConfig.d.ts +16 -0
- package/dist/v2/react/hooks/usePluginConfig.js +36 -0
- package/dist/v2/react/hooks/usePostPurchasesQuery.d.ts +63 -0
- package/dist/v2/react/hooks/usePostPurchasesQuery.js +365 -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 +166 -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,153 @@
|
|
|
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
|
+
console.log('Stopped polling payment status');
|
|
36
|
+
}
|
|
37
|
+
}, []);
|
|
38
|
+
const startPolling = useCallback((paymentId, options = {}) => {
|
|
39
|
+
if (!paymentId) {
|
|
40
|
+
console.error('Cannot poll payment status: paymentId is missing');
|
|
41
|
+
return { stop: stopPolling, isPolling: () => false };
|
|
42
|
+
}
|
|
43
|
+
// Prevent multiple polling sessions for the same payment
|
|
44
|
+
if (currentPaymentIdRef.current === paymentId && isPollingRef.current) {
|
|
45
|
+
console.log('Already polling for payment:', paymentId);
|
|
46
|
+
return { stop: stopPolling, isPolling: () => isPollingRef.current };
|
|
47
|
+
}
|
|
48
|
+
// Clean up any existing polling
|
|
49
|
+
stopPolling();
|
|
50
|
+
// Don't start polling if component is unmounted
|
|
51
|
+
if (!isMountedRef.current) {
|
|
52
|
+
console.log('Component unmounted, skipping polling start');
|
|
53
|
+
return { stop: stopPolling, isPolling: () => false };
|
|
54
|
+
}
|
|
55
|
+
// Reset attempts counter and set current payment
|
|
56
|
+
attemptsRef.current = 0;
|
|
57
|
+
isPollingRef.current = true;
|
|
58
|
+
currentPaymentIdRef.current = paymentId;
|
|
59
|
+
const { onRequireAction, onSuccess, onFailure, maxAttempts = 20, pollInterval = 1500 } = options;
|
|
60
|
+
console.log('Starting to poll payment status for:', paymentId);
|
|
61
|
+
const checkPaymentStatus = async () => {
|
|
62
|
+
// Stop if component was unmounted or polling was stopped
|
|
63
|
+
if (!isMountedRef.current || !isPollingRef.current) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
attemptsRef.current++;
|
|
68
|
+
console.log(`Polling attempt ${attemptsRef.current}/${maxAttempts} for payment ${paymentId}`);
|
|
69
|
+
const payment = await paymentsResource.getPaymentStatus(paymentId);
|
|
70
|
+
// Check again after async operation
|
|
71
|
+
if (!isMountedRef.current || !isPollingRef.current) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Type guard and validation
|
|
75
|
+
if (!payment?.id) {
|
|
76
|
+
console.warn('Invalid payment response received');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
console.log('Payment status update:', payment);
|
|
80
|
+
// Check if payment requires action
|
|
81
|
+
if (payment.requireAction !== 'none' && payment.requireActionData) {
|
|
82
|
+
console.log('Payment requires new action, handling...');
|
|
83
|
+
stopPolling();
|
|
84
|
+
if (isMountedRef.current && onRequireAction) {
|
|
85
|
+
onRequireAction(payment, stopPolling);
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// Check for successful payment
|
|
90
|
+
if (payment.status === 'succeeded' ||
|
|
91
|
+
(payment.status === 'pending' && payment.subStatus === 'authorized')) {
|
|
92
|
+
console.log('Payment succeeded, stopping polling');
|
|
93
|
+
stopPolling();
|
|
94
|
+
if (isMountedRef.current && onSuccess) {
|
|
95
|
+
onSuccess(payment);
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// Check for failed payment (non-succeeded and not pending)
|
|
100
|
+
if (payment.status !== 'succeeded' && payment.status !== 'pending') {
|
|
101
|
+
console.log('Payment failed, stopping polling');
|
|
102
|
+
stopPolling();
|
|
103
|
+
if (isMountedRef.current && onFailure) {
|
|
104
|
+
onFailure(payment.status || 'Payment failed');
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Stop after max attempts
|
|
109
|
+
if (attemptsRef.current >= maxAttempts) {
|
|
110
|
+
console.log('Reached maximum polling attempts');
|
|
111
|
+
stopPolling();
|
|
112
|
+
if (isMountedRef.current && onFailure) {
|
|
113
|
+
onFailure('Payment verification timeout');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
console.error('Error checking payment status:', error);
|
|
119
|
+
// Stop polling on repeated errors to prevent infinite loops
|
|
120
|
+
if (attemptsRef.current >= 3) {
|
|
121
|
+
console.log('Multiple errors encountered, stopping polling');
|
|
122
|
+
stopPolling();
|
|
123
|
+
if (isMountedRef.current && onFailure) {
|
|
124
|
+
onFailure('Payment verification failed due to network errors');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Continue polling for occasional errors
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
// Start polling immediately, but check if still mounted
|
|
131
|
+
if (isMountedRef.current && isPollingRef.current) {
|
|
132
|
+
void checkPaymentStatus();
|
|
133
|
+
pollIntervalRef.current = setInterval(() => {
|
|
134
|
+
if (isMountedRef.current && isPollingRef.current) {
|
|
135
|
+
void checkPaymentStatus();
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
stopPolling();
|
|
139
|
+
}
|
|
140
|
+
}, pollInterval);
|
|
141
|
+
}
|
|
142
|
+
// Return control object
|
|
143
|
+
return {
|
|
144
|
+
stop: stopPolling,
|
|
145
|
+
isPolling: () => isPollingRef.current && isMountedRef.current,
|
|
146
|
+
};
|
|
147
|
+
}, [paymentsResource, stopPolling]);
|
|
148
|
+
return {
|
|
149
|
+
startPolling,
|
|
150
|
+
stopPolling,
|
|
151
|
+
isPolling: () => isPollingRef.current && isMountedRef.current,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -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,283 @@
|
|
|
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
|
+
console.error('BasisTheory initialization error:', btError);
|
|
43
|
+
setError('Failed to initialize payment processor: ' + btError.message);
|
|
44
|
+
}
|
|
45
|
+
else if (basisTheory && !error) {
|
|
46
|
+
console.log('✅ BasisTheory initialized successfully');
|
|
47
|
+
setError(null); // Clear any previous errors
|
|
48
|
+
}
|
|
49
|
+
}, [basisTheory, btError]); // Removed error from dependency to prevent loops
|
|
50
|
+
// Clean up polling when component unmounts
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
return () => {
|
|
53
|
+
stopPolling();
|
|
54
|
+
};
|
|
55
|
+
}, [stopPolling]);
|
|
56
|
+
// Handle payment actions (3DS, redirects, etc.) - matches old implementation
|
|
57
|
+
const handlePaymentAction = useCallback(async (payment, options = {}) => {
|
|
58
|
+
if (payment.requireAction === 'none')
|
|
59
|
+
return;
|
|
60
|
+
if (payment?.requireActionData?.processed)
|
|
61
|
+
return;
|
|
62
|
+
const actionData = payment.requireActionData;
|
|
63
|
+
if (!actionData)
|
|
64
|
+
return;
|
|
65
|
+
// Mark action as processed
|
|
66
|
+
try {
|
|
67
|
+
await paymentsResource.markPaymentActionProcessed(payment.id);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error('Error setting payment action as processed', error);
|
|
71
|
+
}
|
|
72
|
+
console.log('Processing payment action:', actionData.type);
|
|
73
|
+
switch (actionData.type) {
|
|
74
|
+
case 'threeds_auth':
|
|
75
|
+
if (actionData.metadata?.threedsSession && !challengeInProgressRef.current) {
|
|
76
|
+
try {
|
|
77
|
+
challengeInProgressRef.current = true;
|
|
78
|
+
console.log('Starting 3DS challenge...');
|
|
79
|
+
await startChallenge({
|
|
80
|
+
sessionId: actionData.metadata.threedsSession.externalSessionId,
|
|
81
|
+
acsChallengeUrl: actionData.metadata.threedsSession.acsChallengeUrl,
|
|
82
|
+
acsTransactionId: actionData.metadata.threedsSession.acsTransID,
|
|
83
|
+
threeDSVersion: actionData.metadata.threedsSession.messageVersion,
|
|
84
|
+
}, { provider: options.threedsProvider || 'basis_theory' });
|
|
85
|
+
challengeInProgressRef.current = false;
|
|
86
|
+
console.log('3DS challenge completed');
|
|
87
|
+
// Start polling after challenge completion
|
|
88
|
+
if (payment.id) {
|
|
89
|
+
startPolling(payment.id, {
|
|
90
|
+
onRequireAction: (updatedPayment) => {
|
|
91
|
+
void handlePaymentAction(updatedPayment, options);
|
|
92
|
+
},
|
|
93
|
+
onSuccess: (successPayment) => {
|
|
94
|
+
setIsLoading(false);
|
|
95
|
+
options.onSuccess?.({
|
|
96
|
+
paymentId: successPayment.id,
|
|
97
|
+
payment: successPayment,
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
onFailure: (errorMsg) => {
|
|
101
|
+
console.error('Payment failed:', errorMsg);
|
|
102
|
+
setError(errorMsg);
|
|
103
|
+
setIsLoading(false);
|
|
104
|
+
options.onFailure?.(errorMsg);
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
challengeInProgressRef.current = false;
|
|
111
|
+
console.error('Error starting 3DS challenge:', error);
|
|
112
|
+
const errorMsg = error instanceof Error ? error.message : 'Failed to start 3DS challenge';
|
|
113
|
+
setError(errorMsg);
|
|
114
|
+
setIsLoading(false);
|
|
115
|
+
options.onFailure?.(errorMsg);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
case 'processor_auth':
|
|
120
|
+
case 'redirect': {
|
|
121
|
+
if (actionData.metadata?.redirect?.redirectUrl) {
|
|
122
|
+
window.location.href = actionData.metadata.redirect.redirectUrl;
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case 'error': {
|
|
127
|
+
const errorMsg = actionData.message || 'Payment processing failed';
|
|
128
|
+
setError(errorMsg);
|
|
129
|
+
setIsLoading(false);
|
|
130
|
+
options.onFailure?.(errorMsg);
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
options.onRequireAction?.(payment);
|
|
135
|
+
}, [paymentsResource, startPolling, startChallenge]);
|
|
136
|
+
// Create card payment instrument - matches old implementation
|
|
137
|
+
const createCardPaymentInstrument = useCallback((cardData) => {
|
|
138
|
+
return paymentsResource.createCardPaymentInstrument(basisTheory, cardData);
|
|
139
|
+
}, [basisTheory, paymentsResource]);
|
|
140
|
+
// Create Apple Pay payment instrument - matches old implementation
|
|
141
|
+
const createApplePayPaymentInstrument = useCallback((applePayToken) => {
|
|
142
|
+
return paymentsResource.createApplePayPaymentInstrument(basisTheory, applePayToken);
|
|
143
|
+
}, [basisTheory, paymentsResource]);
|
|
144
|
+
// Process payment directly with checkout session - matches old implementation
|
|
145
|
+
const processPaymentDirect = useCallback(async (checkoutSessionId, paymentInstrumentId, threedsSessionId, options = {}) => {
|
|
146
|
+
try {
|
|
147
|
+
const response = await paymentsResource.processPaymentDirect(checkoutSessionId, paymentInstrumentId, threedsSessionId, {
|
|
148
|
+
initiatedBy: options.initiatedBy,
|
|
149
|
+
source: options.source,
|
|
150
|
+
});
|
|
151
|
+
console.log('Payment response:', response);
|
|
152
|
+
setCurrentPaymentId(response.payment?.id);
|
|
153
|
+
if (response.payment.requireAction !== 'none') {
|
|
154
|
+
await handlePaymentAction(response.payment, options);
|
|
155
|
+
}
|
|
156
|
+
else if (response.payment.status === 'succeeded') {
|
|
157
|
+
setIsLoading(false);
|
|
158
|
+
options.onSuccess?.(response);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
// Start polling for payment status
|
|
162
|
+
startPolling(response.payment?.id, {
|
|
163
|
+
onRequireAction: (payment) => {
|
|
164
|
+
void handlePaymentAction(payment, options);
|
|
165
|
+
},
|
|
166
|
+
onSuccess: (payment) => {
|
|
167
|
+
setIsLoading(false);
|
|
168
|
+
options.onSuccess?.({
|
|
169
|
+
paymentId: payment.id,
|
|
170
|
+
payment,
|
|
171
|
+
});
|
|
172
|
+
},
|
|
173
|
+
onFailure: (errorMsg) => {
|
|
174
|
+
console.error('Payment failed:', errorMsg);
|
|
175
|
+
setError(errorMsg);
|
|
176
|
+
setIsLoading(false);
|
|
177
|
+
options.onFailure?.(errorMsg);
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return response;
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
const errorMsg = error instanceof Error ? error.message : 'Payment failed';
|
|
185
|
+
setError(errorMsg);
|
|
186
|
+
setIsLoading(false);
|
|
187
|
+
options.onFailure?.(errorMsg);
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
}, [paymentsResource, handlePaymentAction, startPolling]);
|
|
191
|
+
// Process card payment - matches old implementation
|
|
192
|
+
const processCardPayment = useCallback(async (checkoutSessionId, cardData, options = {}) => {
|
|
193
|
+
setIsLoading(true);
|
|
194
|
+
setError(null);
|
|
195
|
+
try {
|
|
196
|
+
// 1. Create payment instrument
|
|
197
|
+
const paymentInstrument = await createCardPaymentInstrument(cardData);
|
|
198
|
+
// 2. Create 3DS session if enabled
|
|
199
|
+
let threedsSessionId;
|
|
200
|
+
if (options.enableThreeds !== false) {
|
|
201
|
+
try {
|
|
202
|
+
const threedsSession = await createSession(paymentInstrument, {
|
|
203
|
+
provider: options.threedsProvider || 'basis_theory',
|
|
204
|
+
});
|
|
205
|
+
threedsSessionId = threedsSession.id;
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
console.warn('Failed to create 3DS session, proceeding without 3DS:', error);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// 3. Process payment directly
|
|
212
|
+
return await processPaymentDirect(checkoutSessionId, paymentInstrument.id, threedsSessionId, options);
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
setIsLoading(false);
|
|
216
|
+
const errorMsg = error instanceof Error ? error.message : 'Payment failed';
|
|
217
|
+
setError(errorMsg);
|
|
218
|
+
options.onFailure?.(errorMsg);
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
}, [createCardPaymentInstrument, createSession, processPaymentDirect]);
|
|
222
|
+
// Process Apple Pay payment - matches old implementation
|
|
223
|
+
const processApplePayPayment = useCallback(async (checkoutSessionId, applePayToken, options = {}) => {
|
|
224
|
+
setIsLoading(true);
|
|
225
|
+
setError(null);
|
|
226
|
+
try {
|
|
227
|
+
// 1. Create payment instrument
|
|
228
|
+
const paymentInstrument = await createApplePayPaymentInstrument(applePayToken);
|
|
229
|
+
// 2. Process payment directly (Apple Pay typically doesn't require 3DS)
|
|
230
|
+
return await processPaymentDirect(checkoutSessionId, paymentInstrument.id, undefined, options);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
setIsLoading(false);
|
|
234
|
+
const errorMsg = error instanceof Error ? error.message : 'Apple Pay payment failed';
|
|
235
|
+
setError(errorMsg);
|
|
236
|
+
options.onFailure?.(errorMsg);
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
239
|
+
}, [createApplePayPaymentInstrument, processPaymentDirect]);
|
|
240
|
+
// Process payment with existing instrument - matches old implementation
|
|
241
|
+
const processPaymentWithInstrument = useCallback(async (checkoutSessionId, paymentInstrumentId, options = {}) => {
|
|
242
|
+
setIsLoading(true);
|
|
243
|
+
setError(null);
|
|
244
|
+
try {
|
|
245
|
+
return await processPaymentDirect(checkoutSessionId, paymentInstrumentId, undefined, options);
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
setIsLoading(false);
|
|
249
|
+
const errorMsg = error instanceof Error ? error.message : 'Payment failed';
|
|
250
|
+
setError(errorMsg);
|
|
251
|
+
options.onFailure?.(errorMsg);
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
}, [processPaymentDirect]);
|
|
255
|
+
// Get card payment instruments - matches old implementation
|
|
256
|
+
const getCardPaymentInstruments = useCallback(async () => {
|
|
257
|
+
try {
|
|
258
|
+
const response = await paymentsResource.getCardPaymentInstruments();
|
|
259
|
+
return response;
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
console.error('Error fetching card payment instruments:', error);
|
|
263
|
+
const errorMsg = error instanceof Error ? error.message : 'Failed to fetch payment instruments';
|
|
264
|
+
setError(errorMsg);
|
|
265
|
+
throw error;
|
|
266
|
+
}
|
|
267
|
+
}, [paymentsResource]);
|
|
268
|
+
const clearError = useCallback(() => {
|
|
269
|
+
setError(null);
|
|
270
|
+
}, []);
|
|
271
|
+
return {
|
|
272
|
+
processCardPayment,
|
|
273
|
+
processApplePayPayment,
|
|
274
|
+
processPaymentWithInstrument,
|
|
275
|
+
createCardPaymentInstrument,
|
|
276
|
+
createApplePayPaymentInstrument,
|
|
277
|
+
getCardPaymentInstruments,
|
|
278
|
+
isLoading: isLoading || !basisTheory, // Indicate loading if BasisTheory is not initialized
|
|
279
|
+
error,
|
|
280
|
+
clearError,
|
|
281
|
+
currentPaymentId,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
@@ -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,36 @@
|
|
|
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
|
+
console.log('🔍 [usePluginConfig] Config:', config);
|
|
29
|
+
return {
|
|
30
|
+
config,
|
|
31
|
+
storeId: config.storeId,
|
|
32
|
+
accountId: config.accountId,
|
|
33
|
+
basePath: config.basePath,
|
|
34
|
+
isValid,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -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;
|