@tagadapay/plugin-sdk 4.0.0 → 4.0.4
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 +1129 -1129
- package/build-cdn.js +499 -499
- package/dist/external-tracker.js +156 -2
- package/dist/external-tracker.min.js +2 -2
- package/dist/external-tracker.min.js.map +4 -4
- package/dist/react/providers/TagadaProvider.js +5 -5
- package/dist/tagada-react-sdk-minimal.min.js +2 -2
- package/dist/tagada-react-sdk-minimal.min.js.map +4 -4
- package/dist/tagada-react-sdk.js +707 -253
- package/dist/tagada-react-sdk.min.js +2 -2
- package/dist/tagada-react-sdk.min.js.map +4 -4
- package/dist/tagada-sdk.js +2922 -102
- package/dist/tagada-sdk.min.js +2 -2
- package/dist/tagada-sdk.min.js.map +4 -4
- package/dist/v2/core/funnelClient.d.ts +40 -0
- package/dist/v2/core/funnelClient.js +30 -0
- package/dist/v2/core/pixelTracker.d.ts +51 -0
- package/dist/v2/core/pixelTracker.js +425 -0
- package/dist/v2/core/resources/checkout.d.ts +45 -1
- package/dist/v2/core/resources/checkout.js +13 -3
- package/dist/v2/core/resources/offers.d.ts +3 -3
- package/dist/v2/core/resources/offers.js +11 -3
- package/dist/v2/core/resources/promotionEvents.d.ts +5 -0
- package/dist/v2/core/resources/promotionEvents.js +2 -0
- package/dist/v2/core/resources/promotions.d.ts +6 -1
- package/dist/v2/core/resources/promotions.js +6 -1
- package/dist/v2/core/resources/shippingRates.d.ts +18 -0
- package/dist/v2/core/resources/shippingRates.js +18 -0
- package/dist/v2/core/utils/clickIdResolver.d.ts +79 -0
- package/dist/v2/core/utils/clickIdResolver.js +169 -0
- package/dist/v2/core/utils/index.d.ts +2 -0
- package/dist/v2/core/utils/index.js +4 -0
- package/dist/v2/core/utils/metaEventId.d.ts +14 -0
- package/dist/v2/core/utils/metaEventId.js +16 -0
- package/dist/v2/core/utils/previewModeIndicator.js +101 -101
- package/dist/v2/index.d.ts +7 -0
- package/dist/v2/index.js +10 -0
- package/dist/v2/react/components/ApplePayButton.js +50 -0
- package/dist/v2/react/components/FunnelScriptInjector.js +9 -9
- package/dist/v2/react/components/GooglePayButton.js +39 -1
- package/dist/v2/react/components/StripeExpressButton.js +54 -2
- package/dist/v2/react/hooks/payment-actions/useNgeniusThreedsAction.js +11 -11
- package/dist/v2/react/hooks/useCheckoutQuery.js +41 -29
- package/dist/v2/react/hooks/useDiscountsQuery.js +4 -0
- package/dist/v2/react/hooks/useFunnel.d.ts +7 -0
- package/dist/v2/react/hooks/useFunnel.js +2 -1
- package/dist/v2/react/hooks/useOfferQuery.d.ts +11 -0
- package/dist/v2/react/hooks/useOfferQuery.js +11 -0
- package/dist/v2/react/hooks/usePixelTracking.d.ts +10 -5
- package/dist/v2/react/hooks/usePixelTracking.js +32 -374
- package/dist/v2/react/hooks/usePreviewOffer.d.ts +3 -1
- package/dist/v2/react/hooks/usePreviewOffer.js +4 -2
- package/dist/v2/react/hooks/usePromotionsQuery.js +9 -3
- package/dist/v2/react/hooks/useShippingRatesQuery.js +36 -21
- package/dist/v2/react/hooks/useStepConfig.d.ts +9 -0
- package/dist/v2/react/hooks/useStepConfig.js +5 -1
- package/dist/v2/react/index.d.ts +5 -0
- package/dist/v2/react/index.js +9 -0
- package/dist/v2/react/providers/TagadaProvider.js +18 -5
- package/dist/v2/standalone/apple-pay-service.d.ts +1 -1
- package/dist/v2/standalone/index.d.ts +3 -0
- package/dist/v2/standalone/index.js +23 -0
- package/dist/v2/standalone/payment-service.d.ts +54 -1
- package/dist/v2/standalone/payment-service.js +228 -61
- package/package.json +115 -115
|
@@ -212,143 +212,143 @@ export function injectPreviewModeIndicator() {
|
|
|
212
212
|
// Create container
|
|
213
213
|
const container = document.createElement('div');
|
|
214
214
|
container.id = 'tgd-preview-indicator';
|
|
215
|
-
container.style.cssText = `
|
|
216
|
-
position: fixed;
|
|
217
|
-
bottom: 16px;
|
|
218
|
-
right: 16px;
|
|
219
|
-
z-index: 999999;
|
|
220
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
215
|
+
container.style.cssText = `
|
|
216
|
+
position: fixed;
|
|
217
|
+
bottom: 16px;
|
|
218
|
+
right: 16px;
|
|
219
|
+
z-index: 999999;
|
|
220
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
221
221
|
`;
|
|
222
222
|
// Create badge
|
|
223
223
|
const badge = document.createElement('div');
|
|
224
|
-
badge.style.cssText = `
|
|
225
|
-
background: ${draftMode ? '#ff9500' : '#007aff'};
|
|
226
|
-
color: white;
|
|
227
|
-
padding: 8px 12px;
|
|
228
|
-
border-radius: 8px;
|
|
229
|
-
font-size: 13px;
|
|
230
|
-
font-weight: 600;
|
|
231
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
232
|
-
cursor: pointer;
|
|
233
|
-
transition: all 0.2s ease;
|
|
234
|
-
display: flex;
|
|
235
|
-
align-items: center;
|
|
236
|
-
gap: 6px;
|
|
224
|
+
badge.style.cssText = `
|
|
225
|
+
background: ${draftMode ? '#ff9500' : '#007aff'};
|
|
226
|
+
color: white;
|
|
227
|
+
padding: 8px 12px;
|
|
228
|
+
border-radius: 8px;
|
|
229
|
+
font-size: 13px;
|
|
230
|
+
font-weight: 600;
|
|
231
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
232
|
+
cursor: pointer;
|
|
233
|
+
transition: all 0.2s ease;
|
|
234
|
+
display: flex;
|
|
235
|
+
align-items: center;
|
|
236
|
+
gap: 6px;
|
|
237
237
|
`;
|
|
238
|
-
badge.innerHTML = `
|
|
239
|
-
<span style="font-size: 16px;">🔍</span>
|
|
240
|
-
<span>${draftMode ? 'Preview Mode' : 'Dev Mode'}</span>
|
|
238
|
+
badge.innerHTML = `
|
|
239
|
+
<span style="font-size: 16px;">🔍</span>
|
|
240
|
+
<span>${draftMode ? 'Preview Mode' : 'Dev Mode'}</span>
|
|
241
241
|
`;
|
|
242
242
|
// Create details popup (with padding-top to bridge gap with badge)
|
|
243
243
|
const details = document.createElement('div');
|
|
244
|
-
details.style.cssText = `
|
|
245
|
-
position: absolute;
|
|
246
|
-
bottom: calc(100% + 8px);
|
|
247
|
-
right: 0;
|
|
248
|
-
background: white;
|
|
249
|
-
border: 1px solid #e5e5e5;
|
|
250
|
-
border-radius: 8px;
|
|
251
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
252
|
-
padding: 12px;
|
|
253
|
-
min-width: 250px;
|
|
254
|
-
font-size: 12px;
|
|
255
|
-
line-height: 1.5;
|
|
256
|
-
display: none;
|
|
244
|
+
details.style.cssText = `
|
|
245
|
+
position: absolute;
|
|
246
|
+
bottom: calc(100% + 8px);
|
|
247
|
+
right: 0;
|
|
248
|
+
background: white;
|
|
249
|
+
border: 1px solid #e5e5e5;
|
|
250
|
+
border-radius: 8px;
|
|
251
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
252
|
+
padding: 12px;
|
|
253
|
+
min-width: 250px;
|
|
254
|
+
font-size: 12px;
|
|
255
|
+
line-height: 1.5;
|
|
256
|
+
display: none;
|
|
257
257
|
`;
|
|
258
258
|
details.style.paddingTop = '20px'; // Extra padding to bridge the gap
|
|
259
259
|
// Add invisible bridge between badge and popup to prevent flickering
|
|
260
260
|
const bridge = document.createElement('div');
|
|
261
|
-
bridge.style.cssText = `
|
|
262
|
-
position: absolute;
|
|
263
|
-
bottom: 100%;
|
|
264
|
-
left: 0;
|
|
265
|
-
right: 0;
|
|
266
|
-
height: 8px;
|
|
267
|
-
display: none;
|
|
261
|
+
bridge.style.cssText = `
|
|
262
|
+
position: absolute;
|
|
263
|
+
bottom: 100%;
|
|
264
|
+
left: 0;
|
|
265
|
+
right: 0;
|
|
266
|
+
height: 8px;
|
|
267
|
+
display: none;
|
|
268
268
|
`;
|
|
269
269
|
// Build details content
|
|
270
270
|
let detailsHTML = '<div style="margin-bottom: 8px; font-weight: 600; color: #1d1d1f;">Current Environment</div>';
|
|
271
271
|
detailsHTML += '<div style="display: flex; flex-direction: column; gap: 6px;">';
|
|
272
272
|
if (draftMode) {
|
|
273
|
-
detailsHTML += `
|
|
274
|
-
<div style="display: flex; justify-content: space-between; color: #86868b;">
|
|
275
|
-
<span>Draft Mode:</span>
|
|
276
|
-
<span style="color: #ff9500; font-weight: 600;">ON</span>
|
|
277
|
-
</div>
|
|
273
|
+
detailsHTML += `
|
|
274
|
+
<div style="display: flex; justify-content: space-between; color: #86868b;">
|
|
275
|
+
<span>Draft Mode:</span>
|
|
276
|
+
<span style="color: #ff9500; font-weight: 600;">ON</span>
|
|
277
|
+
</div>
|
|
278
278
|
`;
|
|
279
279
|
}
|
|
280
280
|
if (trackingDisabled) {
|
|
281
|
-
detailsHTML += `
|
|
282
|
-
<div style="display: flex; justify-content: space-between; color: #86868b;">
|
|
283
|
-
<span>Tracking:</span>
|
|
284
|
-
<span style="color: #ff3b30; font-weight: 600;">DISABLED</span>
|
|
285
|
-
</div>
|
|
281
|
+
detailsHTML += `
|
|
282
|
+
<div style="display: flex; justify-content: space-between; color: #86868b;">
|
|
283
|
+
<span>Tracking:</span>
|
|
284
|
+
<span style="color: #ff3b30; font-weight: 600;">DISABLED</span>
|
|
285
|
+
</div>
|
|
286
286
|
`;
|
|
287
287
|
}
|
|
288
288
|
if (params.funnelEnv) {
|
|
289
|
-
detailsHTML += `
|
|
290
|
-
<div style="display: flex; justify-content: space-between; color: #86868b;">
|
|
291
|
-
<span>Funnel Env:</span>
|
|
292
|
-
<span style="color: #1d1d1f; font-weight: 600; font-family: monospace; font-size: 11px;">
|
|
293
|
-
${params.funnelEnv}
|
|
294
|
-
</span>
|
|
295
|
-
</div>
|
|
289
|
+
detailsHTML += `
|
|
290
|
+
<div style="display: flex; justify-content: space-between; color: #86868b;">
|
|
291
|
+
<span>Funnel Env:</span>
|
|
292
|
+
<span style="color: #1d1d1f; font-weight: 600; font-family: monospace; font-size: 11px;">
|
|
293
|
+
${params.funnelEnv}
|
|
294
|
+
</span>
|
|
295
|
+
</div>
|
|
296
296
|
`;
|
|
297
297
|
}
|
|
298
298
|
if (params.tagadaClientEnv) {
|
|
299
|
-
detailsHTML += `
|
|
300
|
-
<div style="display: flex; justify-content: space-between; color: #86868b;">
|
|
301
|
-
<span>API Env:</span>
|
|
302
|
-
<span style="color: #1d1d1f; font-weight: 600; font-family: monospace; font-size: 11px;">
|
|
303
|
-
${params.tagadaClientEnv}
|
|
304
|
-
</span>
|
|
305
|
-
</div>
|
|
299
|
+
detailsHTML += `
|
|
300
|
+
<div style="display: flex; justify-content: space-between; color: #86868b;">
|
|
301
|
+
<span>API Env:</span>
|
|
302
|
+
<span style="color: #1d1d1f; font-weight: 600; font-family: monospace; font-size: 11px;">
|
|
303
|
+
${params.tagadaClientEnv}
|
|
304
|
+
</span>
|
|
305
|
+
</div>
|
|
306
306
|
`;
|
|
307
307
|
}
|
|
308
308
|
if (params.tagadaClientBaseUrl) {
|
|
309
|
-
detailsHTML += `
|
|
310
|
-
<div style="color: #86868b;">
|
|
311
|
-
<div style="margin-bottom: 4px;">API URL:</div>
|
|
312
|
-
<div style="color: #1d1d1f; font-weight: 600; font-family: monospace; font-size: 10px; word-break: break-all; background: #f5f5f7; padding: 4px 6px; border-radius: 4px;">
|
|
313
|
-
${params.tagadaClientBaseUrl}
|
|
314
|
-
</div>
|
|
315
|
-
</div>
|
|
309
|
+
detailsHTML += `
|
|
310
|
+
<div style="color: #86868b;">
|
|
311
|
+
<div style="margin-bottom: 4px;">API URL:</div>
|
|
312
|
+
<div style="color: #1d1d1f; font-weight: 600; font-family: monospace; font-size: 10px; word-break: break-all; background: #f5f5f7; padding: 4px 6px; border-radius: 4px;">
|
|
313
|
+
${params.tagadaClientBaseUrl}
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
316
|
`;
|
|
317
317
|
}
|
|
318
318
|
if (params.funnelId) {
|
|
319
|
-
detailsHTML += `
|
|
320
|
-
<div style="color: #86868b; margin-top: 4px; padding-top: 8px; border-top: 1px solid #e5e5e5;">
|
|
321
|
-
<div style="margin-bottom: 4px;">Funnel ID:</div>
|
|
322
|
-
<div style="color: #1d1d1f; font-family: monospace; font-size: 10px; word-break: break-all; background: #f5f5f7; padding: 4px 6px; border-radius: 4px;">
|
|
323
|
-
${params.funnelId}
|
|
324
|
-
</div>
|
|
325
|
-
</div>
|
|
319
|
+
detailsHTML += `
|
|
320
|
+
<div style="color: #86868b; margin-top: 4px; padding-top: 8px; border-top: 1px solid #e5e5e5;">
|
|
321
|
+
<div style="margin-bottom: 4px;">Funnel ID:</div>
|
|
322
|
+
<div style="color: #1d1d1f; font-family: monospace; font-size: 10px; word-break: break-all; background: #f5f5f7; padding: 4px 6px; border-radius: 4px;">
|
|
323
|
+
${params.funnelId}
|
|
324
|
+
</div>
|
|
325
|
+
</div>
|
|
326
326
|
`;
|
|
327
327
|
}
|
|
328
328
|
detailsHTML += '</div>';
|
|
329
|
-
detailsHTML += `
|
|
330
|
-
<div style="margin-top: 12px; padding-top: 8px; border-top: 1px solid #e5e5e5; font-size: 11px; color: #86868b; text-align: center;">
|
|
331
|
-
Add <code style="background: #f5f5f7; padding: 2px 4px; border-radius: 3px;">?forceReset=true</code> to reset
|
|
332
|
-
</div>
|
|
329
|
+
detailsHTML += `
|
|
330
|
+
<div style="margin-top: 12px; padding-top: 8px; border-top: 1px solid #e5e5e5; font-size: 11px; color: #86868b; text-align: center;">
|
|
331
|
+
Add <code style="background: #f5f5f7; padding: 2px 4px; border-radius: 3px;">?forceReset=true</code> to reset
|
|
332
|
+
</div>
|
|
333
333
|
`;
|
|
334
334
|
// Add action button
|
|
335
|
-
detailsHTML += `
|
|
336
|
-
<div style="margin-top: 12px; padding-top: 8px; border-top: 1px solid #e5e5e5;">
|
|
337
|
-
<button id="tgd-leave-preview" style="
|
|
338
|
-
background: #ff3b30;
|
|
339
|
-
color: white;
|
|
340
|
-
border: none;
|
|
341
|
-
border-radius: 6px;
|
|
342
|
-
padding: 10px 12px;
|
|
343
|
-
font-size: 13px;
|
|
344
|
-
font-weight: 600;
|
|
345
|
-
cursor: pointer;
|
|
346
|
-
transition: opacity 0.2s;
|
|
347
|
-
width: 100%;
|
|
348
|
-
">
|
|
349
|
-
🚪 Leave Preview Mode
|
|
350
|
-
</button>
|
|
351
|
-
</div>
|
|
335
|
+
detailsHTML += `
|
|
336
|
+
<div style="margin-top: 12px; padding-top: 8px; border-top: 1px solid #e5e5e5;">
|
|
337
|
+
<button id="tgd-leave-preview" style="
|
|
338
|
+
background: #ff3b30;
|
|
339
|
+
color: white;
|
|
340
|
+
border: none;
|
|
341
|
+
border-radius: 6px;
|
|
342
|
+
padding: 10px 12px;
|
|
343
|
+
font-size: 13px;
|
|
344
|
+
font-weight: 600;
|
|
345
|
+
cursor: pointer;
|
|
346
|
+
transition: opacity 0.2s;
|
|
347
|
+
width: 100%;
|
|
348
|
+
">
|
|
349
|
+
🚪 Leave Preview Mode
|
|
350
|
+
</button>
|
|
351
|
+
</div>
|
|
352
352
|
`;
|
|
353
353
|
details.innerHTML = detailsHTML;
|
|
354
354
|
// Hover behavior - keep popup visible when hovering over badge, bridge, or popup
|
package/dist/v2/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from './core/googleAutocomplete';
|
|
|
9
9
|
export * from './core/isoData';
|
|
10
10
|
export * from './core/utils/configHotReload';
|
|
11
11
|
export * from './core/utils/currency';
|
|
12
|
+
export * from './core/utils/metaEventId';
|
|
12
13
|
export * from './core/utils/pluginConfig';
|
|
13
14
|
export * from './core/utils/previewMode';
|
|
14
15
|
export { injectPreviewModeIndicator, isIndicatorInjected, removePreviewModeIndicator } from './core/utils/previewModeIndicator';
|
|
@@ -16,6 +17,10 @@ export * from './core/utils/products';
|
|
|
16
17
|
export { getAssignedPaymentFlowId, getAssignedScripts, getAssignedStaticResources, getAssignedResources, getAssignedStepConfig, getAssignedOrderBumpOfferIds, getAssignedUpsellOfferIds, getLocalFunnelConfig, getEnabledMethods, getExpressMethods, getExpressMethodsByProcessor, findMethod, isMethodEnabled, loadLocalFunnelConfig } from './core/funnelClient';
|
|
17
18
|
export type { LocalFunnelConfig, PaymentMethodConfig, PaymentSetupConfig, PaymentSetupMethod, PixelsConfig, RuntimeStepConfig } from './core/funnelClient';
|
|
18
19
|
export * from './core/pathRemapping';
|
|
20
|
+
export { bootstrapPixelTrackerFromWindow, createPixelTracker, } from './core/pixelTracker';
|
|
21
|
+
export type { PixelTracker, TrackOptions } from './core/pixelTracker';
|
|
22
|
+
export type { PixelProviderKey } from './core/pixelMapping';
|
|
23
|
+
export { makeMetaEventId } from './core/utils/metaEventId';
|
|
19
24
|
export type { CheckoutData, CheckoutInitParams, CheckoutLineItem, CheckoutSession, CheckoutSessionPreview, Promotion } from './core/resources/checkout';
|
|
20
25
|
export type { Order, OrderLineItem } from './core/utils/order';
|
|
21
26
|
export type { PostPurchaseOffer, PostPurchaseOfferItem, PostPurchaseOfferSummary } from './core/resources/postPurchases';
|
|
@@ -31,6 +36,8 @@ export type { BackNavigationActionData, CartUpdatedActionData, DirectNavigationA
|
|
|
31
36
|
export { ApplePayButton, StripeExpressButton, ExpressPaymentMethodsProvider, formatMoney, getAvailableLanguages, GooglePayButton, PreviewModeIndicator, queryKeys, TagadaProvider, useApiMutation, useApiQuery, useApplePayCheckout, useAuth, useCheckout, useCheckoutToken, useClubOffers, useCountryOptions, useCredits, useCurrency, useCustomer, useCustomerInfos, useCustomerOrders, useCustomerSubscriptions, useDiscounts, useExpressPaymentMethods, useFunnel, useFunnelLegacy, useGeoLocation, useGoogleAutocomplete, useGooglePayCheckout, useInvalidateQuery, useISOData, useLanguageImport, useLogin, useOffer, useOrder, useOrderBump, usePayment, usePaymentRetrieve, usePixelTracking, usePluginConfig, usePostPurchases, usePreloadQuery, usePreviewOffer, useProducts, usePromotions, useRegionOptions, useRemappableParams, useShippingRates, useSimpleFunnel, useStepConfig, useStoreConfig, useTagadaContext, useThreeds, useThreedsModal, useTranslation, useVipOffers, useSetPaymentMethod, WhopCheckout, useWhopPaymentPolling } from './react';
|
|
32
37
|
export type { DebugScript } from './react';
|
|
33
38
|
export type { PaymentMethodName } from './react';
|
|
39
|
+
export { resolveClickId, publishTrackingGlobal, CLICK_ID_URL_PARAMS, CLICK_ID_COOKIES, } from './core/utils/clickIdResolver';
|
|
40
|
+
export type { ResolvedClickId, TagadaTrackingGlobal, ClickIdSource, } from './core/utils/clickIdResolver';
|
|
34
41
|
export type { TranslateFunction, TranslationText, UseTranslationOptions, UseTranslationResult } from './react/hooks/useTranslation';
|
|
35
42
|
export type { FunnelContextValue } from './react/hooks/useFunnel';
|
|
36
43
|
export type { UseApplePayCheckoutOptions } from './react/hooks/useApplePayCheckout';
|
package/dist/v2/index.js
CHANGED
|
@@ -10,6 +10,7 @@ export * from './core/googleAutocomplete';
|
|
|
10
10
|
export * from './core/isoData';
|
|
11
11
|
export * from './core/utils/configHotReload';
|
|
12
12
|
export * from './core/utils/currency';
|
|
13
|
+
export * from './core/utils/metaEventId';
|
|
13
14
|
export * from './core/utils/pluginConfig';
|
|
14
15
|
export * from './core/utils/previewMode';
|
|
15
16
|
export { injectPreviewModeIndicator, isIndicatorInjected, removePreviewModeIndicator } from './core/utils/previewModeIndicator';
|
|
@@ -22,6 +23,15 @@ getEnabledMethods, getExpressMethods, getExpressMethodsByProcessor, findMethod,
|
|
|
22
23
|
loadLocalFunnelConfig } from './core/funnelClient';
|
|
23
24
|
// Path remapping helpers (framework-agnostic)
|
|
24
25
|
export * from './core/pathRemapping';
|
|
26
|
+
// Framework-agnostic pixel tracker (Studio entries bootstrap from this)
|
|
27
|
+
export { bootstrapPixelTrackerFromWindow, createPixelTracker, } from './core/pixelTracker';
|
|
28
|
+
// Stable event_id helper for Meta CAPI / browser pixel deduplication.
|
|
29
|
+
// Re-exported here (already exposed via /v2/react) so framework-agnostic
|
|
30
|
+
// Studio islands can import it from the same entry as bootstrapPixelTracker.
|
|
31
|
+
export { makeMetaEventId } from './core/utils/metaEventId';
|
|
25
32
|
export { FunnelActionType } from './core/resources/funnel';
|
|
26
33
|
// React exports (hooks and components only, types are exported above)
|
|
27
34
|
export { ApplePayButton, StripeExpressButton, ExpressPaymentMethodsProvider, formatMoney, getAvailableLanguages, GooglePayButton, PreviewModeIndicator, queryKeys, TagadaProvider, useApiMutation, useApiQuery, useApplePayCheckout, useAuth, useCheckout, useCheckoutToken, useClubOffers, useCountryOptions, useCredits, useCurrency, useCustomer, useCustomerInfos, useCustomerOrders, useCustomerSubscriptions, useDiscounts, useExpressPaymentMethods, useFunnel, useFunnelLegacy, useGeoLocation, useGoogleAutocomplete, useGooglePayCheckout, useInvalidateQuery, useISOData, useLanguageImport, useLogin, useOffer, useOrder, useOrderBump, usePayment, usePaymentRetrieve, usePixelTracking, usePluginConfig, usePostPurchases, usePreloadQuery, usePreviewOffer, useProducts, usePromotions, useRegionOptions, useRemappableParams, useShippingRates, useSimpleFunnel, useStepConfig, useStoreConfig, useTagadaContext, useThreeds, useThreedsModal, useTranslation, useVipOffers, useSetPaymentMethod, WhopCheckout, useWhopPaymentPolling } from './react';
|
|
35
|
+
// Click-id resolver (ad-tracker integrations: ClickFlare, Voluum, Binom, …)
|
|
36
|
+
// Re-exported from core so it's reachable from any entry point.
|
|
37
|
+
export { resolveClickId, publishTrackingGlobal, CLICK_ID_URL_PARAMS, CLICK_ID_COOKIES, } from './core/utils/clickIdResolver';
|
|
@@ -9,6 +9,7 @@ import { getCurrencyInfo, minorUnitsToMajorUnits } from '../../../react/utils/mo
|
|
|
9
9
|
import { useExpressPaymentMethods } from '../hooks/useExpressPaymentMethods';
|
|
10
10
|
import { usePaymentQuery } from '../hooks/usePaymentQuery';
|
|
11
11
|
import { useShippingRatesQuery } from '../hooks/useShippingRatesQuery';
|
|
12
|
+
import { useStepConfig } from '../hooks/useStepConfig';
|
|
12
13
|
// Helper function to convert Apple Pay contact to Address (matches CMS)
|
|
13
14
|
const applePayContactToAddress = (contact) => {
|
|
14
15
|
return {
|
|
@@ -52,6 +53,14 @@ export const ApplePayButton = ({ checkout, onSuccess, onError, onCancel }) => {
|
|
|
52
53
|
const { applePayPaymentMethod, reComputeOrderSummary, shippingMethods, lineItems, handleAddExpressId, updateCheckoutSessionValues, updateCustomerEmail, setError: setContextError, } = useExpressPaymentMethods();
|
|
53
54
|
const [processingPayment, setProcessingPayment] = useState(false);
|
|
54
55
|
const [isApplePayAvailable, setIsApplePayAvailable] = useState(false);
|
|
56
|
+
// Per-step country allow-list (CRM-injected stepConfig). Empty/missing = all allowed.
|
|
57
|
+
const { stepConfig } = useStepConfig();
|
|
58
|
+
const countryAllowlist = useMemo(() => {
|
|
59
|
+
const list = stepConfig?.addressSettings?.countryAllowlist;
|
|
60
|
+
if (!list || list.length === 0)
|
|
61
|
+
return undefined;
|
|
62
|
+
return list.map((c) => c.toUpperCase());
|
|
63
|
+
}, [stepConfig]);
|
|
55
64
|
// Get Basis Theory API key
|
|
56
65
|
const basistheoryPublicKey = useMemo(() => getBasisTheoryApiKey(), []);
|
|
57
66
|
// Use payment hook for proper payment processing
|
|
@@ -192,6 +201,7 @@ export const ApplePayButton = ({ checkout, onSuccess, onError, onCancel }) => {
|
|
|
192
201
|
lineItems,
|
|
193
202
|
requiredShippingContactFields: ['name', 'phone', 'email', 'postalAddress'],
|
|
194
203
|
requiredBillingContactFields: ['postalAddress'],
|
|
204
|
+
...(countryAllowlist ? { supportedCountries: countryAllowlist } : {}),
|
|
195
205
|
};
|
|
196
206
|
try {
|
|
197
207
|
const session = new ApplePaySession(3, request);
|
|
@@ -216,6 +226,18 @@ export const ApplePayButton = ({ checkout, onSuccess, onError, onCancel }) => {
|
|
|
216
226
|
const billingContact = event.payment.billingContact;
|
|
217
227
|
const shippingAddress = applePayContactToAddress(shippingContact);
|
|
218
228
|
const billingAddress = applePayContactToAddress(billingContact);
|
|
229
|
+
// Defense-in-depth: reject if wallet-returned country isn't in allowlist
|
|
230
|
+
if (countryAllowlist &&
|
|
231
|
+
shippingAddress.country &&
|
|
232
|
+
!countryAllowlist.includes(shippingAddress.country.toUpperCase())) {
|
|
233
|
+
console.error('[ApplePay] Shipping country not in allowlist:', shippingAddress.country);
|
|
234
|
+
session.completePayment(ApplePaySession.STATUS_FAILURE);
|
|
235
|
+
const errorMessage = 'Shipping to this country is not supported';
|
|
236
|
+
setContextError(errorMessage);
|
|
237
|
+
if (onError)
|
|
238
|
+
onError(errorMessage);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
219
241
|
await updateCheckoutSessionValues({
|
|
220
242
|
data: {
|
|
221
243
|
shippingAddress,
|
|
@@ -283,6 +305,33 @@ export const ApplePayButton = ({ checkout, onSuccess, onError, onCancel }) => {
|
|
|
283
305
|
session.onshippingcontactselected = (event) => {
|
|
284
306
|
void (async () => {
|
|
285
307
|
const shippingContact = event.shippingContact;
|
|
308
|
+
const contactCountry = (shippingContact?.countryCode || '').toUpperCase();
|
|
309
|
+
// Defense-in-depth: surface an inline error in the Apple Pay sheet
|
|
310
|
+
// when the selected shipping country isn't in the allow-list, so the
|
|
311
|
+
// user can pick a valid address without restarting the flow.
|
|
312
|
+
if (countryAllowlist && contactCountry && !countryAllowlist.includes(contactCountry)) {
|
|
313
|
+
console.warn('[ApplePay] Selected shipping country not in allowlist:', contactCountry);
|
|
314
|
+
const ApplePayErrorCtor = window.ApplePayError;
|
|
315
|
+
const currentTotal = {
|
|
316
|
+
label: checkout.checkoutSession.store?.name || 'Store',
|
|
317
|
+
amount: minorUnitsToCurrencyString(checkout.summary?.totalAdjustedAmount ?? 0, checkout.summary?.currency),
|
|
318
|
+
type: 'final',
|
|
319
|
+
};
|
|
320
|
+
const errors = ApplePayErrorCtor
|
|
321
|
+
? [new ApplePayErrorCtor('shippingContactInvalid', 'country', 'Shipping to this country is not supported')]
|
|
322
|
+
: [{
|
|
323
|
+
code: 'shippingContactInvalid',
|
|
324
|
+
contactField: 'country',
|
|
325
|
+
message: 'Shipping to this country is not supported',
|
|
326
|
+
}];
|
|
327
|
+
session.completeShippingContactSelection({
|
|
328
|
+
newTotal: currentTotal,
|
|
329
|
+
newLineItems: lineItems,
|
|
330
|
+
newShippingMethods: [],
|
|
331
|
+
errors,
|
|
332
|
+
});
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
286
335
|
try {
|
|
287
336
|
await updateCheckoutSessionValues({
|
|
288
337
|
data: {
|
|
@@ -334,6 +383,7 @@ export const ApplePayButton = ({ checkout, onSuccess, onError, onCancel }) => {
|
|
|
334
383
|
shippingMethods,
|
|
335
384
|
lineItems,
|
|
336
385
|
applePayPaymentMethod,
|
|
386
|
+
countryAllowlist,
|
|
337
387
|
minorUnitsToCurrencyString,
|
|
338
388
|
validateMerchant,
|
|
339
389
|
tokenizeApplePay,
|
|
@@ -58,15 +58,15 @@ function parseScriptContent(content) {
|
|
|
58
58
|
}
|
|
59
59
|
/** Wrap inline JS in an error-handling IIFE */
|
|
60
60
|
function wrapInErrorHandler(scriptName, code) {
|
|
61
|
-
return `
|
|
62
|
-
(function() {
|
|
63
|
-
try {
|
|
64
|
-
// Script: ${scriptName}
|
|
65
|
-
${code}
|
|
66
|
-
} catch (error) {
|
|
67
|
-
console.error('[TagadaPay] StepConfig script "${scriptName}" error:', error);
|
|
68
|
-
}
|
|
69
|
-
})();
|
|
61
|
+
return `
|
|
62
|
+
(function() {
|
|
63
|
+
try {
|
|
64
|
+
// Script: ${scriptName}
|
|
65
|
+
${code}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error('[TagadaPay] StepConfig script "${scriptName}" error:', error);
|
|
68
|
+
}
|
|
69
|
+
})();
|
|
70
70
|
`;
|
|
71
71
|
}
|
|
72
72
|
/** Inject a DOM element at the specified position */
|
|
@@ -8,6 +8,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
8
8
|
import { useExpressPaymentMethods } from '../hooks/useExpressPaymentMethods';
|
|
9
9
|
import { usePaymentQuery } from '../hooks/usePaymentQuery';
|
|
10
10
|
import { useShippingRatesQuery } from '../hooks/useShippingRatesQuery';
|
|
11
|
+
import { useStepConfig } from '../hooks/useStepConfig';
|
|
11
12
|
import { getBasisTheoryKeys } from '../../../config/basisTheory';
|
|
12
13
|
export const GooglePayButton = ({ className = '', disabled = false, onSuccess, onError, onCancel, checkout, size = 'lg', buttonColor = 'black', buttonType = 'plain', requiresShipping: requiresShippingProp, }) => {
|
|
13
14
|
const { googlePayPaymentMethod, shippingMethods, lineItems, handleAddExpressId, updateCheckoutSessionValues, updateCustomerEmail, reComputeOrderSummary, setError: setContextError, } = useExpressPaymentMethods();
|
|
@@ -33,6 +34,14 @@ export const GooglePayButton = ({ className = '', disabled = false, onSuccess, o
|
|
|
33
34
|
const [processingPayment, setProcessingPayment] = useState(false);
|
|
34
35
|
const [pendingPaymentData, setPendingPaymentData] = useState(null);
|
|
35
36
|
const [googlePayError, setGooglePayError] = useState(null);
|
|
37
|
+
// Per-step country allow-list (CRM-injected stepConfig). Empty/missing = all allowed.
|
|
38
|
+
const { stepConfig } = useStepConfig();
|
|
39
|
+
const countryAllowlist = useMemo(() => {
|
|
40
|
+
const list = stepConfig?.addressSettings?.countryAllowlist;
|
|
41
|
+
if (!list || list.length === 0)
|
|
42
|
+
return undefined;
|
|
43
|
+
return list.map((c) => c.toUpperCase());
|
|
44
|
+
}, [stepConfig]);
|
|
36
45
|
// Don't render if no Google Pay payment method is enabled
|
|
37
46
|
if (!googlePayPaymentMethod) {
|
|
38
47
|
return null;
|
|
@@ -124,6 +133,19 @@ export const GooglePayButton = ({ className = '', disabled = false, onSuccess, o
|
|
|
124
133
|
if (paymentData.shippingAddress) {
|
|
125
134
|
shippingAddress = googlePayAddressToAddress(paymentData.shippingAddress);
|
|
126
135
|
}
|
|
136
|
+
// Defense-in-depth: reject if wallet-returned country isn't in allowlist
|
|
137
|
+
if (countryAllowlist &&
|
|
138
|
+
shippingAddress?.country &&
|
|
139
|
+
!countryAllowlist.includes(shippingAddress.country.toUpperCase())) {
|
|
140
|
+
const msg = 'Shipping to this country is not supported';
|
|
141
|
+
console.error('[GooglePay] Shipping country not in allowlist:', shippingAddress.country);
|
|
142
|
+
setProcessingPayment(false);
|
|
143
|
+
setGooglePayError(msg);
|
|
144
|
+
setContextError(msg);
|
|
145
|
+
if (onError)
|
|
146
|
+
onError(msg);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
127
149
|
// Update checkout session with addresses before processing payment
|
|
128
150
|
if (shippingAddress) {
|
|
129
151
|
await updateCheckoutSessionValues({
|
|
@@ -164,6 +186,7 @@ export const GooglePayButton = ({ className = '', disabled = false, onSuccess, o
|
|
|
164
186
|
updateCustomerEmail,
|
|
165
187
|
tokenizeGooglePayTokenWithBasisTheory,
|
|
166
188
|
handleGooglePayPayment,
|
|
189
|
+
countryAllowlist,
|
|
167
190
|
onSuccess,
|
|
168
191
|
onError,
|
|
169
192
|
setContextError,
|
|
@@ -193,6 +216,18 @@ export const GooglePayButton = ({ className = '', disabled = false, onSuccess, o
|
|
|
193
216
|
const paymentDataRequestUpdate = {};
|
|
194
217
|
if (intermediatePaymentData.callbackTrigger === 'SHIPPING_ADDRESS') {
|
|
195
218
|
const address = intermediatePaymentData.shippingAddress;
|
|
219
|
+
const addressCountry = (address?.countryCode || '').toUpperCase();
|
|
220
|
+
// Defense-in-depth: reject if selected country isn't in allowlist
|
|
221
|
+
if (countryAllowlist && addressCountry && !countryAllowlist.includes(addressCountry)) {
|
|
222
|
+
resolve({
|
|
223
|
+
error: {
|
|
224
|
+
reason: 'SHIPPING_ADDRESS_UNSERVICEABLE',
|
|
225
|
+
message: 'Shipping to this country is not supported',
|
|
226
|
+
intent: 'SHIPPING_ADDRESS',
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
196
231
|
const shippingAddress = {
|
|
197
232
|
address1: address?.addressLines?.[0] || '',
|
|
198
233
|
address2: address?.addressLines?.[1] || '',
|
|
@@ -270,7 +305,7 @@ export const GooglePayButton = ({ className = '', disabled = false, onSuccess, o
|
|
|
270
305
|
};
|
|
271
306
|
void processCallback();
|
|
272
307
|
});
|
|
273
|
-
}, [updateCheckoutSessionValues, reComputeOrderSummary, checkout, selectRate]);
|
|
308
|
+
}, [updateCheckoutSessionValues, reComputeOrderSummary, checkout, selectRate, countryAllowlist]);
|
|
274
309
|
// Handle payment authorization
|
|
275
310
|
const handleGooglePayAuthorized = useCallback((paymentData) => {
|
|
276
311
|
setPendingPaymentData(paymentData);
|
|
@@ -336,6 +371,9 @@ export const GooglePayButton = ({ className = '', disabled = false, onSuccess, o
|
|
|
336
371
|
? ['SHIPPING_OPTION', 'SHIPPING_ADDRESS', 'PAYMENT_AUTHORIZATION']
|
|
337
372
|
: ['PAYMENT_AUTHORIZATION'],
|
|
338
373
|
...(requiresShipping && {
|
|
374
|
+
shippingAddressParameters: countryAllowlist
|
|
375
|
+
? { allowedCountryCodes: countryAllowlist }
|
|
376
|
+
: undefined,
|
|
339
377
|
shippingOptionParameters: {
|
|
340
378
|
defaultSelectedOptionId: checkout.checkoutSession?.shippingRate?.id ||
|
|
341
379
|
(shippingMethods.length > 0 ? shippingMethods[0].identifier : ''),
|
|
@@ -5,6 +5,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
|
|
|
5
5
|
import { PaymentsResource } from '../../core/resources/payments';
|
|
6
6
|
import { useExpressPaymentMethods } from '../hooks/useExpressPaymentMethods';
|
|
7
7
|
import { usePaymentPolling } from '../hooks/usePaymentPolling';
|
|
8
|
+
import { useStepConfig } from '../hooks/useStepConfig';
|
|
8
9
|
import { getGlobalApiClient } from '../hooks/useApiQuery';
|
|
9
10
|
// Express method keys — drives processorGroup detection and ECE paymentMethods options.
|
|
10
11
|
// 'klarna_express' is the CRM config key for Klarna via ECE, distinct from 'klarna' (redirect flow).
|
|
@@ -18,6 +19,14 @@ function StripeExpressButtonInner({ checkout, processorId, enabledExpressMethods
|
|
|
18
19
|
const { startPolling } = usePaymentPolling();
|
|
19
20
|
const isProcessingRef = useRef(false);
|
|
20
21
|
const [visible, setVisible] = useState(false);
|
|
22
|
+
// Per-step country allow-list (CRM-injected stepConfig). Empty/missing = all allowed.
|
|
23
|
+
const { stepConfig } = useStepConfig();
|
|
24
|
+
const countryAllowlist = useMemo(() => {
|
|
25
|
+
const list = stepConfig?.addressSettings?.countryAllowlist;
|
|
26
|
+
if (!list || list.length === 0)
|
|
27
|
+
return undefined;
|
|
28
|
+
return list.map((c) => c.toUpperCase());
|
|
29
|
+
}, [stepConfig]);
|
|
21
30
|
const paymentsResource = useMemo(() => new PaymentsResource(getGlobalApiClient()), []);
|
|
22
31
|
// Push amount/currency changes (e.g. shipping cost added once address resolved,
|
|
23
32
|
// promo applied, quantity changed) to the mounted ExpressCheckoutElement.
|
|
@@ -41,6 +50,7 @@ function StripeExpressButtonInner({ checkout, processorId, enabledExpressMethods
|
|
|
41
50
|
// This mirrors what ApplePayButton and GooglePayButton do before processing payment,
|
|
42
51
|
// and ensures customer.email is present for the backend email validation.
|
|
43
52
|
const billing = event.billingDetails;
|
|
53
|
+
const eventShipping = event.shippingAddress;
|
|
44
54
|
if (billing) {
|
|
45
55
|
const nameParts = (billing.name ?? '').trim().split(/\s+/);
|
|
46
56
|
const firstName = nameParts[0] ?? '';
|
|
@@ -57,9 +67,33 @@ function StripeExpressButtonInner({ checkout, processorId, enabledExpressMethods
|
|
|
57
67
|
phone: billing.phone ?? '',
|
|
58
68
|
email: billing.email ?? '',
|
|
59
69
|
};
|
|
70
|
+
// When shippingAddressRequired is true, Stripe provides event.shippingAddress.
|
|
71
|
+
// Otherwise the billing address is used as the shipping address (legacy behavior).
|
|
72
|
+
const shippingFromEvent = eventShipping?.address
|
|
73
|
+
? {
|
|
74
|
+
firstName: (eventShipping.name ?? '').trim().split(/\s+/)[0] ?? '',
|
|
75
|
+
lastName: (eventShipping.name ?? '').trim().split(/\s+/).slice(1).join(' '),
|
|
76
|
+
address1: eventShipping.address.line1 ?? '',
|
|
77
|
+
address2: eventShipping.address.line2 ?? '',
|
|
78
|
+
city: eventShipping.address.city ?? '',
|
|
79
|
+
state: eventShipping.address.state ?? '',
|
|
80
|
+
country: eventShipping.address.country ?? '',
|
|
81
|
+
postal: eventShipping.address.postal_code ?? '',
|
|
82
|
+
phone: billing.phone ?? '',
|
|
83
|
+
email: billing.email ?? '',
|
|
84
|
+
}
|
|
85
|
+
: billingAddress;
|
|
86
|
+
// Defense-in-depth: reject if wallet-returned country isn't in allowlist
|
|
87
|
+
const shippingCountry = shippingFromEvent.country.toUpperCase();
|
|
88
|
+
if (countryAllowlist && shippingCountry && !countryAllowlist.includes(shippingCountry)) {
|
|
89
|
+
console.error('[StripeExpress] Shipping country not in allowlist:', shippingCountry);
|
|
90
|
+
onError?.('Shipping to this country is not supported');
|
|
91
|
+
isProcessingRef.current = false;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
60
94
|
await updateCheckoutSessionValues({
|
|
61
95
|
data: {
|
|
62
|
-
shippingAddress:
|
|
96
|
+
shippingAddress: shippingFromEvent,
|
|
63
97
|
billingAddress,
|
|
64
98
|
},
|
|
65
99
|
});
|
|
@@ -139,7 +173,16 @@ function StripeExpressButtonInner({ checkout, processorId, enabledExpressMethods
|
|
|
139
173
|
onError?.(msg);
|
|
140
174
|
}
|
|
141
175
|
};
|
|
142
|
-
|
|
176
|
+
// When ECE has no available wallets, keep the element mounted (so onReady can still
|
|
177
|
+
// fire if the browser later exposes one) but pull it off-screen so it doesn't reserve
|
|
178
|
+
// an empty slot. Off-screen rather than zero-size so the iframe still gets layout.
|
|
179
|
+
const hiddenStyle = {
|
|
180
|
+
position: 'absolute',
|
|
181
|
+
left: '-9999px',
|
|
182
|
+
top: '-9999px',
|
|
183
|
+
pointerEvents: 'none',
|
|
184
|
+
};
|
|
185
|
+
return (_jsx("div", { style: visible ? undefined : hiddenStyle, children: _jsx(ExpressCheckoutElement, { onReady: ({ availablePaymentMethods }) => {
|
|
143
186
|
if (availablePaymentMethods) {
|
|
144
187
|
setVisible(true);
|
|
145
188
|
handleAddExpressId('stripe_express_checkout');
|
|
@@ -161,6 +204,15 @@ function StripeExpressButtonInner({ checkout, processorId, enabledExpressMethods
|
|
|
161
204
|
},
|
|
162
205
|
emailRequired: true,
|
|
163
206
|
billingAddressRequired: true,
|
|
207
|
+
// When an allow-list is configured, collect shipping via the wallet sheet so Stripe
|
|
208
|
+
// can enforce country restrictions natively. allowedShippingCountries requires
|
|
209
|
+
// shippingAddressRequired: true.
|
|
210
|
+
...(countryAllowlist
|
|
211
|
+
? {
|
|
212
|
+
shippingAddressRequired: true,
|
|
213
|
+
allowedShippingCountries: countryAllowlist,
|
|
214
|
+
}
|
|
215
|
+
: {}),
|
|
164
216
|
} }) }));
|
|
165
217
|
}
|
|
166
218
|
// Outer component — resolves Stripe instance and provides <Elements> context
|
|
@@ -16,17 +16,17 @@ function ensureNgeniusContainer() {
|
|
|
16
16
|
if (!container) {
|
|
17
17
|
container = document.createElement('div');
|
|
18
18
|
container.id = NGENIUS_3DS_CONTAINER_ID;
|
|
19
|
-
container.style.cssText = `
|
|
20
|
-
position: fixed;
|
|
21
|
-
top: 0;
|
|
22
|
-
left: 0;
|
|
23
|
-
width: 100%;
|
|
24
|
-
height: 100%;
|
|
25
|
-
background-color: rgba(0, 0, 0, 0.5);
|
|
26
|
-
z-index: 9999;
|
|
27
|
-
display: flex;
|
|
28
|
-
align-items: center;
|
|
29
|
-
justify-content: center;
|
|
19
|
+
container.style.cssText = `
|
|
20
|
+
position: fixed;
|
|
21
|
+
top: 0;
|
|
22
|
+
left: 0;
|
|
23
|
+
width: 100%;
|
|
24
|
+
height: 100%;
|
|
25
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
26
|
+
z-index: 9999;
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: center;
|
|
30
30
|
`;
|
|
31
31
|
document.body.appendChild(container);
|
|
32
32
|
console.log('[N-Genius 3DS] Container created and appended to body');
|