@tagadapay/plugin-sdk 3.1.24 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +1129 -1129
  2. package/build-cdn.js +499 -499
  3. package/dist/external-tracker.js +247 -2875
  4. package/dist/external-tracker.min.js +2 -2
  5. package/dist/external-tracker.min.js.map +4 -4
  6. package/dist/react/config/payment.d.ts +2 -2
  7. package/dist/react/config/payment.js +5 -5
  8. package/dist/react/hooks/useCheckout.js +7 -2
  9. package/dist/react/hooks/usePayment.d.ts +7 -0
  10. package/dist/react/hooks/usePayment.js +1 -0
  11. package/dist/react/providers/TagadaProvider.js +5 -5
  12. package/dist/tagada-react-sdk-minimal.min.js +2 -2
  13. package/dist/tagada-react-sdk-minimal.min.js.map +4 -4
  14. package/dist/tagada-react-sdk.js +1680 -1172
  15. package/dist/tagada-react-sdk.min.js +2 -2
  16. package/dist/tagada-react-sdk.min.js.map +4 -4
  17. package/dist/tagada-sdk.js +1701 -3410
  18. package/dist/tagada-sdk.min.js +2 -2
  19. package/dist/tagada-sdk.min.js.map +4 -4
  20. package/dist/v2/core/client.js +1 -0
  21. package/dist/v2/core/config/environment.d.ts +3 -3
  22. package/dist/v2/core/config/environment.js +7 -7
  23. package/dist/v2/core/funnelClient.d.ts +10 -0
  24. package/dist/v2/core/funnelClient.js +1 -1
  25. package/dist/v2/core/resources/apiClient.d.ts +18 -14
  26. package/dist/v2/core/resources/apiClient.js +151 -109
  27. package/dist/v2/core/resources/checkout.d.ts +1 -1
  28. package/dist/v2/core/resources/funnel.d.ts +1 -1
  29. package/dist/v2/core/resources/geo.d.ts +50 -0
  30. package/dist/v2/core/resources/geo.js +35 -0
  31. package/dist/v2/core/resources/index.d.ts +1 -1
  32. package/dist/v2/core/resources/index.js +1 -1
  33. package/dist/v2/core/resources/offers.js +4 -4
  34. package/dist/v2/core/resources/payments.d.ts +20 -1
  35. package/dist/v2/core/resources/payments.js +8 -0
  36. package/dist/v2/core/utils/currency.d.ts +3 -0
  37. package/dist/v2/core/utils/currency.js +40 -2
  38. package/dist/v2/core/utils/deviceInfo.d.ts +1 -0
  39. package/dist/v2/core/utils/deviceInfo.js +1 -0
  40. package/dist/v2/core/utils/previewMode.js +12 -0
  41. package/dist/v2/core/utils/previewModeIndicator.js +101 -101
  42. package/dist/v2/react/components/ApplePayButton.js +39 -16
  43. package/dist/v2/react/components/FunnelScriptInjector.js +167 -19
  44. package/dist/v2/react/components/StripeExpressButton.d.ts +8 -0
  45. package/dist/v2/react/components/StripeExpressButton.js +23 -3
  46. package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.js +1 -0
  47. package/dist/v2/react/hooks/payment-actions/useNgeniusThreedsAction.d.ts +15 -0
  48. package/dist/v2/react/hooks/payment-actions/useNgeniusThreedsAction.js +166 -0
  49. package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.js +12 -0
  50. package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.js +1 -0
  51. package/dist/v2/react/hooks/useApiQuery.d.ts +1 -1
  52. package/dist/v2/react/hooks/useApiQuery.js +1 -1
  53. package/dist/v2/react/hooks/useCheckoutQuery.js +6 -2
  54. package/dist/v2/react/hooks/useISOData.js +25 -7
  55. package/dist/v2/react/hooks/usePaymentPolling.d.ts +1 -1
  56. package/dist/v2/react/hooks/usePreviewOffer.js +1 -1
  57. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +7 -0
  58. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +105 -9
  59. package/dist/v2/react/providers/TagadaProvider.js +6 -6
  60. package/dist/v2/standalone/apple-pay-service.d.ts +12 -0
  61. package/dist/v2/standalone/apple-pay-service.js +12 -0
  62. package/dist/v2/standalone/external-tracker.d.ts +1 -1
  63. package/dist/v2/standalone/google-pay-service.d.ts +9 -0
  64. package/dist/v2/standalone/google-pay-service.js +9 -0
  65. package/dist/v2/standalone/index.d.ts +8 -1
  66. package/dist/v2/standalone/index.js +7 -0
  67. package/dist/v2/standalone/payment-service.d.ts +18 -5
  68. package/dist/v2/standalone/payment-service.js +63 -9
  69. package/package.json +115 -114
@@ -1,16 +1,21 @@
1
1
  import { useMemo, useEffect, useState, useCallback } from 'react';
2
- import { getCountries, getStatesForCountry, ensureGeoDataLoaded, importLanguage, isLanguageRegistered, getRegisteredLanguages } from '../../../data/iso3166';
2
+ import { getCountries, getStatesForCountry, ensureGeoDataLoaded, AVAILABLE_LANGUAGES, importLanguage, isLanguageRegistered, getRegisteredLanguages } from '../../../data/iso3166';
3
3
  /**
4
4
  * React hook for accessing ISO3166 countries and regions data.
5
5
  * Fetches slim JSON from CDN on first use, then caches in memory.
6
6
  */
7
7
  export function useISOData(language = 'en', autoImport = true, disputeSetting = 'UN') {
8
- const [isLanguageLoaded, setIsLanguageLoaded] = useState(isLanguageRegistered(language));
8
+ // Normalize: if the caller passed an unsupported locale (e.g. 'nl'),
9
+ // silently fall back to English so the country dropdown still renders.
10
+ // The CDN only hosts languages listed in AVAILABLE_LANGUAGES; any other
11
+ // value would 404 and leave `isLanguageLoaded` stuck on false.
12
+ const effectiveLanguage = AVAILABLE_LANGUAGES.includes(language) ? language : 'en';
13
+ const [isLanguageLoaded, setIsLanguageLoaded] = useState(isLanguageRegistered(effectiveLanguage));
9
14
  const [registeredLanguages, setRegisteredLanguages] = useState(getRegisteredLanguages);
10
15
  // Fetch geodata from CDN (countries + regions) for the requested language
11
16
  useEffect(() => {
12
17
  let cancelled = false;
13
- ensureGeoDataLoaded(language)
18
+ ensureGeoDataLoaded(effectiveLanguage)
14
19
  .then(() => {
15
20
  if (!cancelled) {
16
21
  setIsLanguageLoaded(true);
@@ -19,9 +24,22 @@ export function useISOData(language = 'en', autoImport = true, disputeSetting =
19
24
  })
20
25
  .catch((err) => {
21
26
  console.error('[SDK] Failed to load geodata from CDN:', err);
27
+ if (cancelled)
28
+ return;
29
+ // English is prefetched on module load (iso3166.ts). If it
30
+ // landed, unblock the UI — getCountriesCached() falls back to 'en'.
31
+ if (isLanguageRegistered('en')) {
32
+ setIsLanguageLoaded(true);
33
+ return;
34
+ }
35
+ // Otherwise try one last English fetch as ultimate fallback.
36
+ ensureGeoDataLoaded('en')
37
+ .then(() => { if (!cancelled)
38
+ setIsLanguageLoaded(true); })
39
+ .catch(() => { });
22
40
  });
23
41
  return () => { cancelled = true; };
24
- }, [language, autoImport]);
42
+ }, [effectiveLanguage, autoImport]);
25
43
  const data = useMemo(() => {
26
44
  if (!isLanguageLoaded) {
27
45
  return {
@@ -34,7 +52,7 @@ export function useISOData(language = 'en', autoImport = true, disputeSetting =
34
52
  };
35
53
  }
36
54
  try {
37
- const countriesArray = getCountries(language);
55
+ const countriesArray = getCountries(effectiveLanguage);
38
56
  const countries = {};
39
57
  countriesArray.forEach((country) => {
40
58
  countries[country.code] = {
@@ -46,7 +64,7 @@ export function useISOData(language = 'en', autoImport = true, disputeSetting =
46
64
  });
47
65
  const getRegions = (countryCode) => {
48
66
  try {
49
- const states = getStatesForCountry(countryCode, language);
67
+ const states = getStatesForCountry(countryCode, effectiveLanguage);
50
68
  return states.map((state) => ({
51
69
  iso: state.code,
52
70
  name: state.name,
@@ -97,7 +115,7 @@ export function useISOData(language = 'en', autoImport = true, disputeSetting =
97
115
  registeredLanguages,
98
116
  };
99
117
  }
100
- }, [language, disputeSetting, isLanguageLoaded, registeredLanguages]);
118
+ }, [effectiveLanguage, disputeSetting, isLanguageLoaded, registeredLanguages]);
101
119
  return data;
102
120
  }
103
121
  /**
@@ -4,7 +4,7 @@ export interface Payment {
4
4
  subStatus: string;
5
5
  requireAction: 'none' | 'redirect' | 'redirect_to_payment' | 'error' | 'radar' | 'stripe_express_checkout';
6
6
  requireActionData?: {
7
- type: 'redirect' | 'redirect_to_payment' | 'threeds_auth' | 'processor_auth' | 'error' | 'stripe_radar' | 'finix_radar' | 'radar' | 'kesspay_auth' | 'trustflow_auth' | 'mastercard_auth' | 'stripe_express_checkout';
7
+ type: 'redirect' | 'redirect_to_payment' | 'threeds_auth' | 'processor_auth' | 'error' | 'stripe_radar' | 'finix_radar' | 'radar' | 'kesspay_auth' | 'trustflow_auth' | 'mastercard_auth' | 'ngenius_3ds' | 'stripe_express_checkout';
8
8
  url?: string;
9
9
  processed: boolean;
10
10
  processorId?: string;
@@ -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 = 'USD', initialSelections = {} } = options;
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,74 @@ 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
- export const ExpressPaymentMethodsProvider = ({ children, customerId, checkout, }) => {
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
+ // Match by: explicit express flag, type, method, provider, or key name
46
+ const resolvedMethod = entry.method || entry.provider || key;
47
+ const isExpressMethod = entry.express
48
+ || entry.type === 'apple_pay' || entry.type === 'google_pay'
49
+ || resolvedMethod === 'apple_pay' || resolvedMethod === 'google_pay';
50
+ if (isExpressMethod) {
51
+ methods.push({
52
+ id: key,
53
+ type: entry.type === 'apple_pay' || entry.type === 'google_pay' ? entry.type : resolvedMethod,
54
+ title: entry.label || key,
55
+ iconUrl: entry.logoUrl || '',
56
+ default: false,
57
+ settings: { processorId: entry.processorId, publishableKey: entry.publishableKey },
58
+ });
59
+ }
60
+ }
61
+ return methods;
62
+ }
63
+ export const ExpressPaymentMethodsProvider = ({ children, customerId, checkout, paymentSetupConfig, }) => {
14
64
  const queryClient = useQueryClient();
15
65
  const [availableExpressPaymentMethodIds, setAvailableExpressPaymentMethodIds] = useState([]);
16
66
  const [error, setError] = useState(null);
17
67
  const checkoutSessionId = checkout?.checkoutSession?.id;
68
+ // If paymentSetupConfig is provided AND contains at least one enabled method, derive from it.
69
+ // Missing, empty `{}`, or all-disabled configs fall through to the API fallback.
70
+ const configDerivedMethods = useMemo(() => (paymentSetupConfig && Object.values(paymentSetupConfig).some((v) => v?.enabled === true))
71
+ ? paymentMethodsFromSetupConfig(paymentSetupConfig)
72
+ : undefined, [paymentSetupConfig]);
18
73
  // Create express payment methods resource client
19
74
  const expressPaymentResource = useMemo(() => {
20
75
  try {
@@ -25,14 +80,15 @@ export const ExpressPaymentMethodsProvider = ({ children, customerId, checkout,
25
80
  (error instanceof Error ? error.message : 'Unknown error'));
26
81
  }
27
82
  }, []);
28
- // Fetch payment methods using TanStack Query
29
- const { data: paymentMethods, isLoading: isLoadingPaymentMethods } = useQuery({
83
+ // Only fetch from API when paymentSetupConfig is NOT provided
84
+ const { data: apiPaymentMethods, isLoading: isLoadingPaymentMethods } = useQuery({
30
85
  queryKey: ['payment-methods', checkoutSessionId],
31
86
  queryFn: () => expressPaymentResource.getPaymentMethods(checkoutSessionId),
32
- enabled: !!checkoutSessionId,
33
- staleTime: 60000, // 1 minute
87
+ enabled: !!checkoutSessionId && !configDerivedMethods,
88
+ staleTime: 60000,
34
89
  refetchOnWindowFocus: false,
35
90
  });
91
+ const paymentMethods = configDerivedMethods ?? apiPaymentMethods;
36
92
  // Use v2 shipping rates hook
37
93
  const { shippingRates, refetch: refetchRates } = useShippingRatesQuery({ checkout });
38
94
  // Get order summary from checkout data
@@ -161,8 +217,48 @@ export const ExpressPaymentMethodsProvider = ({ children, customerId, checkout,
161
217
  return;
162
218
  await updateEmailMutation.mutateAsync(input.data);
163
219
  }, [customerId, updateEmailMutation]);
164
- // Check if Apple Pay is available (window.ApplePaySession only exists on Apple devices)
165
- const isApplePayAvailable = typeof window !== 'undefined' && typeof window.ApplePaySession !== 'undefined';
220
+ // Check if Apple Pay is available load SDK on demand then re-check reactively
221
+ const [isApplePayAvailable, setIsApplePayAvailable] = useState(() => typeof window !== 'undefined' && typeof window.ApplePaySession !== 'undefined');
222
+ const [isApplePaySdkLoading, setIsApplePaySdkLoading] = useState(false);
223
+ const hasApplePayConfig = paymentMethods?.some((p) => p.type === 'apple_pay');
224
+ useEffect(() => {
225
+ if (isApplePayAvailable || !hasApplePayConfig)
226
+ return;
227
+ const APPLE_PAY_SDK_URL = 'https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js';
228
+ const onSdkReady = () => {
229
+ if (typeof window.ApplePaySession !== 'undefined') {
230
+ setIsApplePayAvailable(true);
231
+ }
232
+ setIsApplePaySdkLoading(false);
233
+ };
234
+ // Check if script is already in DOM (may still be loading)
235
+ const existing = document.querySelector(`script[src="${APPLE_PAY_SDK_URL}"]`);
236
+ if (existing) {
237
+ setIsApplePaySdkLoading(true);
238
+ // Script may already be loaded or still loading
239
+ if (typeof window.ApplePaySession !== 'undefined') {
240
+ onSdkReady();
241
+ }
242
+ else {
243
+ existing.addEventListener('load', onSdkReady);
244
+ // Fallback timeout in case we missed the load event
245
+ const timer = setTimeout(onSdkReady, 2000);
246
+ return () => {
247
+ existing.removeEventListener('load', onSdkReady);
248
+ clearTimeout(timer);
249
+ };
250
+ }
251
+ return;
252
+ }
253
+ setIsApplePaySdkLoading(true);
254
+ const script = document.createElement('script');
255
+ script.src = APPLE_PAY_SDK_URL;
256
+ script.crossOrigin = 'anonymous';
257
+ script.async = true;
258
+ script.onload = onSdkReady;
259
+ script.onerror = () => setIsApplePaySdkLoading(false);
260
+ document.head.appendChild(script);
261
+ }, [isApplePayAvailable, hasApplePayConfig]);
166
262
  // Identify specific payment method types
167
263
  const applePayPaymentMethod = useMemo(() => {
168
264
  return isApplePayAvailable ? paymentMethods?.find((p) => p.type === 'apple_pay') : undefined;
@@ -177,7 +273,7 @@ export const ExpressPaymentMethodsProvider = ({ children, customerId, checkout,
177
273
  return paymentMethods?.find((p) => (p.type === 'stripe_apm' || p.type === 'tagadapay_apm') &&
178
274
  p.settings?.processors?.some((g) => EXPRESS_METHOD_KEYS.some((key) => g?.methods?.[key]?.enabled === true)));
179
275
  }, [paymentMethods]);
180
- const loading = !paymentMethods || isLoadingPaymentMethods;
276
+ const loading = !paymentMethods || (!configDerivedMethods && isLoadingPaymentMethods) || isApplePaySdkLoading;
181
277
  const tagadaError = useMemo(() => error ? new TagadaError(error, { code: TagadaErrorCode.PAYMENT_FAILED }) : null, [error]);
182
278
  const contextValue = {
183
279
  paymentMethods,
@@ -38,11 +38,11 @@ const InitializationLoader = () => (_jsxs("div", { style: {
38
38
  borderTop: '1.5px solid #9ca3af',
39
39
  borderRadius: '50%',
40
40
  animation: 'tagada-spin 1s linear infinite',
41
- } }), _jsx("span", { children: "Loading..." }), _jsx("style", { children: `
42
- @keyframes tagada-spin {
43
- 0% { transform: rotate(0deg); }
44
- 100% { transform: rotate(360deg); }
45
- }
41
+ } }), _jsx("span", { children: "Loading..." }), _jsx("style", { children: `
42
+ @keyframes tagada-spin {
43
+ 0% { transform: rotate(0deg); }
44
+ 100% { transform: rotate(360deg); }
45
+ }
46
46
  ` })] }));
47
47
  const TagadaContext = createContext(null);
48
48
  export function TagadaProvider({ children, environment, customApiConfig, debugMode, localConfig, blockUntilSessionReady = false, rawPluginConfig, features, funnelId, autoInitializeFunnel = true, onNavigate, onFunnelError, debugScripts = [], }) {
@@ -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.href = result.url;
318
+ window.location.replace(result.url);
319
319
  }
320
320
  };
321
321
  const next = async (event, options) => {
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Apple Pay Service (Standalone) — re-export shim.
3
+ *
4
+ * The canonical implementation lives in `@tagadapay/core-js` so partner
5
+ * code, the studio, the native checkout, and the plugin-sdk all share a
6
+ * single source of truth for the native Apple Pay primitive (BasisTheory
7
+ * tokenization, no checkout session). This file preserves the historical
8
+ * import path `@tagadapay/plugin-sdk/v2/standalone` so existing consumers
9
+ * (studio-app, vanilla-checkout templates, &hellip;) keep working without
10
+ * code changes.
11
+ */
12
+ export { isApplePayAvailable, startApplePaySession, type ApplePayServiceConfig, type ApplePayPaymentRequest, type ApplePayTokenResult, type ApplePayShippingContact, type ApplePayCallbacks, } from '@tagadapay/core-js';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Apple Pay Service (Standalone) — re-export shim.
3
+ *
4
+ * The canonical implementation lives in `@tagadapay/core-js` so partner
5
+ * code, the studio, the native checkout, and the plugin-sdk all share a
6
+ * single source of truth for the native Apple Pay primitive (BasisTheory
7
+ * tokenization, no checkout session). This file preserves the historical
8
+ * import path `@tagadapay/plugin-sdk/v2/standalone` so existing consumers
9
+ * (studio-app, vanilla-checkout templates, &hellip;) keep working without
10
+ * code changes.
11
+ */
12
+ export { isApplePayAvailable, startApplePaySession, } from '@tagadapay/core-js';
@@ -36,7 +36,7 @@ export interface TagadaTrackerConfig {
36
36
  stepName?: string;
37
37
  /** Step type (optional — e.g., 'landing', 'offer', 'external') */
38
38
  stepType?: string;
39
- /** API base URL (defaults to https://app.tagadapay.com) */
39
+ /** API base URL (defaults to https://api.tagada.io) */
40
40
  apiBaseUrl?: string;
41
41
  /** Enable debug logging */
42
42
  debug?: boolean;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Google Pay Service (Standalone) — re-export shim.
3
+ *
4
+ * The canonical implementation lives in `@tagadapay/core-js`. This file
5
+ * preserves the historical import path `@tagadapay/plugin-sdk/v2/standalone`
6
+ * for downstream consumers (studio-app, vanilla-checkout templates,
7
+ * payment-error-dashboard) — they keep working unchanged.
8
+ */
9
+ export { isGooglePayAvailable, startGooglePaySession, type GooglePayServiceConfig, type GooglePayPaymentRequest, type GooglePayTokenResult, type GooglePayAddress, type GooglePayCallbacks, } from '@tagadapay/core-js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Google Pay Service (Standalone) — re-export shim.
3
+ *
4
+ * The canonical implementation lives in `@tagadapay/core-js`. This file
5
+ * preserves the historical import path `@tagadapay/plugin-sdk/v2/standalone`
6
+ * for downstream consumers (studio-app, vanilla-checkout templates,
7
+ * payment-error-dashboard) — they keep working unchanged.
8
+ */
9
+ export { isGooglePayAvailable, startGooglePaySession, } from '@tagadapay/core-js';
@@ -25,12 +25,15 @@ export { TagadaClient, ApiClient, CheckoutResource };
25
25
  export { ShippingRatesResource } from '../core/resources/shippingRates';
26
26
  export { PaymentsResource } from '../core/resources/payments';
27
27
  export { OrdersResource } from '../core/resources/orders';
28
+ export { OffersResource } from '../core/resources/offers';
28
29
  export { ExpressPaymentMethodsResource } from '../core/resources/expressPaymentMethods';
29
30
  export type { PaymentMethod as ExpressPaymentMethod } from '../core/resources/expressPaymentMethods';
30
31
  export type { ShippingRate, ShippingRatesResponse, ShippingRatesPreviewParams } from '../core/resources/shippingRates';
31
- export type { Payment, PaymentResponse, CardPaymentMethod, BasisTheoryInstance, GooglePayToken, ApplePayToken, PaymentInstrumentInput, PaymentInstrumentResponse, PaymentInstrumentCustomerResponse, } from '../core/resources/payments';
32
+ export type { Payment, PaymentResponse, PaymentOptions, CardPaymentMethod, BasisTheoryInstance, GooglePayToken, ApplePayToken, PaymentInstrumentInput, PaymentInstrumentResponse, PaymentInstrumentCustomerResponse, } from '../core/resources/payments';
32
33
  export { StoreConfigResource } from '../core/resources/storeConfig';
33
34
  export type { StoreConfig } from '../core/resources/storeConfig';
35
+ export { GeoResource } from '../core/resources/geo';
36
+ export type { GeoLocationData } from '../core/resources/geo';
34
37
  export { ThreedsResource } from '../core/resources/threeds';
35
38
  export { ISODataCore } from '../core/isoData';
36
39
  export type { ISOCountry, ISORegion } from '../core/isoData';
@@ -50,6 +53,10 @@ export { getAssignedStepConfig } from '../core/funnelClient';
50
53
  export type { RuntimeStepConfig } from '../core/funnelClient';
51
54
  export { PaymentService } from './payment-service';
52
55
  export type { PaymentServiceConfig, PaymentResult, PaymentCallbacks, CardData, ApmData, } from './payment-service';
56
+ export { isApplePayAvailable, startApplePaySession } from './apple-pay-service';
57
+ export type { ApplePayServiceConfig, ApplePayPaymentRequest, ApplePayTokenResult, ApplePayShippingContact, ApplePayCallbacks, } from './apple-pay-service';
58
+ export { isGooglePayAvailable, startGooglePaySession } from './google-pay-service';
59
+ export type { GooglePayServiceConfig, GooglePayPaymentRequest, GooglePayTokenResult, GooglePayAddress, GooglePayCallbacks, } from './google-pay-service';
53
60
  /**
54
61
  * Get BasisTheory public API key based on hostname detection.
55
62
  * Delegates to the centralized isProductionBasisTheory() logic.
@@ -134,9 +134,12 @@ export { TagadaClient, ApiClient, CheckoutResource };
134
134
  export { ShippingRatesResource } from '../core/resources/shippingRates';
135
135
  export { PaymentsResource } from '../core/resources/payments';
136
136
  export { OrdersResource } from '../core/resources/orders';
137
+ export { OffersResource } from '../core/resources/offers';
137
138
  export { ExpressPaymentMethodsResource } from '../core/resources/expressPaymentMethods';
138
139
  // Re-export Store Config (for standalone resolvers / builders)
139
140
  export { StoreConfigResource } from '../core/resources/storeConfig';
141
+ // Re-export Geo Resource (for standalone geolocation / country detection)
142
+ export { GeoResource } from '../core/resources/geo';
140
143
  // Re-export 3DS Resource (for standalone resolvers / builders)
141
144
  export { ThreedsResource } from '../core/resources/threeds';
142
145
  // Re-export ISO Data Core (for standalone address form / resolvers)
@@ -155,6 +158,10 @@ export { getAssignedPaymentFlowId };
155
158
  export { getAssignedStepConfig } from '../core/funnelClient';
156
159
  // Re-export Payment Service (standalone payment processing — no React)
157
160
  export { PaymentService } from './payment-service';
161
+ // Re-export Apple Pay Service (standalone native Apple Pay — no React)
162
+ export { isApplePayAvailable, startApplePaySession } from './apple-pay-service';
163
+ // Re-export Google Pay Service (standalone native Google Pay — no React)
164
+ export { isGooglePayAvailable, startGooglePaySession } from './google-pay-service';
158
165
  /**
159
166
  * Get BasisTheory public API key based on hostname detection.
160
167
  * Delegates to the centralized isProductionBasisTheory() logic.
@@ -51,6 +51,8 @@ export interface PaymentCallbacks {
51
51
  onSuccess?: (payment: Payment) => void;
52
52
  onFailure?: (error: string) => void;
53
53
  onRedirectReturn?: (paymentId: string) => void;
54
+ /** Called before a browser redirect, allows caller to await async work (e.g. funnel navigate) */
55
+ onBeforeRedirect?: (payment: Payment, redirectUrl: string) => Promise<void>;
54
56
  }
55
57
  export interface PollingCallbacks {
56
58
  onSuccess: (payment: Payment) => void;
@@ -101,6 +103,7 @@ export declare class PaymentService {
101
103
  private handleTrustFlowAuth;
102
104
  private handleFinixRadar;
103
105
  private handleStripeRadar;
106
+ private handleNgeniusThreeds;
104
107
  private handleAirwallexRadar;
105
108
  private handleMasterCardAuth;
106
109
  private loadScript;
@@ -126,9 +129,19 @@ export declare class PaymentService {
126
129
  id: string;
127
130
  sessionId: string;
128
131
  }>;
129
- processCardPayment(checkoutSessionId: string, cardData: CardData): Promise<PaymentResult>;
130
- processApplePayPayment(checkoutSessionId: string, applePayToken: ApplePayToken): Promise<PaymentResult>;
131
- processGooglePayPayment(checkoutSessionId: string, googlePayToken: GooglePayToken): Promise<PaymentResult>;
132
- processPaymentWithInstrument(checkoutSessionId: string, paymentInstrumentId: string): Promise<PaymentResult>;
133
- processApmPayment(checkoutSessionId: string, apmData: ApmData): Promise<PaymentResult>;
132
+ processCardPayment(checkoutSessionId: string, cardData: CardData, options?: {
133
+ shippingRateId?: string;
134
+ }): Promise<PaymentResult>;
135
+ processApplePayPayment(checkoutSessionId: string, applePayToken: ApplePayToken, options?: {
136
+ shippingRateId?: string;
137
+ }): Promise<PaymentResult>;
138
+ processGooglePayPayment(checkoutSessionId: string, googlePayToken: GooglePayToken, options?: {
139
+ shippingRateId?: string;
140
+ }): Promise<PaymentResult>;
141
+ processPaymentWithInstrument(checkoutSessionId: string, paymentInstrumentId: string, options?: {
142
+ shippingRateId?: string;
143
+ }): Promise<PaymentResult>;
144
+ processApmPayment(checkoutSessionId: string, apmData: ApmData, options?: {
145
+ shippingRateId?: string;
146
+ }): Promise<PaymentResult>;
134
147
  }
@@ -212,6 +212,7 @@ export class PaymentService {
212
212
  paymentFlowId,
213
213
  processorId: extra?.processorId,
214
214
  paymentMethod: extra?.paymentMethod,
215
+ shippingRateId: extra?.shippingRateId,
215
216
  });
216
217
  console.log('[PaymentService] Payment response:', {
217
218
  paymentId: response.payment?.id,
@@ -301,6 +302,9 @@ export class PaymentService {
301
302
  const redirectUrl = actionData.metadata?.redirect?.redirectUrl || actionData.redirectUrl || actionData.url;
302
303
  if (redirectUrl) {
303
304
  console.log('[PaymentService] Redirecting:', redirectUrl);
305
+ if (this.callbacks.onBeforeRedirect) {
306
+ await this.callbacks.onBeforeRedirect(payment, redirectUrl);
307
+ }
304
308
  window.location.href = redirectUrl;
305
309
  }
306
310
  else if (payment.status === 'succeeded') {
@@ -343,6 +347,9 @@ export class PaymentService {
343
347
  case 'mastercard_auth':
344
348
  await this.handleMasterCardAuth(payment, actionData);
345
349
  break;
350
+ case 'ngenius_3ds':
351
+ await this.handleNgeniusThreeds(payment, actionData);
352
+ break;
346
353
  default: {
347
354
  console.log('[PaymentService] Unhandled action, starting polling:', actionData.type);
348
355
  this.startPolling(payment.id, {
@@ -501,6 +508,51 @@ export class PaymentService {
501
508
  }
502
509
  }
503
510
  // --------------------------------------------------------------------------
511
+ // N-Genius 3DS (WebSDK)
512
+ // --------------------------------------------------------------------------
513
+ async handleNgeniusThreeds(payment, actionData) {
514
+ const sdk = actionData.metadata?.sdk;
515
+ if (!sdk?.paymentResponse || !sdk.orderReference || !sdk.paymentReference) {
516
+ this.callbacks.onError?.('N-Genius 3DS: missing SDK metadata');
517
+ this.callbacks.onProcessing?.(false);
518
+ return;
519
+ }
520
+ try {
521
+ const sdkUrl = sdk.isSandboxed
522
+ ? 'https://paypage.sandbox.ngenius-payments.com/hosted-sessions/sdk.js'
523
+ : 'https://paypage.ngenius-payments.com/hosted-sessions/sdk.js';
524
+ if (!document.getElementById('ngenius-websdk')) {
525
+ await new Promise((resolve, reject) => {
526
+ const script = document.createElement('script');
527
+ script.id = 'ngenius-websdk';
528
+ script.src = sdkUrl;
529
+ script.onload = () => resolve();
530
+ script.onerror = () => reject(new Error('Failed to load N-Genius WebSDK'));
531
+ document.head.appendChild(script);
532
+ });
533
+ }
534
+ const NI = window.NI;
535
+ if (!NI?.handlePaymentResponse) {
536
+ throw new Error('N-Genius WebSDK did not expose window.NI.handlePaymentResponse');
537
+ }
538
+ console.log('[N-Genius 3DS] Starting WebSDK challenge');
539
+ const outcome = await NI.handlePaymentResponse(sdk.paymentResponse, { mountId: 'ngenius-3ds-container', style: { width: '100%', height: 500 } });
540
+ console.log('[N-Genius 3DS] WebSDK outcome:', outcome.status);
541
+ const completedPayment = await this.paymentsResource.ngeniusThreedsComplete({
542
+ paymentId: payment.id,
543
+ orderReference: sdk.orderReference,
544
+ paymentReference: sdk.paymentReference,
545
+ });
546
+ await this.handleResumedPayment(completedPayment);
547
+ }
548
+ catch (error) {
549
+ const msg = error instanceof Error ? error.message : 'N-Genius 3DS failed';
550
+ console.error('[N-Genius 3DS] Error:', error);
551
+ this.callbacks.onError?.(msg);
552
+ this.callbacks.onProcessing?.(false);
553
+ }
554
+ }
555
+ // --------------------------------------------------------------------------
504
556
  // Airwallex Radar
505
557
  // --------------------------------------------------------------------------
506
558
  async handleAirwallexRadar(payment, actionData) {
@@ -533,6 +585,7 @@ export class PaymentService {
533
585
  existingScript.setAttribute('data-order-session-id', sessionId);
534
586
  }
535
587
  await this.paymentsResource.saveRadarSession({
588
+ paymentId: payment.id,
536
589
  checkoutSessionId,
537
590
  orderId,
538
591
  airwallexRadarSessionId: sessionId,
@@ -834,7 +887,7 @@ export class PaymentService {
834
887
  // ==========================================================================
835
888
  // PAYMENT PROCESSING (all methods)
836
889
  // ==========================================================================
837
- async processCardPayment(checkoutSessionId, cardData) {
890
+ async processCardPayment(checkoutSessionId, cardData, options) {
838
891
  this.callbacks.onProcessing?.(true);
839
892
  this.callbacks.onError?.(null);
840
893
  try {
@@ -857,7 +910,7 @@ export class PaymentService {
857
910
  // Continue without 3DS
858
911
  }
859
912
  }
860
- return await this.processAndHandle(checkoutSessionId, instrument.id, threedsSessionId);
913
+ return await this.processAndHandle(checkoutSessionId, instrument.id, threedsSessionId, { shippingRateId: options?.shippingRateId });
861
914
  }
862
915
  catch (error) {
863
916
  const msg = error instanceof Error ? error.message : String(error);
@@ -866,12 +919,12 @@ export class PaymentService {
866
919
  return { success: false, error: msg };
867
920
  }
868
921
  }
869
- async processApplePayPayment(checkoutSessionId, applePayToken) {
922
+ async processApplePayPayment(checkoutSessionId, applePayToken, options) {
870
923
  this.callbacks.onProcessing?.(true);
871
924
  this.callbacks.onError?.(null);
872
925
  try {
873
926
  const instrument = await this.createApplePayPaymentInstrument(applePayToken);
874
- return await this.processAndHandle(checkoutSessionId, instrument.id);
927
+ return await this.processAndHandle(checkoutSessionId, instrument.id, undefined, { shippingRateId: options?.shippingRateId });
875
928
  }
876
929
  catch (error) {
877
930
  const msg = error instanceof Error ? error.message : String(error);
@@ -880,12 +933,12 @@ export class PaymentService {
880
933
  return { success: false, error: msg };
881
934
  }
882
935
  }
883
- async processGooglePayPayment(checkoutSessionId, googlePayToken) {
936
+ async processGooglePayPayment(checkoutSessionId, googlePayToken, options) {
884
937
  this.callbacks.onProcessing?.(true);
885
938
  this.callbacks.onError?.(null);
886
939
  try {
887
940
  const instrument = await this.createGooglePayPaymentInstrument(googlePayToken);
888
- return await this.processAndHandle(checkoutSessionId, instrument.id);
941
+ return await this.processAndHandle(checkoutSessionId, instrument.id, undefined, { shippingRateId: options?.shippingRateId });
889
942
  }
890
943
  catch (error) {
891
944
  const msg = error instanceof Error ? error.message : String(error);
@@ -894,11 +947,11 @@ export class PaymentService {
894
947
  return { success: false, error: msg };
895
948
  }
896
949
  }
897
- async processPaymentWithInstrument(checkoutSessionId, paymentInstrumentId) {
950
+ async processPaymentWithInstrument(checkoutSessionId, paymentInstrumentId, options) {
898
951
  this.callbacks.onProcessing?.(true);
899
952
  this.callbacks.onError?.(null);
900
953
  try {
901
- return await this.processAndHandle(checkoutSessionId, paymentInstrumentId);
954
+ return await this.processAndHandle(checkoutSessionId, paymentInstrumentId, undefined, { shippingRateId: options?.shippingRateId });
902
955
  }
903
956
  catch (error) {
904
957
  const msg = error instanceof Error ? error.message : String(error);
@@ -907,7 +960,7 @@ export class PaymentService {
907
960
  return { success: false, error: msg };
908
961
  }
909
962
  }
910
- async processApmPayment(checkoutSessionId, apmData) {
963
+ async processApmPayment(checkoutSessionId, apmData, options) {
911
964
  this.callbacks.onProcessing?.(true);
912
965
  this.callbacks.onError?.(null);
913
966
  try {
@@ -916,6 +969,7 @@ export class PaymentService {
916
969
  paymentMethod: apmData.paymentMethod,
917
970
  initiatedBy: apmData.initiatedBy,
918
971
  source: apmData.source,
972
+ shippingRateId: options?.shippingRateId,
919
973
  });
920
974
  }
921
975
  catch (error) {