@tagadapay/plugin-sdk 1.0.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/README.md +475 -0
- package/dist/data/currencies.json +2410 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +37 -0
- package/dist/react/components/DebugDrawer.d.ts +7 -0
- package/dist/react/components/DebugDrawer.js +368 -0
- package/dist/react/components/OffersDemo.d.ts +1 -0
- package/dist/react/components/OffersDemo.js +50 -0
- package/dist/react/components/index.d.ts +1 -0
- package/dist/react/components/index.js +1 -0
- package/dist/react/config/environment.d.ts +22 -0
- package/dist/react/config/environment.js +132 -0
- package/dist/react/config/payment.d.ts +23 -0
- package/dist/react/config/payment.js +52 -0
- package/dist/react/hooks/useAuth.d.ts +4 -0
- package/dist/react/hooks/useAuth.js +12 -0
- package/dist/react/hooks/useCheckout.d.ts +262 -0
- package/dist/react/hooks/useCheckout.js +325 -0
- package/dist/react/hooks/useCurrency.d.ts +4 -0
- package/dist/react/hooks/useCurrency.js +640 -0
- package/dist/react/hooks/useCustomer.d.ts +7 -0
- package/dist/react/hooks/useCustomer.js +14 -0
- package/dist/react/hooks/useEnvironment.d.ts +7 -0
- package/dist/react/hooks/useEnvironment.js +18 -0
- package/dist/react/hooks/useLocale.d.ts +2 -0
- package/dist/react/hooks/useLocale.js +43 -0
- package/dist/react/hooks/useOffers.d.ts +99 -0
- package/dist/react/hooks/useOffers.js +115 -0
- package/dist/react/hooks/useOrder.d.ts +44 -0
- package/dist/react/hooks/useOrder.js +77 -0
- package/dist/react/hooks/usePayment.d.ts +60 -0
- package/dist/react/hooks/usePayment.js +343 -0
- package/dist/react/hooks/usePaymentPolling.d.ts +45 -0
- package/dist/react/hooks/usePaymentPolling.js +146 -0
- package/dist/react/hooks/useProducts.d.ts +95 -0
- package/dist/react/hooks/useProducts.js +120 -0
- package/dist/react/hooks/useSession.d.ts +10 -0
- package/dist/react/hooks/useSession.js +17 -0
- package/dist/react/hooks/useThreeds.d.ts +38 -0
- package/dist/react/hooks/useThreeds.js +162 -0
- package/dist/react/hooks/useThreedsModal.d.ts +16 -0
- package/dist/react/hooks/useThreedsModal.js +328 -0
- package/dist/react/index.d.ts +26 -0
- package/dist/react/index.js +27 -0
- package/dist/react/providers/TagadaProvider.d.ts +55 -0
- package/dist/react/providers/TagadaProvider.js +471 -0
- package/dist/react/services/apiService.d.ts +149 -0
- package/dist/react/services/apiService.js +168 -0
- package/dist/react/types.d.ts +151 -0
- package/dist/react/types.js +4 -0
- package/dist/react/utils/__tests__/urlUtils.test.d.ts +1 -0
- package/dist/react/utils/__tests__/urlUtils.test.js +189 -0
- package/dist/react/utils/deviceInfo.d.ts +39 -0
- package/dist/react/utils/deviceInfo.js +163 -0
- package/dist/react/utils/jwtDecoder.d.ts +14 -0
- package/dist/react/utils/jwtDecoder.js +86 -0
- package/dist/react/utils/money.d.ts +2273 -0
- package/dist/react/utils/money.js +104 -0
- package/dist/react/utils/tokenStorage.d.ts +16 -0
- package/dist/react/utils/tokenStorage.js +52 -0
- package/dist/react/utils/urlUtils.d.ts +239 -0
- package/dist/react/utils/urlUtils.js +449 -0
- package/package.json +64 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { useState, useCallback, useEffect, useMemo } from 'react';
|
|
2
|
+
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
3
|
+
export function useProducts(options = {}) {
|
|
4
|
+
const { apiService } = useTagadaContext();
|
|
5
|
+
const [products, setProducts] = useState([]);
|
|
6
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
7
|
+
const [error, setError] = useState(null);
|
|
8
|
+
// Auto-memoize all options to prevent unnecessary re-renders
|
|
9
|
+
const stableOptions = useMemo(() => {
|
|
10
|
+
const { productIds = [], enabled = true, includeVariants = true, includePrices = true } = options;
|
|
11
|
+
return {
|
|
12
|
+
productIds: [...productIds], // Shallow copy to ensure stable reference
|
|
13
|
+
enabled: enabled ?? true,
|
|
14
|
+
includeVariants: includeVariants ?? true,
|
|
15
|
+
includePrices: includePrices ?? true,
|
|
16
|
+
};
|
|
17
|
+
}, [
|
|
18
|
+
JSON.stringify(options.productIds ?? []),
|
|
19
|
+
options.enabled,
|
|
20
|
+
options.includeVariants,
|
|
21
|
+
options.includePrices,
|
|
22
|
+
]);
|
|
23
|
+
const { productIds, enabled, includeVariants, includePrices } = stableOptions;
|
|
24
|
+
const fetchProducts = useCallback(async () => {
|
|
25
|
+
if (!enabled)
|
|
26
|
+
return;
|
|
27
|
+
setIsLoading(true);
|
|
28
|
+
setError(null);
|
|
29
|
+
try {
|
|
30
|
+
const storeId = apiService.getStoredStoreId();
|
|
31
|
+
if (!storeId) {
|
|
32
|
+
throw new Error('Store ID not found. Make sure the TagadaProvider is properly configured.');
|
|
33
|
+
}
|
|
34
|
+
// Use apiService.fetch instead of direct fetch to avoid dependency on environment.apiConfig.baseUrl
|
|
35
|
+
if (productIds.length > 0) {
|
|
36
|
+
// Fetch specific products
|
|
37
|
+
const fetchPromises = productIds.map(async (productId) => {
|
|
38
|
+
return apiService.fetch(`/api/v1/products/${productId}`, {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
body: {
|
|
41
|
+
productId,
|
|
42
|
+
storeId,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
const fetchedProducts = await Promise.all(fetchPromises);
|
|
47
|
+
setProducts(fetchedProducts);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// Fetch all products for the store
|
|
51
|
+
const data = await apiService.fetch('/api/v1/products', {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
body: {
|
|
54
|
+
storeId,
|
|
55
|
+
includeVariants,
|
|
56
|
+
includePrices,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
setProducts(Array.isArray(data) ? data : (data.items ?? []));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
const error = err instanceof Error ? err : new Error('Failed to fetch products');
|
|
64
|
+
setError(error);
|
|
65
|
+
console.error('Error fetching products:', error);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
setIsLoading(false);
|
|
69
|
+
}
|
|
70
|
+
}, [apiService, productIds, enabled, includeVariants, includePrices]);
|
|
71
|
+
const getProduct = useCallback((productId) => {
|
|
72
|
+
return products.find((product) => product.id === productId);
|
|
73
|
+
}, [products]);
|
|
74
|
+
const getVariant = useCallback((variantId) => {
|
|
75
|
+
for (const product of products) {
|
|
76
|
+
const variant = product.variants?.find((v) => v.id === variantId);
|
|
77
|
+
if (variant) {
|
|
78
|
+
return { product, variant };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}, [products]);
|
|
83
|
+
const getAllVariants = useCallback(() => {
|
|
84
|
+
const allVariants = [];
|
|
85
|
+
products.forEach((product) => {
|
|
86
|
+
if (product.variants) {
|
|
87
|
+
product.variants.forEach((variant) => {
|
|
88
|
+
allVariants.push({ product, variant });
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
return allVariants;
|
|
93
|
+
}, [products]);
|
|
94
|
+
const filterVariants = useCallback((predicate) => {
|
|
95
|
+
const filteredVariants = [];
|
|
96
|
+
products.forEach((product) => {
|
|
97
|
+
if (product.variants) {
|
|
98
|
+
product.variants.forEach((variant) => {
|
|
99
|
+
if (predicate(variant, product)) {
|
|
100
|
+
filteredVariants.push({ product, variant });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return filteredVariants;
|
|
106
|
+
}, [products]);
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
void fetchProducts();
|
|
109
|
+
}, [fetchProducts]);
|
|
110
|
+
return {
|
|
111
|
+
products,
|
|
112
|
+
isLoading,
|
|
113
|
+
error,
|
|
114
|
+
refetch: fetchProducts,
|
|
115
|
+
getProduct,
|
|
116
|
+
getVariant,
|
|
117
|
+
getAllVariants,
|
|
118
|
+
filterVariants,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Session } from '../types';
|
|
2
|
+
export declare function useSession(): {
|
|
3
|
+
session: Session | null;
|
|
4
|
+
sessionId: string | null;
|
|
5
|
+
storeId: string | null;
|
|
6
|
+
accountId: string | null;
|
|
7
|
+
customerId: string | null;
|
|
8
|
+
isValid: boolean;
|
|
9
|
+
isLoading: boolean;
|
|
10
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
/**
|
|
3
|
+
* useSession - Hook to access current session data
|
|
4
|
+
*/
|
|
5
|
+
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
6
|
+
export function useSession() {
|
|
7
|
+
const { session, isLoading } = useTagadaContext();
|
|
8
|
+
return {
|
|
9
|
+
session,
|
|
10
|
+
sessionId: session?.sessionId || null,
|
|
11
|
+
storeId: session?.storeId || null,
|
|
12
|
+
accountId: session?.accountId || null,
|
|
13
|
+
customerId: session?.customerId || null,
|
|
14
|
+
isValid: session?.isValid || false,
|
|
15
|
+
isLoading,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type ThreedsProvider = 'basis_theory';
|
|
2
|
+
export interface PaymentInstrument {
|
|
3
|
+
id: string;
|
|
4
|
+
token: string | null;
|
|
5
|
+
type: string;
|
|
6
|
+
card: {
|
|
7
|
+
maskedCardNumber?: string;
|
|
8
|
+
expirationMonth?: number;
|
|
9
|
+
expirationYear?: number;
|
|
10
|
+
brand?: string;
|
|
11
|
+
bin?: string;
|
|
12
|
+
last4?: string;
|
|
13
|
+
} | null;
|
|
14
|
+
}
|
|
15
|
+
export interface ThreedsSession {
|
|
16
|
+
id: string;
|
|
17
|
+
provider: ThreedsProvider;
|
|
18
|
+
status: string;
|
|
19
|
+
sessionData: any;
|
|
20
|
+
}
|
|
21
|
+
export interface ThreedsChallenge {
|
|
22
|
+
sessionId: string;
|
|
23
|
+
acsChallengeUrl: string;
|
|
24
|
+
acsTransactionId: string;
|
|
25
|
+
threeDSVersion: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ThreedsOptions {
|
|
28
|
+
provider?: ThreedsProvider;
|
|
29
|
+
}
|
|
30
|
+
export interface ThreedsHook {
|
|
31
|
+
createSession: (paymentInstrument: PaymentInstrument, options?: ThreedsOptions) => Promise<ThreedsSession>;
|
|
32
|
+
startChallenge: (challengeData: ThreedsChallenge, options?: ThreedsOptions) => Promise<any>;
|
|
33
|
+
isLoading: boolean;
|
|
34
|
+
error: Error | null;
|
|
35
|
+
}
|
|
36
|
+
export declare function useThreeds(options?: {
|
|
37
|
+
defaultProvider?: ThreedsProvider;
|
|
38
|
+
}): ThreedsHook;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
3
|
+
import { getBasisTheoryApiKey } from '../config/payment';
|
|
4
|
+
import { useThreedsModal } from './useThreedsModal';
|
|
5
|
+
export function useThreeds(options = {}) {
|
|
6
|
+
const { defaultProvider = 'basis_theory' } = options;
|
|
7
|
+
const { apiService, environment } = useTagadaContext();
|
|
8
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
9
|
+
const [error, setError] = useState(null);
|
|
10
|
+
const { createThreedsModal, closeThreedsModal } = useThreedsModal();
|
|
11
|
+
const [basisTheory3ds, setBasisTheory3ds] = useState(null);
|
|
12
|
+
// Dynamically import BasisTheory3ds on the client side only
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
let isMounted = true;
|
|
15
|
+
const loadBasisTheory = async () => {
|
|
16
|
+
try {
|
|
17
|
+
// Get API key from embedded configuration (with env override support)
|
|
18
|
+
const apiKey = getBasisTheoryApiKey(environment?.environment || 'local');
|
|
19
|
+
if (!apiKey) {
|
|
20
|
+
console.warn('BasisTheory API key not configured');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
// Dynamically import the library only on the client side
|
|
24
|
+
const { BasisTheory3ds } = await import('@basis-theory/web-threeds');
|
|
25
|
+
if (isMounted) {
|
|
26
|
+
setBasisTheory3ds(() => BasisTheory3ds);
|
|
27
|
+
console.log('✅ BasisTheory3ds initialized successfully');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
console.error('Failed to load BasisTheory3ds:', err);
|
|
32
|
+
if (isMounted) {
|
|
33
|
+
setError(err instanceof Error ? err : new Error('Failed to load 3DS library'));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
void loadBasisTheory();
|
|
38
|
+
return () => {
|
|
39
|
+
isMounted = false;
|
|
40
|
+
};
|
|
41
|
+
}, [environment]);
|
|
42
|
+
// Create a 3DS session with BasisTheory
|
|
43
|
+
const createBasisTheorySession = useCallback(async (paymentInstrument) => {
|
|
44
|
+
try {
|
|
45
|
+
if (!basisTheory3ds) {
|
|
46
|
+
throw new Error('BasisTheory3ds not loaded yet');
|
|
47
|
+
}
|
|
48
|
+
// Get API key from embedded configuration
|
|
49
|
+
const apiKey = getBasisTheoryApiKey(environment?.environment || 'local');
|
|
50
|
+
if (!apiKey) {
|
|
51
|
+
throw new Error('BasisTheory API key not configured');
|
|
52
|
+
}
|
|
53
|
+
const bt3ds = basisTheory3ds(apiKey);
|
|
54
|
+
console.log('Creating BasisTheory 3DS session for payment instrument:', paymentInstrument.id);
|
|
55
|
+
const session = await bt3ds.createSession({
|
|
56
|
+
tokenId: paymentInstrument.token,
|
|
57
|
+
});
|
|
58
|
+
// Create session through our API
|
|
59
|
+
const threedsSession = await apiService.fetch('/api/v1/threeds/create-session', {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
body: {
|
|
62
|
+
provider: 'basis_theory',
|
|
63
|
+
sessionData: session,
|
|
64
|
+
paymentInstrumentId: paymentInstrument.id,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
return threedsSession;
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error('Error creating BasisTheory 3DS session:', error);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}, [apiService, basisTheory3ds, environment]);
|
|
74
|
+
// Generic createSession method that supports multiple providers
|
|
75
|
+
const createSession = useCallback(async (paymentInstrument, options) => {
|
|
76
|
+
const provider = options?.provider || defaultProvider;
|
|
77
|
+
setIsLoading(true);
|
|
78
|
+
setError(null);
|
|
79
|
+
try {
|
|
80
|
+
let session;
|
|
81
|
+
if (provider === 'basis_theory') {
|
|
82
|
+
session = await createBasisTheorySession(paymentInstrument);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
throw new Error(`Unsupported 3DS provider: ${String(provider)}`);
|
|
86
|
+
}
|
|
87
|
+
return session;
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
const error = err instanceof Error ? err : new Error('Failed to create 3DS session');
|
|
91
|
+
setError(error);
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
setIsLoading(false);
|
|
96
|
+
}
|
|
97
|
+
}, [defaultProvider, createBasisTheorySession]);
|
|
98
|
+
// Start a 3DS challenge with BasisTheory
|
|
99
|
+
const startBasisTheoryChallenge = useCallback(async (challengeData) => {
|
|
100
|
+
try {
|
|
101
|
+
if (!basisTheory3ds) {
|
|
102
|
+
throw new Error('BasisTheory3ds not loaded yet');
|
|
103
|
+
}
|
|
104
|
+
// Get API key from embedded configuration
|
|
105
|
+
const apiKey = getBasisTheoryApiKey(environment?.environment || 'local');
|
|
106
|
+
if (!apiKey) {
|
|
107
|
+
throw new Error('BasisTheory API key is not configured');
|
|
108
|
+
}
|
|
109
|
+
const modal = createThreedsModal({
|
|
110
|
+
onClose: () => {
|
|
111
|
+
throw new Error('Authentication was cancelled by the user');
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
const bt3ds = basisTheory3ds(apiKey);
|
|
115
|
+
console.log('Starting BasisTheory 3DS challenge:', challengeData);
|
|
116
|
+
const challengeCompletion = await bt3ds.startChallenge({
|
|
117
|
+
sessionId: challengeData.sessionId,
|
|
118
|
+
acsChallengeUrl: challengeData.acsChallengeUrl,
|
|
119
|
+
acsTransactionId: challengeData.acsTransactionId,
|
|
120
|
+
threeDSVersion: challengeData.threeDSVersion,
|
|
121
|
+
containerId: modal.containerId + '-content',
|
|
122
|
+
mode: 'iframe',
|
|
123
|
+
timeout: 60000 * 3, // 3 minutes
|
|
124
|
+
});
|
|
125
|
+
closeThreedsModal();
|
|
126
|
+
return challengeCompletion;
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
console.error('Error starting BasisTheory 3DS challenge:', error);
|
|
130
|
+
closeThreedsModal();
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}, [basisTheory3ds, createThreedsModal, closeThreedsModal, environment]);
|
|
134
|
+
// Generic startChallenge method that supports multiple providers
|
|
135
|
+
const startChallenge = useCallback(async (challengeData, options) => {
|
|
136
|
+
const provider = options?.provider || defaultProvider;
|
|
137
|
+
setIsLoading(true);
|
|
138
|
+
setError(null);
|
|
139
|
+
try {
|
|
140
|
+
if (provider === 'basis_theory') {
|
|
141
|
+
return await startBasisTheoryChallenge(challengeData);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
throw new Error(`Unsupported 3DS provider: ${String(provider)}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
const error = err instanceof Error ? err : new Error('Failed to start 3DS challenge');
|
|
149
|
+
setError(error);
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
setIsLoading(false);
|
|
154
|
+
}
|
|
155
|
+
}, [defaultProvider, startBasisTheoryChallenge]);
|
|
156
|
+
return {
|
|
157
|
+
createSession,
|
|
158
|
+
startChallenge,
|
|
159
|
+
isLoading,
|
|
160
|
+
error,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface ThreedsModalOptions {
|
|
2
|
+
containerId?: string;
|
|
3
|
+
mode?: 'fixed' | 'auto-fit';
|
|
4
|
+
onClose?: () => void;
|
|
5
|
+
}
|
|
6
|
+
export interface ThreedsModalInstance {
|
|
7
|
+
containerId: string;
|
|
8
|
+
getContentElement: () => HTMLElement | null;
|
|
9
|
+
updateModalSize: () => void;
|
|
10
|
+
cleanup: () => void;
|
|
11
|
+
}
|
|
12
|
+
export interface ThreedsModalHook {
|
|
13
|
+
createThreedsModal: (options?: ThreedsModalOptions) => ThreedsModalInstance;
|
|
14
|
+
closeThreedsModal: (containerId?: string) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function useThreedsModal(): ThreedsModalHook;
|