@tagadapay/plugin-sdk 3.1.2 → 3.1.8

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 (56) hide show
  1. package/README.md +1129 -1129
  2. package/build-cdn.js +113 -113
  3. package/dist/external-tracker.js +1104 -491
  4. package/dist/external-tracker.min.js +2 -2
  5. package/dist/external-tracker.min.js.map +4 -4
  6. package/dist/react/hooks/useApplePay.js +25 -36
  7. package/dist/react/hooks/usePaymentPolling.d.ts +9 -3
  8. package/dist/react/providers/TagadaProvider.js +5 -5
  9. package/dist/react/utils/money.d.ts +4 -3
  10. package/dist/react/utils/money.js +39 -6
  11. package/dist/react/utils/trackingUtils.js +1 -0
  12. package/dist/v2/core/client.js +34 -2
  13. package/dist/v2/core/config/environment.js +9 -2
  14. package/dist/v2/core/funnelClient.d.ts +92 -1
  15. package/dist/v2/core/funnelClient.js +247 -3
  16. package/dist/v2/core/resources/apiClient.js +1 -1
  17. package/dist/v2/core/resources/checkout.d.ts +68 -0
  18. package/dist/v2/core/resources/funnel.d.ts +15 -0
  19. package/dist/v2/core/resources/payments.d.ts +50 -3
  20. package/dist/v2/core/resources/payments.js +38 -7
  21. package/dist/v2/core/utils/pluginConfig.js +40 -5
  22. package/dist/v2/core/utils/previewMode.d.ts +3 -0
  23. package/dist/v2/core/utils/previewMode.js +44 -14
  24. package/dist/v2/core/utils/previewModeIndicator.d.ts +19 -0
  25. package/dist/v2/core/utils/previewModeIndicator.js +414 -0
  26. package/dist/v2/core/utils/tokenStorage.d.ts +4 -0
  27. package/dist/v2/core/utils/tokenStorage.js +15 -1
  28. package/dist/v2/index.d.ts +6 -1
  29. package/dist/v2/index.js +6 -1
  30. package/dist/v2/react/components/ApplePayButton.d.ts +21 -121
  31. package/dist/v2/react/components/ApplePayButton.js +221 -290
  32. package/dist/v2/react/components/FunnelScriptInjector.d.ts +3 -1
  33. package/dist/v2/react/components/FunnelScriptInjector.js +128 -24
  34. package/dist/v2/react/components/PreviewModeIndicator.d.ts +46 -0
  35. package/dist/v2/react/components/PreviewModeIndicator.js +113 -0
  36. package/dist/v2/react/hooks/useApplePayCheckout.d.ts +16 -0
  37. package/dist/v2/react/hooks/useApplePayCheckout.js +193 -0
  38. package/dist/v2/react/hooks/useFunnel.d.ts +42 -6
  39. package/dist/v2/react/hooks/useFunnel.js +25 -5
  40. package/dist/v2/react/hooks/usePaymentPolling.d.ts +9 -3
  41. package/dist/v2/react/hooks/usePaymentPolling.js +31 -9
  42. package/dist/v2/react/hooks/usePaymentQuery.d.ts +32 -2
  43. package/dist/v2/react/hooks/usePaymentQuery.js +304 -7
  44. package/dist/v2/react/hooks/usePaymentRetrieve.d.ts +26 -0
  45. package/dist/v2/react/hooks/usePaymentRetrieve.js +175 -0
  46. package/dist/v2/react/hooks/useStepConfig.d.ts +62 -0
  47. package/dist/v2/react/hooks/useStepConfig.js +52 -0
  48. package/dist/v2/react/index.d.ts +9 -3
  49. package/dist/v2/react/index.js +5 -1
  50. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +27 -19
  51. package/dist/v2/react/providers/TagadaProvider.js +7 -7
  52. package/dist/v2/standalone/external-tracker.d.ts +2 -0
  53. package/dist/v2/standalone/external-tracker.js +6 -3
  54. package/package.json +112 -112
  55. package/dist/v2/react/hooks/useApplePay.d.ts +0 -16
  56. package/dist/v2/react/hooks/useApplePay.js +0 -247
@@ -1,45 +1,41 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /**
3
3
  * Apple Pay Button Component for v2 Architecture
4
- * Uses v2 useExpressPaymentMethods hook and follows clean architecture principles
4
+ * Clean rewrite based on working CMS implementation
5
5
  */
6
- import { useCallback, useEffect, useState, useMemo } from 'react';
7
- import { PaymentsResource } from '../../core/resources/payments';
8
- import { OrdersResource } from '../../core/resources/orders';
6
+ import { useCallback, useEffect, useMemo, useState } from 'react';
9
7
  import { useExpressPaymentMethods } from '../hooks/useExpressPaymentMethods';
10
- import { getGlobalApiClient } from '../hooks/useApiQuery';
8
+ import { usePaymentQuery } from '../hooks/usePaymentQuery';
11
9
  import { getBasisTheoryApiKey } from '../../../react/config/payment';
12
- import { minorUnitsToMajorUnits, getCurrencyInfo } from '../../../react/utils/money';
13
- export const ApplePayButton = ({ className = '', disabled = false, onSuccess, onError, onCancel, storeName, currencyCode = 'USD', variant = 'default', size = 'lg', checkout, }) => {
14
- const { applePayPaymentMethod, shippingMethods, lineItems, handleAddExpressId, updateCheckoutSessionValues, updateCustomerEmail, reComputeOrderSummary, setError: setContextError, } = useExpressPaymentMethods();
10
+ import { getCurrencyInfo, minorUnitsToMajorUnits } from '../../../react/utils/money';
11
+ // Helper function to convert Apple Pay contact to Address (matches CMS)
12
+ const applePayContactToAddress = (contact) => {
13
+ return {
14
+ address1: contact?.addressLines?.[0] || '',
15
+ address2: contact?.addressLines?.[1] || '',
16
+ lastName: contact?.familyName || '',
17
+ firstName: contact?.givenName || '',
18
+ city: contact?.locality || '',
19
+ state: contact?.administrativeArea || '',
20
+ country: contact?.countryCode || '',
21
+ postal: contact?.postalCode || '',
22
+ phone: contact?.phoneNumber || '',
23
+ email: contact?.emailAddress || '',
24
+ };
25
+ };
26
+ export const ApplePayButton = ({ checkout, onSuccess, onError, onCancel }) => {
27
+ const { applePayPaymentMethod, reComputeOrderSummary, shippingMethods, lineItems, handleAddExpressId, updateCheckoutSessionValues, updateCustomerEmail, setError: setContextError, } = useExpressPaymentMethods();
15
28
  const [processingPayment, setProcessingPayment] = useState(false);
16
29
  const [isApplePayAvailable, setIsApplePayAvailable] = useState(false);
17
- const [applePayError, setApplePayError] = useState(null);
18
- // Get Basis Theory API key from config (auto-detects environment)
30
+ // Get Basis Theory API key
19
31
  const basistheoryPublicKey = useMemo(() => getBasisTheoryApiKey(), []);
20
- // Create SDK resource clients
21
- const paymentsResource = useMemo(() => {
22
- try {
23
- return new PaymentsResource(getGlobalApiClient());
24
- }
25
- catch (error) {
26
- throw new Error('Failed to initialize payments resource: ' +
27
- (error instanceof Error ? error.message : 'Unknown error'));
28
- }
29
- }, []);
30
- const ordersResource = useMemo(() => {
31
- try {
32
- return new OrdersResource(getGlobalApiClient());
33
- }
34
- catch (error) {
35
- throw new Error('Failed to initialize orders resource: ' + (error instanceof Error ? error.message : 'Unknown error'));
36
- }
37
- }, []);
32
+ // Use payment hook for proper payment processing
33
+ const { processApplePayPayment } = usePaymentQuery();
38
34
  // Don't render if no Apple Pay payment method is enabled
39
35
  if (!applePayPaymentMethod) {
40
36
  return null;
41
37
  }
42
- // Check Apple Pay availability
38
+ // Check Apple Pay availability (matches CMS pattern)
43
39
  useEffect(() => {
44
40
  const checkApplePayAvailability = async () => {
45
41
  if (typeof window === 'undefined' || !window.ApplePaySession) {
@@ -61,42 +57,28 @@ export const ApplePayButton = ({ className = '', disabled = false, onSuccess, on
61
57
  }
62
58
  }
63
59
  catch (error) {
64
- console.error('Error checking Apple Pay availability:', error);
60
+ // Silently fail - Apple Pay just won't be available
65
61
  setIsApplePayAvailable(false);
66
62
  }
67
63
  };
68
64
  checkApplePayAvailability();
69
65
  }, [applePayPaymentMethod, handleAddExpressId]);
70
- // Helper to convert minor units to currency string for Apple Pay
71
- // Uses SDK's minorUnitsToMajorUnits which properly handles different currency decimal places
66
+ // Helper to convert minor units to currency string
72
67
  const minorUnitsToCurrencyString = useCallback((amountMinor, currency) => {
73
- if (!amountMinor || !currency)
74
- return '0.00';
68
+ // Fail safely - don't allow invalid data to become '0.00'
69
+ if (amountMinor === undefined || amountMinor === null || !currency) {
70
+ throw new Error(`Invalid currency data: amountMinor=${amountMinor}, currency=${currency}`);
71
+ }
75
72
  const currencyInfo = getCurrencyInfo(currency);
76
73
  if (!currencyInfo) {
77
- // Fallback to simple division if currency info not found
78
74
  console.warn(`Currency info not found for ${currency}, using fallback`);
79
75
  return (amountMinor / 100).toFixed(2);
80
76
  }
77
+ // 0 is a valid amount (e.g., free items)
81
78
  const majorUnits = minorUnitsToMajorUnits(amountMinor, currency);
82
79
  return majorUnits.toFixed(currencyInfo.ISOdigits);
83
80
  }, []);
84
- // Convert Apple Pay contact to internal Address format
85
- const applePayContactToAddress = useCallback((contact) => {
86
- return {
87
- address1: contact.addressLines?.[0] || '',
88
- address2: contact.addressLines?.[1] || '',
89
- lastName: contact.familyName || '',
90
- firstName: contact.givenName || '',
91
- city: contact.locality || '',
92
- state: contact.administrativeArea || '',
93
- country: contact.countryCode || '',
94
- postal: contact.postalCode || '',
95
- phone: contact.phoneNumber || '',
96
- email: contact.emailAddress || '',
97
- };
98
- }, []);
99
- // Validate merchant with Basis Theory
81
+ // Validate merchant with Basis Theory (matches CMS)
100
82
  const validateMerchant = useCallback(async () => {
101
83
  try {
102
84
  const response = await fetch('https://api.basistheory.com/apple-pay/session', {
@@ -120,9 +102,9 @@ export const ApplePayButton = ({ className = '', disabled = false, onSuccess, on
120
102
  console.error('Merchant validation failed:', error);
121
103
  throw error;
122
104
  }
123
- }, [checkout.checkoutSession.store?.name]);
124
- // Tokenize Apple Pay payment with Basis Theory
125
- const tokenizeApplePay = useCallback(async (token) => {
105
+ }, [basistheoryPublicKey, checkout.checkoutSession.store?.name]);
106
+ // Tokenize Apple Pay payment with Basis Theory (matches CMS)
107
+ const tokenizeApplePay = useCallback(async (event) => {
126
108
  try {
127
109
  const response = await fetch('https://api.basistheory.com/apple-pay', {
128
110
  method: 'POST',
@@ -131,278 +113,227 @@ export const ApplePayButton = ({ className = '', disabled = false, onSuccess, on
131
113
  'BT-API-KEY': basistheoryPublicKey,
132
114
  },
133
115
  body: JSON.stringify({
134
- apple_payment_data: token,
116
+ apple_payment_data: event.payment.token,
135
117
  }),
136
118
  });
137
119
  if (!response.ok) {
138
- throw new Error(`Failed to tokenize Apple Pay: ${response.status}`);
120
+ throw new Error(`HTTP error! Status: ${response.status}`);
139
121
  }
140
122
  const result = await response.json();
141
123
  return result.apple_pay; // Basis Theory returns the Apple Pay token in the apple_pay field
142
124
  }
143
125
  catch (error) {
144
- console.error('Tokenization failed:', error);
126
+ console.error('Tokenizing Apple Pay failed:', error);
145
127
  throw error;
146
128
  }
147
- }, []);
148
- // Process Apple Pay payment
149
- const processApplePayPayment = useCallback(async (token, billingContact, shippingContact) => {
129
+ }, [basistheoryPublicKey]);
130
+ // Process payment using the hook (handles order creation, instrument creation, payment processing, and polling)
131
+ const handleApplePayPayment = useCallback(async (payToken) => {
132
+ setProcessingPayment(true);
150
133
  try {
151
- setProcessingPayment(true);
152
- // Update addresses before processing payment
153
- if (shippingContact) {
154
- const shippingAddress = applePayContactToAddress(shippingContact);
155
- const billingAddress = billingContact ? applePayContactToAddress(billingContact) : null;
156
- await updateCheckoutSessionValues({
157
- data: {
158
- shippingAddress,
159
- billingAddress,
160
- },
161
- });
162
- }
163
- // Update customer email
164
- if (shippingContact?.emailAddress) {
165
- await updateCustomerEmail({
166
- data: {
167
- email: shippingContact.emailAddress,
168
- },
169
- });
170
- }
171
- // 1. Tokenize with Basis Theory
172
- const applePayToken = await tokenizeApplePay(token);
173
- // 2. Create payment instrument from the tokenized Apple Pay payment
174
- const paymentInstrumentData = {
175
- type: 'apple_pay',
176
- token: applePayToken.id,
177
- dpanType: applePayToken.type,
178
- card: {
179
- bin: applePayToken.card.bin,
180
- last4: applePayToken.card.last4,
181
- expirationMonth: applePayToken.card.expiration_month,
182
- expirationYear: applePayToken.card.expiration_year,
183
- brand: applePayToken.card.brand,
134
+ // Use the hook's method which handles everything properly
135
+ const result = await processApplePayPayment(checkout.checkoutSession.id, payToken, {
136
+ // Callbacks are handled by the hook, but we can intercept for custom behavior
137
+ onPaymentSuccess: (response) => {
138
+ // Keep processingPayment true for loading state during navigation
139
+ // The hook already returns the response, we just need to return it
140
+ },
141
+ onPaymentFailed: (error) => {
142
+ setProcessingPayment(false);
143
+ throw new Error(error.message);
184
144
  },
185
- };
186
- const paymentInstrument = await paymentsResource.createPaymentInstrument(paymentInstrumentData);
187
- if (!paymentInstrument?.id) {
188
- throw new Error('Failed to create payment instrument');
189
- }
190
- // 3. Create order from checkout session
191
- const orderResponse = await ordersResource.createOrder(checkout.checkoutSession.id);
192
- if (!orderResponse?.success || !orderResponse?.order?.id) {
193
- throw new Error('Failed to create order');
194
- }
195
- // 4. Process payment
196
- const paymentResult = await paymentsResource.processPaymentDirect(checkout.checkoutSession.id, paymentInstrument.id, undefined, {
197
- initiatedBy: 'customer',
198
- source: 'checkout',
199
145
  });
200
- if (onSuccess) {
201
- onSuccess(paymentResult);
202
- }
203
- return paymentResult;
146
+ // Return the result (contains payment and order)
147
+ return result;
204
148
  }
205
149
  catch (error) {
206
- console.error('Error processing Apple Pay payment:', error);
207
- const errorMessage = error instanceof Error ? error.message : 'Apple Pay payment failed';
208
- setApplePayError(errorMessage);
209
- setContextError(errorMessage);
210
- if (onError) {
211
- onError(errorMessage);
212
- }
213
- throw error;
214
- }
215
- finally {
150
+ console.error('Payment processing failed:', error);
216
151
  setProcessingPayment(false);
152
+ throw error;
217
153
  }
218
- }, [
219
- checkout.checkoutSession.id,
220
- applePayContactToAddress,
221
- updateCheckoutSessionValues,
222
- updateCustomerEmail,
223
- tokenizeApplePay,
224
- paymentsResource,
225
- ordersResource,
226
- onSuccess,
227
- onError,
228
- setContextError,
229
- ]);
230
- // Handle Apple Pay button click
154
+ }, [processApplePayPayment, checkout.checkoutSession.id]);
155
+ // Handle Apple Pay button click (matches CMS implementation)
231
156
  const handleApplePayClick = useCallback(() => {
232
157
  if (!isApplePayAvailable || processingPayment || !checkout.summary) {
233
158
  return;
234
159
  }
235
- const paymentRequest = {
160
+ const storeName = checkout.checkoutSession.store?.name || 'Store';
161
+ const total = {
162
+ label: storeName,
163
+ amount: minorUnitsToCurrencyString(checkout.summary.totalAdjustedAmount, checkout.summary.currency),
164
+ type: 'final',
165
+ };
166
+ const request = {
236
167
  countryCode: applePayPaymentMethod?.metadata?.country || 'US',
237
168
  currencyCode: checkout.summary.currency,
238
169
  supportedNetworks: ['visa', 'masterCard', 'amex', 'discover'],
239
170
  merchantCapabilities: ['supports3DS'],
240
- total: {
241
- label: storeName || checkout.checkoutSession.store?.name || 'Total',
242
- amount: minorUnitsToCurrencyString(checkout.summary.totalAdjustedAmount, checkout.summary.currency),
243
- type: 'final',
244
- },
245
- lineItems: lineItems.map((item) => ({
246
- label: item.label,
247
- amount: item.amount,
248
- type: 'final',
249
- })),
250
- shippingType: 'shipping',
251
- shippingMethods: shippingMethods.map((method) => ({
252
- label: method.label,
253
- amount: method.amount,
254
- detail: method.detail,
255
- identifier: method.identifier,
256
- })),
257
- requiredBillingContactFields: ['postalAddress', 'name', 'phone', 'email'],
258
- requiredShippingContactFields: ['postalAddress', 'name', 'phone', 'email'],
171
+ total,
172
+ shippingMethods,
173
+ lineItems,
174
+ requiredShippingContactFields: ['name', 'phone', 'email', 'postalAddress'],
175
+ requiredBillingContactFields: ['postalAddress'],
259
176
  };
260
- const session = new window.ApplePaySession(3, paymentRequest);
261
- session.onvalidatemerchant = async (event) => {
262
- try {
263
- console.log('Merchant validation requested for:', event.validationURL);
264
- const merchantSession = await validateMerchant();
265
- session.completeMerchantValidation(merchantSession);
266
- }
267
- catch (error) {
268
- console.error('Merchant validation failed:', error);
269
- session.abort();
270
- const errorMessage = 'Apple Pay setup failed';
271
- setApplePayError(errorMessage);
272
- setContextError(errorMessage);
273
- if (onError) {
274
- onError(errorMessage);
275
- }
276
- }
277
- };
278
- session.onshippingcontactselected = async (event) => {
279
- try {
280
- const shippingAddress = applePayContactToAddress(event.shippingContact);
281
- await updateCheckoutSessionValues({
282
- data: { shippingAddress },
283
- });
284
- const newOrderSummary = await reComputeOrderSummary();
285
- const update = {
286
- newShippingMethods: newOrderSummary?.shippingMethods.map((method) => ({
287
- label: method.label,
288
- amount: method.amount,
289
- detail: method.detail,
290
- identifier: method.identifier,
291
- })) || [],
292
- newTotal: {
293
- label: storeName || checkout.checkoutSession.store?.name || 'Total',
294
- amount: newOrderSummary?.total.amount || '0.00',
295
- type: 'final',
296
- },
297
- newLineItems: newOrderSummary?.lineItems.map((item) => ({
298
- label: item.label,
299
- amount: item.amount,
300
- type: 'final',
301
- })) || [],
302
- };
303
- session.completeShippingContactSelection(update);
304
- }
305
- catch (error) {
306
- console.error('Shipping contact selection failed:', error);
307
- session.completeShippingContactSelection({
308
- errors: [
309
- {
310
- code: 'shippingContactInvalid',
311
- message: 'Unable to ship to this address',
312
- },
313
- ],
314
- });
315
- }
316
- };
317
- session.onshippingmethodselected = async (event) => {
318
- try {
319
- // Update shipping method via API
320
- const response = await fetch('/api/v1/shipping-rates/select', {
321
- method: 'POST',
322
- headers: {
323
- 'Content-Type': 'application/json',
324
- },
325
- body: JSON.stringify({
326
- checkoutSessionId: checkout.checkoutSession.id,
327
- shippingRateId: event.shippingMethod.identifier,
328
- }),
329
- });
330
- if (!response.ok) {
331
- throw new Error('Failed to update shipping method');
177
+ try {
178
+ const session = new ApplePaySession(3, request);
179
+ // Merchant validation (matches CMS)
180
+ session.onvalidatemerchant = (event) => {
181
+ void (async () => {
182
+ try {
183
+ const merchantSession = await validateMerchant();
184
+ session.completeMerchantValidation(merchantSession);
185
+ }
186
+ catch (error) {
187
+ console.error('Merchant validation failed:', error);
188
+ session.abort();
189
+ }
190
+ })();
191
+ };
192
+ // Payment authorized (matches CMS)
193
+ session.onpaymentauthorized = (event) => {
194
+ void (async () => {
195
+ try {
196
+ const shippingContact = event.payment.shippingContact;
197
+ const billingContact = event.payment.billingContact;
198
+ const shippingAddress = applePayContactToAddress(shippingContact);
199
+ const billingAddress = applePayContactToAddress(billingContact);
200
+ await updateCheckoutSessionValues({
201
+ data: {
202
+ shippingAddress,
203
+ billingAddress,
204
+ },
205
+ });
206
+ if (shippingContact.emailAddress) {
207
+ await updateCustomerEmail({
208
+ data: {
209
+ email: shippingContact.emailAddress,
210
+ },
211
+ });
212
+ }
213
+ const applePayToken = await tokenizeApplePay(event);
214
+ session.completePayment(ApplePaySession.STATUS_SUCCESS);
215
+ const result = await handleApplePayPayment(applePayToken);
216
+ // Call success callback (triggers navigation)
217
+ // Note: processingPayment stays true until component unmounts during navigation
218
+ if (onSuccess) {
219
+ onSuccess(result);
220
+ }
221
+ }
222
+ catch (error) {
223
+ console.error('Payment processing failed:', error);
224
+ session.completePayment(ApplePaySession.STATUS_FAILURE);
225
+ const errorMessage = 'Payment Failed';
226
+ setContextError(errorMessage);
227
+ if (onError) {
228
+ onError(errorMessage);
229
+ }
230
+ }
231
+ })();
232
+ };
233
+ // Shipping method selected (matches CMS - abort if recompute fails)
234
+ session.onshippingmethodselected = (event) => {
235
+ void (async () => {
236
+ try {
237
+ // Update shipping method via API
238
+ await fetch(`/api/v1/checkout-sessions/${checkout.checkoutSession.id}/shipping-rate`, {
239
+ method: 'PUT',
240
+ headers: {
241
+ 'Content-Type': 'application/json',
242
+ },
243
+ body: JSON.stringify({
244
+ shippingRateId: event.shippingMethod.identifier,
245
+ }),
246
+ });
247
+ const newOrderSummary = await reComputeOrderSummary();
248
+ if (!newOrderSummary) {
249
+ session.abort();
250
+ return;
251
+ }
252
+ const { lineItems: newLineItems, total: newTotal } = newOrderSummary;
253
+ session.completeShippingMethodSelection(ApplePaySession.STATUS_SUCCESS, newTotal, newLineItems);
254
+ }
255
+ catch (error) {
256
+ console.error('Shipping method selection failed:', error);
257
+ session.abort();
258
+ }
259
+ })();
260
+ };
261
+ // Shipping contact selected (matches CMS - abort if recompute fails)
262
+ session.onshippingcontactselected = (event) => {
263
+ void (async () => {
264
+ const shippingContact = event.shippingContact;
265
+ try {
266
+ await updateCheckoutSessionValues({
267
+ data: {
268
+ shippingAddress: applePayContactToAddress(shippingContact),
269
+ },
270
+ });
271
+ const newOrderSummary = await reComputeOrderSummary();
272
+ if (!newOrderSummary) {
273
+ session.abort();
274
+ setContextError('Payment Failed');
275
+ return;
276
+ }
277
+ const { lineItems: newLineItems, total: newTotal, shippingMethods: newShippingMethods, } = newOrderSummary;
278
+ session.completeShippingContactSelection(ApplePaySession.STATUS_SUCCESS, newShippingMethods, newTotal, newLineItems);
279
+ }
280
+ catch (error) {
281
+ console.error('Shipping contact selection failed:', error);
282
+ session.abort();
283
+ }
284
+ })();
285
+ };
286
+ session.onerror = (event) => {
287
+ console.error('Apple Pay Session Error:', event);
288
+ };
289
+ session.oncancel = () => {
290
+ console.log('Payment cancelled by user');
291
+ if (onCancel) {
292
+ onCancel();
332
293
  }
333
- const newOrderSummary = await reComputeOrderSummary();
334
- const update = {
335
- newTotal: {
336
- label: storeName || checkout.checkoutSession.store?.name || 'Total',
337
- amount: newOrderSummary?.total.amount || '0.00',
338
- type: 'final',
339
- },
340
- newLineItems: newOrderSummary?.lineItems.map((item) => ({
341
- label: item.label,
342
- amount: item.amount,
343
- type: 'final',
344
- })) || [],
345
- };
346
- session.completeShippingMethodSelection(update);
347
- }
348
- catch (error) {
349
- console.error('Shipping method selection failed:', error);
350
- session.completeShippingMethodSelection({});
351
- }
352
- };
353
- session.onpaymentauthorized = async (event) => {
354
- try {
355
- await processApplePayPayment(event.payment.token, event.payment.billingContact, event.payment.shippingContact);
356
- session.completePayment({
357
- status: window.ApplePaySession.STATUS_SUCCESS,
358
- });
359
- }
360
- catch (error) {
361
- console.error('Payment authorization failed:', error);
362
- session.completePayment({
363
- status: window.ApplePaySession.STATUS_FAILURE,
364
- });
365
- }
366
- };
367
- session.oncancel = () => {
368
- if (onCancel) {
369
- onCancel();
370
- }
371
- };
372
- session.begin();
294
+ };
295
+ session.begin();
296
+ }
297
+ catch (error) {
298
+ console.error('Failed to start Apple Pay session:', error);
299
+ }
373
300
  }, [
374
301
  isApplePayAvailable,
375
302
  processingPayment,
376
303
  checkout,
377
- storeName,
378
- lineItems,
379
304
  shippingMethods,
305
+ lineItems,
306
+ applePayPaymentMethod,
307
+ minorUnitsToCurrencyString,
380
308
  validateMerchant,
381
- applePayContactToAddress,
309
+ tokenizeApplePay,
310
+ processApplePayPayment,
382
311
  updateCheckoutSessionValues,
312
+ updateCustomerEmail,
383
313
  reComputeOrderSummary,
384
- processApplePayPayment,
385
- onCancel,
386
- onError,
387
314
  setContextError,
388
- minorUnitsToCurrencyString,
389
- applePayPaymentMethod,
315
+ onSuccess,
316
+ onError,
317
+ onCancel,
390
318
  ]);
391
- // Button size classes
392
- const sizeClasses = {
393
- sm: 'h-8 px-3 text-sm',
394
- md: 'h-10 px-4 text-base',
395
- lg: 'h-12 px-6 text-lg',
396
- };
397
- // Button variant classes
398
- const variantClasses = {
399
- default: 'bg-black text-white hover:bg-black/90',
400
- outline: 'border border-black bg-white text-black hover:bg-black hover:text-white',
401
- ghost: 'text-black hover:bg-black/10',
402
- };
403
- if (!isApplePayAvailable) {
404
- return null;
405
- }
406
- return (_jsxs("div", { className: "w-full", children: [_jsx("button", { type: "button", onClick: handleApplePayClick, disabled: disabled || processingPayment, className: `relative w-full rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${sizeClasses[size]} ${variantClasses[variant]} ${className} `, children: processingPayment ? (_jsxs("div", { className: "flex items-center justify-center gap-2", children: [_jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" }), _jsx("span", { children: "Processing..." })] })) : (_jsxs("span", { className: "flex items-center justify-center gap-2", children: [_jsx("svg", { className: "h-6 w-6", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z" }) }), "Pay with Apple Pay"] })) }), applePayError && (_jsx("div", { className: "mt-2 rounded border border-red-200 bg-red-50 p-2 text-sm text-red-600", children: applePayError }))] }));
319
+ // if (!isApplePayAvailable) {
320
+ // return null;
321
+ // }
322
+ return (_jsxs("button", { onClick: handleApplePayClick, disabled: processingPayment, style: {
323
+ backgroundColor: '#000',
324
+ color: '#fff',
325
+ border: 'none',
326
+ borderRadius: '4px',
327
+ padding: '12px',
328
+ width: '100%',
329
+ fontSize: '16px',
330
+ fontWeight: '500',
331
+ cursor: processingPayment ? 'not-allowed' : 'pointer',
332
+ opacity: processingPayment ? 0.6 : 1,
333
+ display: 'flex',
334
+ alignItems: 'center',
335
+ justifyContent: 'center',
336
+ gap: '8px',
337
+ }, children: [_jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z" }) }), processingPayment ? 'Processing...' : 'Pay with Apple Pay'] }));
407
338
  };
408
339
  export default ApplePayButton;
@@ -6,7 +6,9 @@ interface FunnelScriptInjectorProps extends FunnelState {
6
6
  *
7
7
  * This component:
8
8
  * - Sets up Tagada on the window object
9
- * - Injects and manages funnel scripts from the context
9
+ * - Injects scripts from stepConfig.scripts (NEW: from HTML injection, supports A/B variants)
10
+ * - Falls back to context.script (LEGACY: from funnel context)
11
+ * - Handles script positions: head-start, head-end, body-start, body-end
10
12
  * - Prevents duplicate script injection (handles React StrictMode)
11
13
  * - Cleans up scripts when context changes or component unmounts
12
14
  */