@tagadapay/plugin-sdk 1.0.29 → 2.0.1
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 +150 -18
- package/dist/data/iso3166.d.ts +30 -0
- package/dist/data/iso3166.js +102 -0
- package/dist/react/hooks/useAddressV2.d.ts +53 -0
- package/dist/react/hooks/useAddressV2.js +379 -0
- package/dist/react/hooks/useGoogleAutocomplete.d.ts +69 -0
- package/dist/react/hooks/useGoogleAutocomplete.js +219 -0
- package/dist/react/hooks/useISOData.d.ts +41 -0
- package/dist/react/hooks/useISOData.js +127 -0
- package/dist/react/hooks/useLogin.d.ts +20 -0
- package/dist/react/hooks/useLogin.js +75 -0
- package/dist/react/hooks/usePayment.d.ts +43 -0
- package/dist/react/hooks/usePayment.js +16 -2
- package/dist/react/hooks/usePluginConfig.d.ts +53 -0
- package/dist/react/hooks/usePluginConfig.js +190 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.js +7 -0
- package/dist/react/providers/TagadaProvider.d.ts +5 -1
- package/dist/react/providers/TagadaProvider.js +48 -2
- package/package.json +2 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface ISOCountry {
|
|
2
|
+
iso: string;
|
|
3
|
+
iso3: string;
|
|
4
|
+
numeric: number;
|
|
5
|
+
name: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ISORegion {
|
|
8
|
+
iso: string;
|
|
9
|
+
name: string;
|
|
10
|
+
}
|
|
11
|
+
export interface UseISODataResult {
|
|
12
|
+
countries: Record<string, ISOCountry>;
|
|
13
|
+
getRegions: (countryCode: string) => ISORegion[];
|
|
14
|
+
findRegion: (countryCode: string, regionCode: string) => ISORegion | null;
|
|
15
|
+
mapGoogleToISO: (googleState: string, googleStateLong: string, countryCode: string) => ISORegion | null;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* React hook for accessing ISO3166 countries and regions data
|
|
19
|
+
* @param language - Language code (en, fr, de, es, etc.)
|
|
20
|
+
* @param disputeSetting - Territorial dispute perspective (UN, RU, UA, TR)
|
|
21
|
+
* @returns Object with countries data and helper functions
|
|
22
|
+
*/
|
|
23
|
+
export declare function useISOData(language?: string, disputeSetting?: string): UseISODataResult;
|
|
24
|
+
/**
|
|
25
|
+
* Get available languages for ISO data
|
|
26
|
+
*/
|
|
27
|
+
export declare function getAvailableLanguages(): string[];
|
|
28
|
+
/**
|
|
29
|
+
* Get list of countries as options for select components
|
|
30
|
+
*/
|
|
31
|
+
export declare function useCountryOptions(language?: string): {
|
|
32
|
+
value: string;
|
|
33
|
+
label: string;
|
|
34
|
+
}[];
|
|
35
|
+
/**
|
|
36
|
+
* Get list of regions/states for a country as options for select components
|
|
37
|
+
*/
|
|
38
|
+
export declare function useRegionOptions(countryCode: string, language?: string): {
|
|
39
|
+
value: string;
|
|
40
|
+
label: string;
|
|
41
|
+
}[];
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
3
|
+
// @ts-ignore - iso3166-2-db doesn't have TypeScript definitions
|
|
4
|
+
import { getDataSet, reduce } from 'iso3166-2-db';
|
|
5
|
+
/**
|
|
6
|
+
* React hook for accessing ISO3166 countries and regions data
|
|
7
|
+
* @param language - Language code (en, fr, de, es, etc.)
|
|
8
|
+
* @param disputeSetting - Territorial dispute perspective (UN, RU, UA, TR)
|
|
9
|
+
* @returns Object with countries data and helper functions
|
|
10
|
+
*/
|
|
11
|
+
export function useISOData(language = 'en', disputeSetting = 'UN') {
|
|
12
|
+
const data = useMemo(() => {
|
|
13
|
+
try {
|
|
14
|
+
// Get the dataset using the iso3166-2-db library
|
|
15
|
+
const worldDatabase = reduce(getDataSet(), language);
|
|
16
|
+
// Transform to our expected format
|
|
17
|
+
const countries = {};
|
|
18
|
+
Object.keys(worldDatabase).forEach((countryCode) => {
|
|
19
|
+
const countryData = worldDatabase[countryCode];
|
|
20
|
+
countries[countryData.iso] = {
|
|
21
|
+
iso: countryData.iso,
|
|
22
|
+
iso3: countryData.iso3,
|
|
23
|
+
numeric: countryData.numeric,
|
|
24
|
+
name: countryData.name,
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
// Helper to load regions for a specific country
|
|
28
|
+
const getRegions = (countryCode) => {
|
|
29
|
+
try {
|
|
30
|
+
const countryData = worldDatabase[countryCode];
|
|
31
|
+
if (!countryData?.regions) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
return Object.keys(countryData.regions).map((regionCode) => {
|
|
35
|
+
const regionData = countryData.regions[regionCode];
|
|
36
|
+
return {
|
|
37
|
+
iso: regionData.iso,
|
|
38
|
+
name: regionData.name,
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return []; // Return empty array if no regions
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
// Find a specific region by ISO code
|
|
47
|
+
const findRegion = (countryCode, regionCode) => {
|
|
48
|
+
const regions = getRegions(countryCode);
|
|
49
|
+
return regions.find((region) => region.iso === regionCode) ?? null;
|
|
50
|
+
};
|
|
51
|
+
// Map Google Places state to ISO region (proven 100% success rate)
|
|
52
|
+
const mapGoogleToISO = (googleState, googleStateLong, countryCode) => {
|
|
53
|
+
const regions = getRegions(countryCode);
|
|
54
|
+
if (regions.length === 0)
|
|
55
|
+
return null;
|
|
56
|
+
// Strategy 1: Exact ISO code match (86% success rate)
|
|
57
|
+
let match = regions.find((r) => r.iso === googleState);
|
|
58
|
+
if (match)
|
|
59
|
+
return match;
|
|
60
|
+
// Strategy 2: Name matching (14% success rate)
|
|
61
|
+
match = regions.find((r) => r.name.toLowerCase() === googleState.toLowerCase());
|
|
62
|
+
if (match)
|
|
63
|
+
return match;
|
|
64
|
+
match = regions.find((r) => r.name.toLowerCase() === googleStateLong.toLowerCase());
|
|
65
|
+
if (match)
|
|
66
|
+
return match;
|
|
67
|
+
// Strategy 3: Partial name matching (fallback)
|
|
68
|
+
match = regions.find((r) => r.name.toLowerCase().includes(googleStateLong.toLowerCase()) ||
|
|
69
|
+
googleStateLong.toLowerCase().includes(r.name.toLowerCase()));
|
|
70
|
+
return match ?? null;
|
|
71
|
+
};
|
|
72
|
+
return {
|
|
73
|
+
countries,
|
|
74
|
+
getRegions,
|
|
75
|
+
findRegion,
|
|
76
|
+
mapGoogleToISO,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
console.error(`Failed to load ISO data for language: ${language}`, error);
|
|
81
|
+
return {
|
|
82
|
+
countries: {},
|
|
83
|
+
getRegions: () => [],
|
|
84
|
+
findRegion: () => null,
|
|
85
|
+
mapGoogleToISO: () => null,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}, [language, disputeSetting]);
|
|
89
|
+
return data;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get available languages for ISO data
|
|
93
|
+
*/
|
|
94
|
+
export function getAvailableLanguages() {
|
|
95
|
+
return ['en', 'fr', 'de', 'es', 'ru', 'zh', 'ar', 'it', 'pt', 'ja', 'hi'];
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get list of countries as options for select components
|
|
99
|
+
*/
|
|
100
|
+
export function useCountryOptions(language = 'en') {
|
|
101
|
+
const { countries } = useISOData(language);
|
|
102
|
+
return useMemo(() => {
|
|
103
|
+
return Object.entries(countries)
|
|
104
|
+
.map(([code, country]) => ({
|
|
105
|
+
value: code,
|
|
106
|
+
label: country.name,
|
|
107
|
+
}))
|
|
108
|
+
.sort((a, b) => a.label.localeCompare(b.label));
|
|
109
|
+
}, [countries]);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get list of regions/states for a country as options for select components
|
|
113
|
+
*/
|
|
114
|
+
export function useRegionOptions(countryCode, language = 'en') {
|
|
115
|
+
const { getRegions } = useISOData(language);
|
|
116
|
+
return useMemo(() => {
|
|
117
|
+
if (!countryCode)
|
|
118
|
+
return [];
|
|
119
|
+
const regions = getRegions(countryCode);
|
|
120
|
+
return regions
|
|
121
|
+
.map((region) => ({
|
|
122
|
+
value: region.iso,
|
|
123
|
+
label: region.name,
|
|
124
|
+
}))
|
|
125
|
+
.sort((a, b) => a.label.localeCompare(b.label));
|
|
126
|
+
}, [countryCode, getRegions]);
|
|
127
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare function useLogin(): {
|
|
2
|
+
error: Error | null;
|
|
3
|
+
isLoading: boolean;
|
|
4
|
+
requestCode: (email: string) => Promise<{
|
|
5
|
+
success: boolean;
|
|
6
|
+
error: string;
|
|
7
|
+
} | {
|
|
8
|
+
success: boolean;
|
|
9
|
+
error?: undefined;
|
|
10
|
+
} | undefined>;
|
|
11
|
+
verifyCode: (email: string, code: string) => Promise<{
|
|
12
|
+
success: boolean;
|
|
13
|
+
error: string;
|
|
14
|
+
} | {
|
|
15
|
+
token: string;
|
|
16
|
+
sessionId: string;
|
|
17
|
+
success: boolean;
|
|
18
|
+
error?: undefined;
|
|
19
|
+
} | undefined>;
|
|
20
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
3
|
+
import { setClientToken } from '../utils/tokenStorage';
|
|
4
|
+
export function useLogin() {
|
|
5
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
6
|
+
const [error, setError] = useState(null);
|
|
7
|
+
const { apiService } = useTagadaContext();
|
|
8
|
+
const requestCode = useCallback(async (email) => {
|
|
9
|
+
setIsLoading(true);
|
|
10
|
+
try {
|
|
11
|
+
const storeId = apiService.getStoredStoreId();
|
|
12
|
+
if (!storeId) {
|
|
13
|
+
throw new Error('Store ID not found. Make sure the TagadaProvider is properly configured.');
|
|
14
|
+
}
|
|
15
|
+
const result = await apiService.fetch('/api/v1/cms/session/request-code', {
|
|
16
|
+
method: 'POST',
|
|
17
|
+
body: { email, storeId },
|
|
18
|
+
skipAuth: true,
|
|
19
|
+
});
|
|
20
|
+
if (!result) {
|
|
21
|
+
return {
|
|
22
|
+
success: false,
|
|
23
|
+
error: 'Failed to send code',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return { success: true };
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
const error = err instanceof Error ? err : new Error('Failed to fetch request code');
|
|
30
|
+
setError(error);
|
|
31
|
+
console.error('[SDK] Failed to fetch request code:', error);
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
setIsLoading(false);
|
|
35
|
+
}
|
|
36
|
+
}, [apiService]);
|
|
37
|
+
const verifyCode = useCallback(async (email, code) => {
|
|
38
|
+
setIsLoading(true);
|
|
39
|
+
try {
|
|
40
|
+
const storeId = apiService.getStoredStoreId();
|
|
41
|
+
if (!storeId) {
|
|
42
|
+
throw new Error('Store ID not found. Make sure the TagadaProvider is properly configured.');
|
|
43
|
+
}
|
|
44
|
+
const result = await apiService.fetch('/api/v1/cms/session/login', {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
body: { email, code, storeId },
|
|
47
|
+
skipAuth: true,
|
|
48
|
+
});
|
|
49
|
+
if (!result) {
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
error: 'Invalid code',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (result.token) {
|
|
56
|
+
setClientToken(result.token);
|
|
57
|
+
}
|
|
58
|
+
return { success: true, ...result };
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
const error = err instanceof Error ? err : new Error('Failed to verify code');
|
|
62
|
+
setError(error);
|
|
63
|
+
console.error('[SDK] Failed to verify code:', error);
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
setIsLoading(false);
|
|
67
|
+
}
|
|
68
|
+
}, [apiService]);
|
|
69
|
+
return {
|
|
70
|
+
error,
|
|
71
|
+
isLoading,
|
|
72
|
+
requestCode,
|
|
73
|
+
verifyCode,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -48,12 +48,55 @@ export interface PaymentInstrumentResponse {
|
|
|
48
48
|
brand?: string;
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
+
export interface PaymentInstrumentListItem {
|
|
52
|
+
id: string;
|
|
53
|
+
type: string;
|
|
54
|
+
customerId: string;
|
|
55
|
+
accountId: string;
|
|
56
|
+
isActive: boolean;
|
|
57
|
+
isDefault: boolean;
|
|
58
|
+
tokenizer: string | null;
|
|
59
|
+
createdAt: Date | null;
|
|
60
|
+
updatedAt: Date | null;
|
|
61
|
+
card?: {
|
|
62
|
+
last4?: string;
|
|
63
|
+
expirationMonth?: number;
|
|
64
|
+
expirationYear?: number;
|
|
65
|
+
brand?: string;
|
|
66
|
+
bin?: string;
|
|
67
|
+
};
|
|
68
|
+
metadata?: any;
|
|
69
|
+
}
|
|
70
|
+
export interface PaymentInstrumentCustomer {
|
|
71
|
+
id: string;
|
|
72
|
+
type: 'card';
|
|
73
|
+
card: {
|
|
74
|
+
last4: string;
|
|
75
|
+
exp_month: number;
|
|
76
|
+
exp_year: number;
|
|
77
|
+
brand: string;
|
|
78
|
+
bin: string;
|
|
79
|
+
};
|
|
80
|
+
us_bank_account: null;
|
|
81
|
+
sepa_debit: null;
|
|
82
|
+
tokenizer: string;
|
|
83
|
+
token: string;
|
|
84
|
+
isDefault: boolean;
|
|
85
|
+
isActive: boolean;
|
|
86
|
+
metadata: {};
|
|
87
|
+
customerId: string;
|
|
88
|
+
accountId: string;
|
|
89
|
+
createdAt: string;
|
|
90
|
+
updatedAt: string;
|
|
91
|
+
}
|
|
92
|
+
export type PaymentInstrumentCustomerResponse = PaymentInstrumentCustomer[];
|
|
51
93
|
export interface PaymentHook {
|
|
52
94
|
processCardPayment: (checkoutSessionId: string, cardData: CardPaymentMethod, options?: PaymentOptions) => Promise<PaymentResponse>;
|
|
53
95
|
processApplePayPayment: (checkoutSessionId: string, applePayToken: ApplePayToken, options?: PaymentOptions) => Promise<PaymentResponse>;
|
|
54
96
|
processPaymentWithInstrument: (checkoutSessionId: string, paymentInstrumentId: string, options?: PaymentOptions) => Promise<PaymentResponse>;
|
|
55
97
|
createCardPaymentInstrument: (cardData: CardPaymentMethod) => Promise<PaymentInstrumentResponse>;
|
|
56
98
|
createApplePayPaymentInstrument: (applePayToken: ApplePayToken) => Promise<PaymentInstrumentResponse>;
|
|
99
|
+
getCardPaymentInstruments: () => Promise<PaymentInstrumentCustomerResponse>;
|
|
57
100
|
isLoading: boolean;
|
|
58
101
|
error: string | null;
|
|
59
102
|
clearError: () => void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { useState, useCallback, useEffect, useRef, useMemo } from 'react';
|
|
2
1
|
import { useBasisTheory } from '@basis-theory/basis-theory-react';
|
|
3
|
-
import {
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
3
|
import { getBasisTheoryApiKey } from '../config/payment';
|
|
4
|
+
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
5
5
|
import { usePaymentPolling } from './usePaymentPolling';
|
|
6
6
|
import { useThreeds } from './useThreeds';
|
|
7
7
|
// Helper function to format expiry date
|
|
@@ -319,6 +319,19 @@ export function usePayment() {
|
|
|
319
319
|
throw error;
|
|
320
320
|
}
|
|
321
321
|
}, [processPaymentDirect]);
|
|
322
|
+
// Get card payment instruments
|
|
323
|
+
const getCardPaymentInstruments = useCallback(async () => {
|
|
324
|
+
try {
|
|
325
|
+
const response = await apiService.fetch(`/api/v1/payment-instruments/customer`);
|
|
326
|
+
return response;
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
console.error('Error fetching card payment instruments:', error);
|
|
330
|
+
const errorMsg = error instanceof Error ? error.message : 'Failed to fetch payment instruments';
|
|
331
|
+
setError(errorMsg);
|
|
332
|
+
throw error;
|
|
333
|
+
}
|
|
334
|
+
}, [apiService]);
|
|
322
335
|
const clearError = useCallback(() => {
|
|
323
336
|
setError(null);
|
|
324
337
|
}, []);
|
|
@@ -328,6 +341,7 @@ export function usePayment() {
|
|
|
328
341
|
processPaymentWithInstrument,
|
|
329
342
|
createCardPaymentInstrument,
|
|
330
343
|
createApplePayPaymentInstrument,
|
|
344
|
+
getCardPaymentInstruments,
|
|
331
345
|
isLoading: isLoading || !basisTheory, // Indicate loading if BasisTheory is not initialized
|
|
332
346
|
error,
|
|
333
347
|
clearError,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Configuration Hook
|
|
3
|
+
*
|
|
4
|
+
* Professional SDK approach to access plugin configuration:
|
|
5
|
+
* - Store ID, Account ID, Base Path: from .local.json (dev) or headers (production)
|
|
6
|
+
* - Deployment Config: from config/*.json (dev) or meta tags (production)
|
|
7
|
+
*/
|
|
8
|
+
export interface PluginConfig {
|
|
9
|
+
storeId?: string;
|
|
10
|
+
accountId?: string;
|
|
11
|
+
basePath?: string;
|
|
12
|
+
config?: Record<string, any>;
|
|
13
|
+
}
|
|
14
|
+
export interface LocalDevConfig {
|
|
15
|
+
storeId: string;
|
|
16
|
+
accountId: string;
|
|
17
|
+
basePath: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Load plugin configuration (cached)
|
|
21
|
+
* Tries local dev config first, then production config
|
|
22
|
+
*/
|
|
23
|
+
export declare const loadPluginConfig: (configVariant?: string) => Promise<PluginConfig>;
|
|
24
|
+
/**
|
|
25
|
+
* Main hook for plugin configuration
|
|
26
|
+
* Gets config from TagadaProvider context (no parameters needed)
|
|
27
|
+
*/
|
|
28
|
+
export declare const usePluginConfig: () => {
|
|
29
|
+
storeId: string | undefined;
|
|
30
|
+
accountId: string | undefined;
|
|
31
|
+
basePath: string;
|
|
32
|
+
config: Record<string, any>;
|
|
33
|
+
loading: boolean;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Specific hook for basePath only
|
|
37
|
+
*/
|
|
38
|
+
export declare const useBasePath: () => {
|
|
39
|
+
basePath: string;
|
|
40
|
+
loading: boolean;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Get cached config directly (for non-React usage)
|
|
44
|
+
*/
|
|
45
|
+
export declare const getPluginConfig: (configVariant?: string) => Promise<PluginConfig>;
|
|
46
|
+
/**
|
|
47
|
+
* Clear the config cache (useful for testing)
|
|
48
|
+
*/
|
|
49
|
+
export declare const clearPluginConfigCache: () => void;
|
|
50
|
+
/**
|
|
51
|
+
* Development helper to log current configuration
|
|
52
|
+
*/
|
|
53
|
+
export declare const debugPluginConfig: (configVariant?: string) => Promise<void>;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Configuration Hook
|
|
3
|
+
*
|
|
4
|
+
* Professional SDK approach to access plugin configuration:
|
|
5
|
+
* - Store ID, Account ID, Base Path: from .local.json (dev) or headers (production)
|
|
6
|
+
* - Deployment Config: from config/*.json (dev) or meta tags (production)
|
|
7
|
+
*/
|
|
8
|
+
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
9
|
+
// Simple cache for plugin configuration
|
|
10
|
+
let cachedConfig = null;
|
|
11
|
+
let configPromise = null;
|
|
12
|
+
/**
|
|
13
|
+
* Load local development configuration
|
|
14
|
+
* Combines .local.json + config/default.tgd.json (or specified variant)
|
|
15
|
+
*/
|
|
16
|
+
const loadLocalDevConfig = async (configVariant = 'default') => {
|
|
17
|
+
try {
|
|
18
|
+
// Only try to load local config in development
|
|
19
|
+
if (process.env.NODE_ENV !== 'development') {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
// Load local store/account config
|
|
23
|
+
const localResponse = await fetch('/.local.json');
|
|
24
|
+
if (!localResponse.ok) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const localConfig = await localResponse.json();
|
|
28
|
+
// Load deployment config (specified variant or fallback to default)
|
|
29
|
+
let config = {};
|
|
30
|
+
let configLoaded = false;
|
|
31
|
+
try {
|
|
32
|
+
// Try .tgd.json first (new format), then fallback to .json
|
|
33
|
+
let deploymentResponse = await fetch(`/config/${configVariant}.tgd.json`);
|
|
34
|
+
if (!deploymentResponse.ok) {
|
|
35
|
+
deploymentResponse = await fetch(`/config/${configVariant}.json`);
|
|
36
|
+
}
|
|
37
|
+
if (deploymentResponse.ok) {
|
|
38
|
+
config = await deploymentResponse.json();
|
|
39
|
+
configLoaded = true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Config fetch failed, will try fallback
|
|
44
|
+
}
|
|
45
|
+
// If config didn't load and it's not 'default', try fallback to default
|
|
46
|
+
if (!configLoaded && configVariant !== 'default') {
|
|
47
|
+
console.warn(`⚠️ Config variant '${configVariant}' not found, falling back to 'default'`);
|
|
48
|
+
try {
|
|
49
|
+
let defaultResponse = await fetch('/config/default.tgd.json');
|
|
50
|
+
if (!defaultResponse.ok) {
|
|
51
|
+
defaultResponse = await fetch('/config/default.json');
|
|
52
|
+
}
|
|
53
|
+
if (defaultResponse.ok) {
|
|
54
|
+
config = await defaultResponse.json();
|
|
55
|
+
configLoaded = true;
|
|
56
|
+
console.log(`✅ Fallback to 'default' config successful`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Default config also failed
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Final warning if no config was loaded
|
|
64
|
+
if (!configLoaded) {
|
|
65
|
+
if (configVariant === 'default') {
|
|
66
|
+
console.warn(`⚠️ No 'default' config found. Create /config/default.tgd.json`);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.warn(`⚠️ Neither '${configVariant}' nor 'default' config found. Create /config/default.tgd.json`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
console.log('🛠️ Using local development plugin config:', {
|
|
73
|
+
configName: config.configName || configVariant,
|
|
74
|
+
local: localConfig,
|
|
75
|
+
deployment: config
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
storeId: localConfig.storeId,
|
|
79
|
+
accountId: localConfig.accountId,
|
|
80
|
+
basePath: localConfig.basePath,
|
|
81
|
+
config,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Load production config from headers and meta tags
|
|
90
|
+
*/
|
|
91
|
+
const loadProductionConfig = async () => {
|
|
92
|
+
try {
|
|
93
|
+
// Get headers
|
|
94
|
+
const response = await fetch(window.location.href, { method: 'HEAD' });
|
|
95
|
+
const storeId = response.headers.get('X-Plugin-Store-Id') || undefined;
|
|
96
|
+
const accountId = response.headers.get('X-Plugin-Account-Id') || undefined;
|
|
97
|
+
const basePath = response.headers.get('X-Plugin-Base-Path') || '/';
|
|
98
|
+
// Get deployment config from meta tags
|
|
99
|
+
let config = {};
|
|
100
|
+
try {
|
|
101
|
+
const configMeta = document.querySelector('meta[name="x-plugin-config"]');
|
|
102
|
+
const encodedConfig = configMeta?.getAttribute('content');
|
|
103
|
+
if (encodedConfig) {
|
|
104
|
+
const decodedConfig = decodeURIComponent(encodedConfig);
|
|
105
|
+
config = JSON.parse(decodedConfig);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Deployment config is optional
|
|
110
|
+
}
|
|
111
|
+
return { storeId, accountId, basePath, config };
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return { basePath: '/', config: {} };
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Load plugin configuration (cached)
|
|
119
|
+
* Tries local dev config first, then production config
|
|
120
|
+
*/
|
|
121
|
+
export const loadPluginConfig = async (configVariant = 'default') => {
|
|
122
|
+
// Try local development config first
|
|
123
|
+
const localConfig = await loadLocalDevConfig(configVariant);
|
|
124
|
+
if (localConfig) {
|
|
125
|
+
return localConfig;
|
|
126
|
+
}
|
|
127
|
+
// Fall back to production config
|
|
128
|
+
return loadProductionConfig();
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Main hook for plugin configuration
|
|
132
|
+
* Gets config from TagadaProvider context (no parameters needed)
|
|
133
|
+
*/
|
|
134
|
+
export const usePluginConfig = () => {
|
|
135
|
+
const context = useTagadaContext();
|
|
136
|
+
const { pluginConfig, pluginConfigLoading } = context;
|
|
137
|
+
return {
|
|
138
|
+
storeId: pluginConfig.storeId,
|
|
139
|
+
accountId: pluginConfig.accountId,
|
|
140
|
+
basePath: pluginConfig.basePath ?? '/',
|
|
141
|
+
config: pluginConfig.config ?? {},
|
|
142
|
+
loading: pluginConfigLoading,
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Specific hook for basePath only
|
|
147
|
+
*/
|
|
148
|
+
export const useBasePath = () => {
|
|
149
|
+
const { basePath, loading } = usePluginConfig();
|
|
150
|
+
return { basePath, loading };
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Get cached config directly (for non-React usage)
|
|
154
|
+
*/
|
|
155
|
+
export const getPluginConfig = async (configVariant = 'default') => {
|
|
156
|
+
if (cachedConfig) {
|
|
157
|
+
return cachedConfig;
|
|
158
|
+
}
|
|
159
|
+
if (configPromise) {
|
|
160
|
+
return configPromise;
|
|
161
|
+
}
|
|
162
|
+
configPromise = loadPluginConfig(configVariant);
|
|
163
|
+
const result = await configPromise;
|
|
164
|
+
cachedConfig = result;
|
|
165
|
+
configPromise = null;
|
|
166
|
+
return result;
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Clear the config cache (useful for testing)
|
|
170
|
+
*/
|
|
171
|
+
export const clearPluginConfigCache = () => {
|
|
172
|
+
cachedConfig = null;
|
|
173
|
+
configPromise = null;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Development helper to log current configuration
|
|
177
|
+
*/
|
|
178
|
+
export const debugPluginConfig = async (configVariant = 'default') => {
|
|
179
|
+
if (process.env.NODE_ENV !== 'development') {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const config = await getPluginConfig(configVariant);
|
|
183
|
+
console.group('🔧 Plugin Configuration Debug');
|
|
184
|
+
console.log('Store ID:', config.storeId || 'Not available');
|
|
185
|
+
console.log('Account ID:', config.accountId || 'Not available');
|
|
186
|
+
console.log('Base Path:', config.basePath || 'Not available');
|
|
187
|
+
console.log('Config Keys:', config.config ? Object.keys(config.config) : 'None');
|
|
188
|
+
console.log('Full Config:', config);
|
|
189
|
+
console.groupEnd();
|
|
190
|
+
};
|
package/dist/react/index.d.ts
CHANGED
|
@@ -9,11 +9,18 @@ export { useCurrency } from './hooks/useCurrency';
|
|
|
9
9
|
export { useCustomer } from './hooks/useCustomer';
|
|
10
10
|
export { useEnvironment } from './hooks/useEnvironment';
|
|
11
11
|
export { useLocale } from './hooks/useLocale';
|
|
12
|
+
export { useLogin } from './hooks/useLogin';
|
|
12
13
|
export { useOffers } from './hooks/useOffers';
|
|
13
14
|
export { useOrderBump } from './hooks/useOrderBump';
|
|
14
15
|
export { usePostPurchases } from './hooks/usePostPurchases';
|
|
15
16
|
export { useProducts } from './hooks/useProducts';
|
|
16
17
|
export { useSession } from './hooks/useSession';
|
|
18
|
+
export { usePluginConfig, useBasePath, getPluginConfig, clearPluginConfigCache, debugPluginConfig } from './hooks/usePluginConfig';
|
|
19
|
+
export type { PluginConfig } from './hooks/usePluginConfig';
|
|
20
|
+
export { useGoogleAutocomplete, useGoogleMapsLoaded } from './hooks/useGoogleAutocomplete';
|
|
21
|
+
export type { GooglePrediction, GoogleAddressComponent, GooglePlaceDetails, ExtractedAddress, UseGoogleAutocompleteOptions, UseGoogleAutocompleteResult } from './hooks/useGoogleAutocomplete';
|
|
22
|
+
export { useISOData, useCountryOptions, useRegionOptions, getAvailableLanguages } from './hooks/useISOData';
|
|
23
|
+
export type { ISOCountry, ISORegion, UseISODataResult } from './hooks/useISOData';
|
|
17
24
|
export { useOrder } from './hooks/useOrder';
|
|
18
25
|
export type { UseOrderOptions, UseOrderResult } from './hooks/useOrder';
|
|
19
26
|
export { usePayment } from './hooks/usePayment';
|
package/dist/react/index.js
CHANGED
|
@@ -12,11 +12,18 @@ export { useCurrency } from './hooks/useCurrency';
|
|
|
12
12
|
export { useCustomer } from './hooks/useCustomer';
|
|
13
13
|
export { useEnvironment } from './hooks/useEnvironment';
|
|
14
14
|
export { useLocale } from './hooks/useLocale';
|
|
15
|
+
export { useLogin } from './hooks/useLogin';
|
|
15
16
|
export { useOffers } from './hooks/useOffers';
|
|
16
17
|
export { useOrderBump } from './hooks/useOrderBump';
|
|
17
18
|
export { usePostPurchases } from './hooks/usePostPurchases';
|
|
18
19
|
export { useProducts } from './hooks/useProducts';
|
|
19
20
|
export { useSession } from './hooks/useSession';
|
|
21
|
+
// Plugin configuration hooks
|
|
22
|
+
export { usePluginConfig, useBasePath, getPluginConfig, clearPluginConfigCache, debugPluginConfig } from './hooks/usePluginConfig';
|
|
23
|
+
// Google Places hooks
|
|
24
|
+
export { useGoogleAutocomplete, useGoogleMapsLoaded } from './hooks/useGoogleAutocomplete';
|
|
25
|
+
// ISO Data hooks
|
|
26
|
+
export { useISOData, useCountryOptions, useRegionOptions, getAvailableLanguages } from './hooks/useISOData';
|
|
20
27
|
// Order hook exports
|
|
21
28
|
export { useOrder } from './hooks/useOrder';
|
|
22
29
|
// Payment hooks exports
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { ReactNode } from 'react';
|
|
5
5
|
import { Customer, Session, AuthState, Locale, Currency, Store, Environment, EnvironmentConfig } from '../types';
|
|
6
6
|
import { ApiService } from '../services/apiService';
|
|
7
|
+
import { PluginConfig } from '../hooks/usePluginConfig';
|
|
7
8
|
import { formatMoney, getCurrencyInfo, moneyStringOrNumberToMinorUnits, minorUnitsToMajorUnits, formatMoneyWithoutSymbol, convertCurrency, formatSimpleMoney } from '../utils/money';
|
|
8
9
|
interface TagadaContextValue {
|
|
9
10
|
auth: AuthState;
|
|
@@ -17,6 +18,8 @@ interface TagadaContextValue {
|
|
|
17
18
|
isLoading: boolean;
|
|
18
19
|
isInitialized: boolean;
|
|
19
20
|
debugMode: boolean;
|
|
21
|
+
pluginConfig: PluginConfig;
|
|
22
|
+
pluginConfigLoading: boolean;
|
|
20
23
|
debugCheckout: {
|
|
21
24
|
isActive: boolean;
|
|
22
25
|
data: any;
|
|
@@ -50,8 +53,9 @@ interface TagadaProviderProps {
|
|
|
50
53
|
debugMode?: boolean;
|
|
51
54
|
storeId?: string;
|
|
52
55
|
accountId?: string;
|
|
56
|
+
localConfig?: string;
|
|
53
57
|
}
|
|
54
58
|
export declare function TagadaProvider({ children, environment, customApiConfig, debugMode, // Remove default, will be set based on environment
|
|
55
|
-
storeId, accountId, }: TagadaProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
59
|
+
storeId: propStoreId, accountId: propAccountId, localConfig, }: TagadaProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
56
60
|
export declare function useTagadaContext(): TagadaContextValue;
|
|
57
61
|
export {};
|