@tagadapay/plugin-sdk 3.1.24 → 3.1.25
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/external-tracker.js +243 -2871
- package/dist/external-tracker.min.js +2 -2
- package/dist/external-tracker.min.js.map +4 -4
- package/dist/react/hooks/useCheckout.js +7 -2
- package/dist/tagada-react-sdk-minimal.min.js +2 -2
- package/dist/tagada-react-sdk-minimal.min.js.map +3 -3
- package/dist/tagada-react-sdk.js +340 -173
- package/dist/tagada-react-sdk.min.js +2 -2
- package/dist/tagada-react-sdk.min.js.map +3 -3
- package/dist/tagada-sdk.js +776 -3327
- package/dist/tagada-sdk.min.js +2 -2
- package/dist/tagada-sdk.min.js.map +4 -4
- package/dist/v2/core/client.js +1 -0
- package/dist/v2/core/funnelClient.d.ts +8 -0
- package/dist/v2/core/funnelClient.js +1 -1
- package/dist/v2/core/resources/apiClient.d.ts +18 -14
- package/dist/v2/core/resources/apiClient.js +151 -109
- package/dist/v2/core/resources/checkout.d.ts +1 -1
- package/dist/v2/core/resources/index.d.ts +1 -1
- package/dist/v2/core/resources/index.js +1 -1
- package/dist/v2/core/resources/offers.js +4 -4
- package/dist/v2/core/resources/payments.d.ts +1 -0
- package/dist/v2/core/utils/currency.d.ts +3 -0
- package/dist/v2/core/utils/currency.js +40 -2
- package/dist/v2/core/utils/deviceInfo.d.ts +1 -0
- package/dist/v2/core/utils/deviceInfo.js +1 -0
- package/dist/v2/core/utils/previewMode.js +12 -0
- package/dist/v2/react/components/ApplePayButton.js +39 -16
- package/dist/v2/react/components/StripeExpressButton.js +1 -2
- package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.js +1 -0
- package/dist/v2/react/hooks/useApiQuery.d.ts +1 -1
- package/dist/v2/react/hooks/useApiQuery.js +1 -1
- package/dist/v2/react/hooks/useCheckoutQuery.js +6 -2
- package/dist/v2/react/hooks/usePreviewOffer.js +1 -1
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +7 -0
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +97 -9
- package/dist/v2/react/providers/TagadaProvider.js +1 -1
- package/dist/v2/standalone/payment-service.js +1 -0
- package/package.json +1 -1
|
@@ -24,6 +24,30 @@ const applePayContactToAddress = (contact) => {
|
|
|
24
24
|
email: contact?.emailAddress || '',
|
|
25
25
|
};
|
|
26
26
|
};
|
|
27
|
+
const APPLE_PAY_SDK_URL = 'https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js';
|
|
28
|
+
let applePaySdkPromise = null;
|
|
29
|
+
function loadApplePaySdk() {
|
|
30
|
+
if (applePaySdkPromise)
|
|
31
|
+
return applePaySdkPromise;
|
|
32
|
+
// Check if the script is already in the DOM (e.g. from a previous load)
|
|
33
|
+
if (document.querySelector(`script[src="${APPLE_PAY_SDK_URL}"]`)) {
|
|
34
|
+
applePaySdkPromise = Promise.resolve();
|
|
35
|
+
return applePaySdkPromise;
|
|
36
|
+
}
|
|
37
|
+
applePaySdkPromise = new Promise((resolve, reject) => {
|
|
38
|
+
const script = document.createElement('script');
|
|
39
|
+
script.src = APPLE_PAY_SDK_URL;
|
|
40
|
+
script.crossOrigin = 'anonymous';
|
|
41
|
+
script.async = true;
|
|
42
|
+
script.onload = () => resolve();
|
|
43
|
+
script.onerror = () => {
|
|
44
|
+
applePaySdkPromise = null;
|
|
45
|
+
reject(new Error('[ApplePay] Failed to load Apple Pay SDK'));
|
|
46
|
+
};
|
|
47
|
+
document.head.appendChild(script);
|
|
48
|
+
});
|
|
49
|
+
return applePaySdkPromise;
|
|
50
|
+
}
|
|
27
51
|
export const ApplePayButton = ({ checkout, onSuccess, onError, onCancel }) => {
|
|
28
52
|
const { applePayPaymentMethod, reComputeOrderSummary, shippingMethods, lineItems, handleAddExpressId, updateCheckoutSessionValues, updateCustomerEmail, setError: setContextError, } = useExpressPaymentMethods();
|
|
29
53
|
const [processingPayment, setProcessingPayment] = useState(false);
|
|
@@ -38,25 +62,24 @@ export const ApplePayButton = ({ checkout, onSuccess, onError, onCancel }) => {
|
|
|
38
62
|
if (!applePayPaymentMethod) {
|
|
39
63
|
return null;
|
|
40
64
|
}
|
|
41
|
-
//
|
|
65
|
+
// Load SDK on demand, then check Apple Pay availability
|
|
42
66
|
useEffect(() => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
67
|
+
let cancelled = false;
|
|
68
|
+
const checkAvailability = () => {
|
|
69
|
+
try {
|
|
70
|
+
if (!cancelled && window?.ApplePaySession && ApplePaySession.canMakePayments()) {
|
|
71
|
+
setIsApplePayAvailable(true);
|
|
72
|
+
handleAddExpressId('apple_pay');
|
|
73
|
+
}
|
|
50
74
|
}
|
|
51
|
-
|
|
52
|
-
|
|
75
|
+
catch (error) {
|
|
76
|
+
console.warn('[ApplePay] Apple Pay not available:', error);
|
|
53
77
|
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
78
|
+
};
|
|
79
|
+
loadApplePaySdk()
|
|
80
|
+
.then(checkAvailability)
|
|
81
|
+
.catch(() => { });
|
|
82
|
+
return () => { cancelled = true; };
|
|
60
83
|
}, [handleAddExpressId]);
|
|
61
84
|
// Helper to convert minor units to currency string
|
|
62
85
|
const minorUnitsToCurrencyString = useCallback((amountMinor, currency) => {
|
|
@@ -9,7 +9,7 @@ import { getGlobalApiClient } from '../hooks/useApiQuery';
|
|
|
9
9
|
// Express method keys — drives processorGroup detection and ECE paymentMethods options.
|
|
10
10
|
// 'klarna_express' is the CRM config key for Klarna via ECE, distinct from 'klarna' (redirect flow).
|
|
11
11
|
// Backward compatible: existing configs with only apple_pay/google_pay continue to match.
|
|
12
|
-
const EXPRESS_METHOD_KEYS = ['apple_pay', 'google_pay', '
|
|
12
|
+
const EXPRESS_METHOD_KEYS = ['apple_pay', 'google_pay', 'link', 'klarna_express'];
|
|
13
13
|
// Inner component — must be a child of <Elements> to use useStripe() and useElements()
|
|
14
14
|
function StripeExpressButtonInner({ checkout, processorId, enabledExpressMethods, onSuccess, onError, onCancel, }) {
|
|
15
15
|
const stripe = useStripe();
|
|
@@ -141,7 +141,6 @@ function StripeExpressButtonInner({ checkout, processorId, enabledExpressMethods
|
|
|
141
141
|
applePay: enabledExpressMethods.includes('apple_pay') ? 'always' : 'never',
|
|
142
142
|
googlePay: enabledExpressMethods.includes('google_pay') ? 'always' : 'never',
|
|
143
143
|
link: enabledExpressMethods.includes('link') ? 'auto' : 'never',
|
|
144
|
-
paypal: enabledExpressMethods.includes('paypal') ? 'auto' : 'never',
|
|
145
144
|
klarna: enabledExpressMethods.includes('klarna_express') ? 'auto' : 'never',
|
|
146
145
|
},
|
|
147
146
|
emailRequired: true,
|
|
@@ -62,6 +62,7 @@ export function useAirwallexRadarAction({ paymentsResource, startPolling, setErr
|
|
|
62
62
|
console.log('Airwallex device fingerprint session created:', sessionId);
|
|
63
63
|
// Save radar session to database
|
|
64
64
|
await paymentsResource.saveRadarSession({
|
|
65
|
+
paymentId: payment.id,
|
|
65
66
|
checkoutSessionId,
|
|
66
67
|
orderId,
|
|
67
68
|
airwallexRadarSessionId: sessionId,
|
|
@@ -80,10 +80,14 @@ export function useCheckoutQuery(options = {}) {
|
|
|
80
80
|
}, [providedToken, internalToken]);
|
|
81
81
|
// Use provided token or internal token
|
|
82
82
|
const checkoutToken = providedToken || internalToken;
|
|
83
|
+
// Only send currency to the backend when explicitly set via URL param or persisted storage.
|
|
84
|
+
// When not explicit, the backend uses the session's own selectedPresentmentCurrency --
|
|
85
|
+
// the frontend then reads the currency from the response, never guesses.
|
|
86
|
+
const explicitCurrency = currency.isExplicit ? currency.code : undefined;
|
|
83
87
|
// Main checkout query
|
|
84
88
|
const { data: checkout, isLoading, error, isSuccess, refetch, } = useQuery({
|
|
85
|
-
queryKey: ['checkout', checkoutToken,
|
|
86
|
-
queryFn: () => checkoutResource.getCheckout(checkoutToken,
|
|
89
|
+
queryKey: ['checkout', checkoutToken, explicitCurrency],
|
|
90
|
+
queryFn: () => checkoutResource.getCheckout(checkoutToken, explicitCurrency),
|
|
87
91
|
enabled: enabled && !!checkoutToken && isSessionInitialized,
|
|
88
92
|
staleTime: 30000, // 30 seconds
|
|
89
93
|
refetchOnWindowFocus: false,
|
|
@@ -12,7 +12,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
12
12
|
import { OffersResource } from '../../core/resources/offers';
|
|
13
13
|
import { getGlobalApiClient } from './useApiQuery';
|
|
14
14
|
export function usePreviewOffer(options) {
|
|
15
|
-
const { offerId, currency: requestedCurrency = '
|
|
15
|
+
const { offerId, currency: requestedCurrency = '', initialSelections = {} } = options;
|
|
16
16
|
const queryParamsCurrency = typeof window !== 'undefined'
|
|
17
17
|
? new URLSearchParams(window.location.search).get('currency') || undefined
|
|
18
18
|
: undefined;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Express Payment Methods Context Provider using v2 Architecture
|
|
3
3
|
* Manages express payment methods (Apple Pay, Google Pay, PayPal, Klarna)
|
|
4
|
+
*
|
|
5
|
+
* Payment methods are resolved from:
|
|
6
|
+
* 1. paymentSetupConfig prop (from __TGD_STEP_CONFIG__ or useStepConfig)
|
|
7
|
+
* 2. API fallback (GET /api/v1/payment-methods) for legacy integrations
|
|
4
8
|
*/
|
|
5
9
|
import React, { ReactNode } from 'react';
|
|
6
10
|
import { TagadaError } from '../../core/errors';
|
|
11
|
+
import type { PaymentSetupConfig } from '../../core/funnelClient';
|
|
7
12
|
import { CheckoutData } from '../../core/resources/checkout';
|
|
8
13
|
import { Address, PaymentMethod } from '../../core/resources/expressPaymentMethods';
|
|
9
14
|
type ExpressOrderLineItem = {
|
|
@@ -56,6 +61,8 @@ interface ExpressPaymentMethodsProviderProps {
|
|
|
56
61
|
children: ReactNode;
|
|
57
62
|
customerId?: string;
|
|
58
63
|
checkout?: CheckoutData;
|
|
64
|
+
/** Step-level payment config from __TGD_STEP_CONFIG__ / useStepConfig(). */
|
|
65
|
+
paymentSetupConfig?: PaymentSetupConfig;
|
|
59
66
|
}
|
|
60
67
|
export declare const ExpressPaymentMethodsContext: React.Context<ExpressPaymentMethodsContextType | undefined>;
|
|
61
68
|
export declare const ExpressPaymentMethodsProvider: React.FC<ExpressPaymentMethodsProviderProps>;
|
|
@@ -2,19 +2,66 @@ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
/**
|
|
3
3
|
* Express Payment Methods Context Provider using v2 Architecture
|
|
4
4
|
* Manages express payment methods (Apple Pay, Google Pay, PayPal, Klarna)
|
|
5
|
+
*
|
|
6
|
+
* Payment methods are resolved from:
|
|
7
|
+
* 1. paymentSetupConfig prop (from __TGD_STEP_CONFIG__ or useStepConfig)
|
|
8
|
+
* 2. API fallback (GET /api/v1/payment-methods) for legacy integrations
|
|
5
9
|
*/
|
|
6
10
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
7
|
-
import { createContext, useCallback, useMemo, useState } from 'react';
|
|
11
|
+
import { createContext, useCallback, useEffect, useMemo, useState } from 'react';
|
|
8
12
|
import { TagadaError, TagadaErrorCode } from '../../core/errors';
|
|
9
13
|
import { ExpressPaymentMethodsResource, } from '../../core/resources/expressPaymentMethods';
|
|
10
14
|
import { getGlobalApiClient } from '../hooks/useApiQuery';
|
|
11
15
|
import { useShippingRatesQuery } from '../hooks/useShippingRatesQuery';
|
|
12
16
|
export const ExpressPaymentMethodsContext = createContext(undefined);
|
|
13
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Synthesize PaymentMethod[] from paymentSetupConfig entries.
|
|
19
|
+
* Converts express_checkout:processorId entries into the shape the provider expects.
|
|
20
|
+
*/
|
|
21
|
+
function paymentMethodsFromSetupConfig(config) {
|
|
22
|
+
const methods = [];
|
|
23
|
+
for (const [key, entry] of Object.entries(config)) {
|
|
24
|
+
if (!entry.enabled)
|
|
25
|
+
continue;
|
|
26
|
+
// express_checkout:processorId entries → synthesize a stripe_apm-style method
|
|
27
|
+
if (key.startsWith('express_checkout:') && entry.methods) {
|
|
28
|
+
methods.push({
|
|
29
|
+
id: key,
|
|
30
|
+
type: 'stripe_apm',
|
|
31
|
+
title: entry.label || 'Express Checkout',
|
|
32
|
+
iconUrl: entry.logoUrl || '',
|
|
33
|
+
default: false,
|
|
34
|
+
settings: {
|
|
35
|
+
publishableKey: entry.publishableKey,
|
|
36
|
+
processors: [{
|
|
37
|
+
processorId: entry.processorId,
|
|
38
|
+
methods: entry.methods,
|
|
39
|
+
}],
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
// Individual express methods (apple_pay, google_pay, etc.)
|
|
45
|
+
if (entry.express || entry.type === 'apple_pay' || entry.type === 'google_pay') {
|
|
46
|
+
methods.push({
|
|
47
|
+
id: key,
|
|
48
|
+
type: entry.type || entry.method || key,
|
|
49
|
+
title: entry.label || key,
|
|
50
|
+
iconUrl: entry.logoUrl || '',
|
|
51
|
+
default: false,
|
|
52
|
+
settings: { processorId: entry.processorId, publishableKey: entry.publishableKey },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return methods;
|
|
57
|
+
}
|
|
58
|
+
export const ExpressPaymentMethodsProvider = ({ children, customerId, checkout, paymentSetupConfig, }) => {
|
|
14
59
|
const queryClient = useQueryClient();
|
|
15
60
|
const [availableExpressPaymentMethodIds, setAvailableExpressPaymentMethodIds] = useState([]);
|
|
16
61
|
const [error, setError] = useState(null);
|
|
17
62
|
const checkoutSessionId = checkout?.checkoutSession?.id;
|
|
63
|
+
// If paymentSetupConfig is provided, derive payment methods from it (no API call needed)
|
|
64
|
+
const configDerivedMethods = useMemo(() => paymentSetupConfig ? paymentMethodsFromSetupConfig(paymentSetupConfig) : undefined, [paymentSetupConfig]);
|
|
18
65
|
// Create express payment methods resource client
|
|
19
66
|
const expressPaymentResource = useMemo(() => {
|
|
20
67
|
try {
|
|
@@ -25,14 +72,15 @@ export const ExpressPaymentMethodsProvider = ({ children, customerId, checkout,
|
|
|
25
72
|
(error instanceof Error ? error.message : 'Unknown error'));
|
|
26
73
|
}
|
|
27
74
|
}, []);
|
|
28
|
-
//
|
|
29
|
-
const { data:
|
|
75
|
+
// Only fetch from API when paymentSetupConfig is NOT provided
|
|
76
|
+
const { data: apiPaymentMethods, isLoading: isLoadingPaymentMethods } = useQuery({
|
|
30
77
|
queryKey: ['payment-methods', checkoutSessionId],
|
|
31
78
|
queryFn: () => expressPaymentResource.getPaymentMethods(checkoutSessionId),
|
|
32
|
-
enabled: !!checkoutSessionId,
|
|
33
|
-
staleTime: 60000,
|
|
79
|
+
enabled: !!checkoutSessionId && !configDerivedMethods,
|
|
80
|
+
staleTime: 60000,
|
|
34
81
|
refetchOnWindowFocus: false,
|
|
35
82
|
});
|
|
83
|
+
const paymentMethods = configDerivedMethods ?? apiPaymentMethods;
|
|
36
84
|
// Use v2 shipping rates hook
|
|
37
85
|
const { shippingRates, refetch: refetchRates } = useShippingRatesQuery({ checkout });
|
|
38
86
|
// Get order summary from checkout data
|
|
@@ -161,8 +209,48 @@ export const ExpressPaymentMethodsProvider = ({ children, customerId, checkout,
|
|
|
161
209
|
return;
|
|
162
210
|
await updateEmailMutation.mutateAsync(input.data);
|
|
163
211
|
}, [customerId, updateEmailMutation]);
|
|
164
|
-
// Check if Apple Pay is available
|
|
165
|
-
const isApplePayAvailable = typeof window !== 'undefined' && typeof window.ApplePaySession !== 'undefined';
|
|
212
|
+
// Check if Apple Pay is available — load SDK on demand then re-check reactively
|
|
213
|
+
const [isApplePayAvailable, setIsApplePayAvailable] = useState(() => typeof window !== 'undefined' && typeof window.ApplePaySession !== 'undefined');
|
|
214
|
+
const [isApplePaySdkLoading, setIsApplePaySdkLoading] = useState(false);
|
|
215
|
+
const hasApplePayConfig = paymentMethods?.some((p) => p.type === 'apple_pay');
|
|
216
|
+
useEffect(() => {
|
|
217
|
+
if (isApplePayAvailable || !hasApplePayConfig)
|
|
218
|
+
return;
|
|
219
|
+
const APPLE_PAY_SDK_URL = 'https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js';
|
|
220
|
+
const onSdkReady = () => {
|
|
221
|
+
if (typeof window.ApplePaySession !== 'undefined') {
|
|
222
|
+
setIsApplePayAvailable(true);
|
|
223
|
+
}
|
|
224
|
+
setIsApplePaySdkLoading(false);
|
|
225
|
+
};
|
|
226
|
+
// Check if script is already in DOM (may still be loading)
|
|
227
|
+
const existing = document.querySelector(`script[src="${APPLE_PAY_SDK_URL}"]`);
|
|
228
|
+
if (existing) {
|
|
229
|
+
setIsApplePaySdkLoading(true);
|
|
230
|
+
// Script may already be loaded or still loading
|
|
231
|
+
if (typeof window.ApplePaySession !== 'undefined') {
|
|
232
|
+
onSdkReady();
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
existing.addEventListener('load', onSdkReady);
|
|
236
|
+
// Fallback timeout in case we missed the load event
|
|
237
|
+
const timer = setTimeout(onSdkReady, 2000);
|
|
238
|
+
return () => {
|
|
239
|
+
existing.removeEventListener('load', onSdkReady);
|
|
240
|
+
clearTimeout(timer);
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
setIsApplePaySdkLoading(true);
|
|
246
|
+
const script = document.createElement('script');
|
|
247
|
+
script.src = APPLE_PAY_SDK_URL;
|
|
248
|
+
script.crossOrigin = 'anonymous';
|
|
249
|
+
script.async = true;
|
|
250
|
+
script.onload = onSdkReady;
|
|
251
|
+
script.onerror = () => setIsApplePaySdkLoading(false);
|
|
252
|
+
document.head.appendChild(script);
|
|
253
|
+
}, [isApplePayAvailable, hasApplePayConfig]);
|
|
166
254
|
// Identify specific payment method types
|
|
167
255
|
const applePayPaymentMethod = useMemo(() => {
|
|
168
256
|
return isApplePayAvailable ? paymentMethods?.find((p) => p.type === 'apple_pay') : undefined;
|
|
@@ -177,7 +265,7 @@ export const ExpressPaymentMethodsProvider = ({ children, customerId, checkout,
|
|
|
177
265
|
return paymentMethods?.find((p) => (p.type === 'stripe_apm' || p.type === 'tagadapay_apm') &&
|
|
178
266
|
p.settings?.processors?.some((g) => EXPRESS_METHOD_KEYS.some((key) => g?.methods?.[key]?.enabled === true)));
|
|
179
267
|
}, [paymentMethods]);
|
|
180
|
-
const loading = !paymentMethods || isLoadingPaymentMethods;
|
|
268
|
+
const loading = !paymentMethods || (!configDerivedMethods && isLoadingPaymentMethods) || isApplePaySdkLoading;
|
|
181
269
|
const tagadaError = useMemo(() => error ? new TagadaError(error, { code: TagadaErrorCode.PAYMENT_FAILED }) : null, [error]);
|
|
182
270
|
const contextValue = {
|
|
183
271
|
paymentMethods,
|
|
@@ -315,7 +315,7 @@ export function TagadaProvider({ children, environment, customApiConfig, debugMo
|
|
|
315
315
|
console.log('🚀 [TagadaProvider] Auto-redirecting to:', result.url);
|
|
316
316
|
// Set pending redirect flag BEFORE navigation to prevent renders
|
|
317
317
|
setPendingRedirect(true);
|
|
318
|
-
window.location.
|
|
318
|
+
window.location.replace(result.url);
|
|
319
319
|
}
|
|
320
320
|
};
|
|
321
321
|
const next = async (event, options) => {
|
|
@@ -533,6 +533,7 @@ export class PaymentService {
|
|
|
533
533
|
existingScript.setAttribute('data-order-session-id', sessionId);
|
|
534
534
|
}
|
|
535
535
|
await this.paymentsResource.saveRadarSession({
|
|
536
|
+
paymentId: payment.id,
|
|
536
537
|
checkoutSessionId,
|
|
537
538
|
orderId,
|
|
538
539
|
airwallexRadarSessionId: sessionId,
|