@tagadapay/plugin-sdk 3.1.5 → 3.1.9

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 (71) hide show
  1. package/README.md +1129 -1129
  2. package/build-cdn.js +220 -113
  3. package/dist/external-tracker.js +1225 -558
  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/tagada-sdk.js +10142 -0
  13. package/dist/tagada-sdk.min.js +43 -0
  14. package/dist/tagada-sdk.min.js.map +7 -0
  15. package/dist/v2/core/client.js +34 -2
  16. package/dist/v2/core/config/environment.js +9 -2
  17. package/dist/v2/core/funnelClient.d.ts +180 -2
  18. package/dist/v2/core/funnelClient.js +289 -6
  19. package/dist/v2/core/resources/apiClient.js +1 -1
  20. package/dist/v2/core/resources/checkout.d.ts +68 -0
  21. package/dist/v2/core/resources/funnel.d.ts +25 -0
  22. package/dist/v2/core/resources/payments.d.ts +70 -3
  23. package/dist/v2/core/resources/payments.js +72 -7
  24. package/dist/v2/core/utils/index.d.ts +1 -0
  25. package/dist/v2/core/utils/index.js +2 -0
  26. package/dist/v2/core/utils/pluginConfig.d.ts +8 -0
  27. package/dist/v2/core/utils/pluginConfig.js +68 -5
  28. package/dist/v2/core/utils/previewMode.d.ts +7 -0
  29. package/dist/v2/core/utils/previewMode.js +72 -14
  30. package/dist/v2/core/utils/previewModeIndicator.d.ts +19 -0
  31. package/dist/v2/core/utils/previewModeIndicator.js +414 -0
  32. package/dist/v2/core/utils/tokenStorage.d.ts +4 -0
  33. package/dist/v2/core/utils/tokenStorage.js +15 -1
  34. package/dist/v2/index.d.ts +9 -3
  35. package/dist/v2/index.js +8 -3
  36. package/dist/v2/react/components/ApplePayButton.d.ts +22 -123
  37. package/dist/v2/react/components/ApplePayButton.js +247 -317
  38. package/dist/v2/react/components/FunnelScriptInjector.d.ts +3 -1
  39. package/dist/v2/react/components/FunnelScriptInjector.js +255 -162
  40. package/dist/v2/react/components/GooglePayButton.d.ts +2 -0
  41. package/dist/v2/react/components/GooglePayButton.js +80 -64
  42. package/dist/v2/react/components/PreviewModeIndicator.d.ts +46 -0
  43. package/dist/v2/react/components/PreviewModeIndicator.js +113 -0
  44. package/dist/v2/react/hooks/useApplePayCheckout.d.ts +16 -0
  45. package/dist/v2/react/hooks/useApplePayCheckout.js +193 -0
  46. package/dist/v2/react/hooks/useFunnel.d.ts +48 -6
  47. package/dist/v2/react/hooks/useFunnel.js +25 -5
  48. package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +10 -0
  49. package/dist/v2/react/hooks/useGoogleAutocomplete.js +48 -0
  50. package/dist/v2/react/hooks/useGooglePayCheckout.d.ts +21 -0
  51. package/dist/v2/react/hooks/useGooglePayCheckout.js +198 -0
  52. package/dist/v2/react/hooks/usePaymentPolling.d.ts +15 -3
  53. package/dist/v2/react/hooks/usePaymentPolling.js +31 -9
  54. package/dist/v2/react/hooks/usePaymentQuery.d.ts +34 -2
  55. package/dist/v2/react/hooks/usePaymentQuery.js +731 -7
  56. package/dist/v2/react/hooks/usePaymentRetrieve.d.ts +26 -0
  57. package/dist/v2/react/hooks/usePaymentRetrieve.js +175 -0
  58. package/dist/v2/react/hooks/usePixelTracking.d.ts +56 -0
  59. package/dist/v2/react/hooks/usePixelTracking.js +508 -0
  60. package/dist/v2/react/hooks/useStepConfig.d.ts +64 -0
  61. package/dist/v2/react/hooks/useStepConfig.js +53 -0
  62. package/dist/v2/react/index.d.ts +15 -5
  63. package/dist/v2/react/index.js +8 -2
  64. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +1 -0
  65. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +41 -13
  66. package/dist/v2/react/providers/TagadaProvider.js +24 -23
  67. package/dist/v2/standalone/external-tracker.d.ts +2 -0
  68. package/dist/v2/standalone/external-tracker.js +6 -3
  69. package/package.json +112 -112
  70. package/dist/v2/react/hooks/useApplePay.d.ts +0 -16
  71. package/dist/v2/react/hooks/useApplePay.js +0 -247
@@ -1,102 +1,70 @@
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';
9
- import { useExpressPaymentMethods } from '../hooks/useExpressPaymentMethods';
10
- import { getGlobalApiClient } from '../hooks/useApiQuery';
6
+ import { useCallback, useEffect, useMemo, useState } from 'react';
11
7
  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();
8
+ import { getCurrencyInfo, minorUnitsToMajorUnits } from '../../../react/utils/money';
9
+ import { useExpressPaymentMethods } from '../hooks/useExpressPaymentMethods';
10
+ import { usePaymentQuery } from '../hooks/usePaymentQuery';
11
+ import { useShippingRatesQuery } from '../hooks/useShippingRatesQuery';
12
+ // Helper function to convert Apple Pay contact to Address (matches CMS)
13
+ const applePayContactToAddress = (contact) => {
14
+ return {
15
+ address1: contact?.addressLines?.[0] || '',
16
+ address2: contact?.addressLines?.[1] || '',
17
+ lastName: contact?.familyName || '',
18
+ firstName: contact?.givenName || '',
19
+ city: contact?.locality || '',
20
+ state: contact?.administrativeArea || '',
21
+ country: contact?.countryCode || '',
22
+ postal: contact?.postalCode || '',
23
+ phone: contact?.phoneNumber || '',
24
+ email: contact?.emailAddress || '',
25
+ };
26
+ };
27
+ export const ApplePayButton = ({ checkout, onSuccess, onError, onCancel }) => {
28
+ const { applePayPaymentMethod, reComputeOrderSummary, shippingMethods, lineItems, handleAddExpressId, updateCheckoutSessionValues, updateCustomerEmail, setError: setContextError, } = useExpressPaymentMethods();
15
29
  const [processingPayment, setProcessingPayment] = useState(false);
16
30
  const [isApplePayAvailable, setIsApplePayAvailable] = useState(false);
17
- const [applePayError, setApplePayError] = useState(null);
18
- // Get Basis Theory API key from config (auto-detects environment)
31
+ // Get Basis Theory API key
19
32
  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
- }, []);
33
+ // Use payment hook for proper payment processing
34
+ const { processApplePayPayment } = usePaymentQuery();
35
+ // Use shipping rates hook for selecting shipping rates
36
+ const { selectRate } = useShippingRatesQuery({ checkout });
38
37
  // Don't render if no Apple Pay payment method is enabled
39
38
  if (!applePayPaymentMethod) {
40
39
  return null;
41
40
  }
42
- // Check Apple Pay availability
41
+ // Check Apple Pay availability (matches CMS pattern - useApplePayAvailable hook)
43
42
  useEffect(() => {
44
- const checkApplePayAvailability = async () => {
45
- if (typeof window === 'undefined' || !window.ApplePaySession) {
46
- setIsApplePayAvailable(false);
47
- return;
48
- }
49
- try {
50
- const canMakePayments = window.ApplePaySession.canMakePayments();
51
- if (canMakePayments) {
52
- const merchantId = applePayPaymentMethod?.metadata?.merchantId || '';
53
- const canMakePaymentsWithActiveCard = await window.ApplePaySession.canMakePaymentsWithActiveCard(merchantId);
54
- setIsApplePayAvailable(canMakePaymentsWithActiveCard);
55
- if (canMakePaymentsWithActiveCard) {
56
- handleAddExpressId('apple_pay');
57
- }
58
- }
59
- else {
60
- setIsApplePayAvailable(false);
61
- }
62
- }
63
- catch (error) {
64
- console.error('Error checking Apple Pay availability:', error);
65
- setIsApplePayAvailable(false);
66
- }
67
- };
68
- checkApplePayAvailability();
69
- }, [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
43
+ const addExpress = () => handleAddExpressId('apple_pay');
44
+ if (window?.ApplePaySession && ApplePaySession.canMakePayments()) {
45
+ setIsApplePayAvailable(true);
46
+ addExpress();
47
+ }
48
+ else {
49
+ setIsApplePayAvailable(false);
50
+ }
51
+ }, [handleAddExpressId]);
52
+ // Helper to convert minor units to currency string
72
53
  const minorUnitsToCurrencyString = useCallback((amountMinor, currency) => {
73
- if (!amountMinor || !currency)
74
- return '0.00';
54
+ // Fail safely - don't allow invalid data to become '0.00'
55
+ if (amountMinor === undefined || amountMinor === null || !currency) {
56
+ throw new Error(`Invalid currency data: amountMinor=${amountMinor}, currency=${currency}`);
57
+ }
75
58
  const currencyInfo = getCurrencyInfo(currency);
76
59
  if (!currencyInfo) {
77
- // Fallback to simple division if currency info not found
78
60
  console.warn(`Currency info not found for ${currency}, using fallback`);
79
61
  return (amountMinor / 100).toFixed(2);
80
62
  }
63
+ // 0 is a valid amount (e.g., free items)
81
64
  const majorUnits = minorUnitsToMajorUnits(amountMinor, currency);
82
65
  return majorUnits.toFixed(currencyInfo.ISOdigits);
83
66
  }, []);
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
67
+ // Validate merchant with Basis Theory (matches CMS)
100
68
  const validateMerchant = useCallback(async () => {
101
69
  try {
102
70
  const response = await fetch('https://api.basistheory.com/apple-pay/session', {
@@ -111,18 +79,18 @@ export const ApplePayButton = ({ className = '', disabled = false, onSuccess, on
111
79
  }),
112
80
  });
113
81
  if (!response.ok) {
114
- throw new Error(`Failed to validate merchant: ${response.status}`);
82
+ throw new Error(`HTTP error! Status: ${response.status}`);
115
83
  }
116
84
  const merchantSession = await response.json();
117
85
  return merchantSession;
118
86
  }
119
- catch (error) {
120
- console.error('Merchant validation failed:', error);
121
- throw error;
87
+ catch (err) {
88
+ console.error('[ApplePay] Merchant validation failed:', err);
89
+ throw err;
122
90
  }
123
- }, [checkout.checkoutSession.store?.name]);
124
- // Tokenize Apple Pay payment with Basis Theory
125
- const tokenizeApplePay = useCallback(async (token) => {
91
+ }, [basistheoryPublicKey, checkout.checkoutSession.store?.name]);
92
+ // Tokenize Apple Pay payment with Basis Theory (matches CMS)
93
+ const tokenizeApplePay = useCallback(async (event) => {
126
94
  try {
127
95
  const response = await fetch('https://api.basistheory.com/apple-pay', {
128
96
  method: 'POST',
@@ -131,278 +99,240 @@ export const ApplePayButton = ({ className = '', disabled = false, onSuccess, on
131
99
  'BT-API-KEY': basistheoryPublicKey,
132
100
  },
133
101
  body: JSON.stringify({
134
- apple_payment_data: token,
102
+ apple_payment_data: event.payment.token,
135
103
  }),
136
104
  });
137
105
  if (!response.ok) {
138
- throw new Error(`Failed to tokenize Apple Pay: ${response.status}`);
106
+ throw new Error(`HTTP error! Status: ${response.status}`);
139
107
  }
140
108
  const result = await response.json();
141
109
  return result.apple_pay; // Basis Theory returns the Apple Pay token in the apple_pay field
142
110
  }
143
111
  catch (error) {
144
- console.error('Tokenization failed:', error);
112
+ console.error('[ApplePay] Tokenizing Apple Pay failed:', error);
145
113
  throw error;
146
114
  }
147
- }, []);
148
- // Process Apple Pay payment
149
- const processApplePayPayment = useCallback(async (token, billingContact, shippingContact) => {
115
+ }, [basistheoryPublicKey]);
116
+ // Process payment using the hook (handles order creation, instrument creation, payment processing, and polling)
117
+ const handleApplePayPayment = useCallback(async (payToken) => {
118
+ setProcessingPayment(true);
150
119
  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,
120
+ // Use the hook's method which handles everything properly
121
+ const result = await processApplePayPayment(checkout.checkoutSession.id, payToken, {
122
+ // Callbacks are handled by the hook, but we can intercept for custom behavior
123
+ onPaymentSuccess: (response) => {
124
+ // Keep processingPayment true for loading state during navigation
125
+ // The hook already returns the response, we just need to return it
126
+ },
127
+ onPaymentFailed: (error) => {
128
+ console.error('[ApplePay] Payment failed:', error);
129
+ setProcessingPayment(false);
130
+ throw new Error(error.message);
184
131
  },
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
132
  });
200
- if (onSuccess) {
201
- onSuccess(paymentResult);
202
- }
203
- return paymentResult;
133
+ // Return the result (contains payment and order)
134
+ return result;
204
135
  }
205
136
  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 {
137
+ console.error('[ApplePay] Payment processing failed:', error);
216
138
  setProcessingPayment(false);
139
+ throw error;
217
140
  }
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
141
+ }, [processApplePayPayment, checkout.checkoutSession.id]);
142
+ // Handle Apple Pay button click (matches CMS implementation)
231
143
  const handleApplePayClick = useCallback(() => {
232
144
  if (!isApplePayAvailable || processingPayment || !checkout.summary) {
233
145
  return;
234
146
  }
235
- const paymentRequest = {
147
+ const storeName = checkout.checkoutSession.store?.name || 'Store';
148
+ const total = {
149
+ label: storeName,
150
+ amount: minorUnitsToCurrencyString(checkout.summary.totalAdjustedAmount, checkout.summary.currency),
151
+ type: 'final',
152
+ };
153
+ const request = {
236
154
  countryCode: applePayPaymentMethod?.metadata?.country || 'US',
237
155
  currencyCode: checkout.summary.currency,
238
156
  supportedNetworks: ['visa', 'masterCard', 'amex', 'discover'],
239
157
  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'],
158
+ total,
159
+ shippingMethods,
160
+ lineItems,
161
+ requiredShippingContactFields: ['name', 'phone', 'email', 'postalAddress'],
162
+ requiredBillingContactFields: ['postalAddress'],
259
163
  };
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');
164
+ try {
165
+ const session = new ApplePaySession(3, request);
166
+ // Merchant validation (matches CMS)
167
+ session.onvalidatemerchant = (event) => {
168
+ void (async () => {
169
+ try {
170
+ const merchantSession = await validateMerchant();
171
+ session.completeMerchantValidation(merchantSession);
172
+ }
173
+ catch (error) {
174
+ console.error('[ApplePay] Merchant validation failed:', error);
175
+ session.abort();
176
+ }
177
+ })();
178
+ };
179
+ // Payment authorized (matches CMS)
180
+ session.onpaymentauthorized = (event) => {
181
+ void (async () => {
182
+ try {
183
+ const shippingContact = event.payment.shippingContact;
184
+ const billingContact = event.payment.billingContact;
185
+ const shippingAddress = applePayContactToAddress(shippingContact);
186
+ const billingAddress = applePayContactToAddress(billingContact);
187
+ await updateCheckoutSessionValues({
188
+ data: {
189
+ shippingAddress,
190
+ billingAddress,
191
+ },
192
+ });
193
+ if (shippingContact.emailAddress) {
194
+ await updateCustomerEmail({
195
+ data: {
196
+ email: shippingContact.emailAddress,
197
+ },
198
+ });
199
+ }
200
+ const applePayToken = await tokenizeApplePay(event);
201
+ session.completePayment(ApplePaySession.STATUS_SUCCESS);
202
+ const result = await handleApplePayPayment(applePayToken);
203
+ // Call success callback (triggers navigation)
204
+ // Note: processingPayment stays true until component unmounts during navigation
205
+ if (onSuccess) {
206
+ onSuccess(result);
207
+ }
208
+ }
209
+ catch (error) {
210
+ console.error('[ApplePay] Payment processing failed:', error);
211
+ session.completePayment(ApplePaySession.STATUS_FAILURE);
212
+ const errorMessage = 'Payment Failed';
213
+ setContextError(errorMessage);
214
+ if (onError) {
215
+ onError(errorMessage);
216
+ }
217
+ }
218
+ })();
219
+ };
220
+ // Shipping method selected (matches CMS - abort if recompute fails)
221
+ session.onshippingmethodselected = (event) => {
222
+ void (async () => {
223
+ try {
224
+ // Update shipping method via client
225
+ await selectRate(event.shippingMethod.identifier);
226
+ const newOrderSummary = await reComputeOrderSummary();
227
+ if (!newOrderSummary) {
228
+ console.error('[ApplePay] No order summary returned, aborting session');
229
+ session.abort();
230
+ return;
231
+ }
232
+ const { lineItems: newLineItems, total: newTotal } = newOrderSummary;
233
+ // Ensure type: 'final' is present for Apple Pay (matches CMS)
234
+ const formattedTotal = {
235
+ ...newTotal,
236
+ type: 'final',
237
+ };
238
+ const formattedLineItems = newLineItems.map((item) => ({
239
+ ...item,
240
+ type: 'final',
241
+ }));
242
+ session.completeShippingMethodSelection(ApplePaySession.STATUS_SUCCESS, formattedTotal, formattedLineItems);
243
+ }
244
+ catch (error) {
245
+ console.error('[ApplePay] Shipping method selection failed:', error);
246
+ session.abort();
247
+ }
248
+ })();
249
+ };
250
+ // Shipping contact selected (matches CMS - abort if recompute fails)
251
+ session.onshippingcontactselected = (event) => {
252
+ void (async () => {
253
+ const shippingContact = event.shippingContact;
254
+ try {
255
+ await updateCheckoutSessionValues({
256
+ data: {
257
+ shippingAddress: applePayContactToAddress(shippingContact),
258
+ },
259
+ });
260
+ const newOrderSummary = await reComputeOrderSummary();
261
+ if (!newOrderSummary) {
262
+ console.error('[ApplePay] No order summary returned, aborting session');
263
+ session.abort();
264
+ setContextError('Payment Failed');
265
+ return;
266
+ }
267
+ const { lineItems: newLineItems, total: newTotal, shippingMethods: newShippingMethods, } = newOrderSummary;
268
+ // Ensure type: 'final' is present for Apple Pay (matches CMS)
269
+ const formattedTotal = {
270
+ ...newTotal,
271
+ type: 'final',
272
+ };
273
+ const formattedLineItems = newLineItems.map((item) => ({
274
+ ...item,
275
+ type: 'final',
276
+ }));
277
+ session.completeShippingContactSelection(ApplePaySession.STATUS_SUCCESS, newShippingMethods, formattedTotal, formattedLineItems);
278
+ }
279
+ catch (error) {
280
+ console.error('[ApplePay] Shipping contact selection failed:', error);
281
+ session.abort();
282
+ }
283
+ })();
284
+ };
285
+ session.onerror = (event) => {
286
+ console.error('[ApplePay] Session Error:', event);
287
+ };
288
+ session.oncancel = () => {
289
+ if (onCancel) {
290
+ onCancel();
332
291
  }
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();
292
+ };
293
+ session.begin();
294
+ }
295
+ catch (error) {
296
+ console.error('[ApplePay] Failed to start Apple Pay session:', error);
297
+ }
373
298
  }, [
374
299
  isApplePayAvailable,
375
300
  processingPayment,
376
301
  checkout,
377
- storeName,
378
- lineItems,
379
302
  shippingMethods,
303
+ lineItems,
304
+ applePayPaymentMethod,
305
+ minorUnitsToCurrencyString,
380
306
  validateMerchant,
381
- applePayContactToAddress,
307
+ tokenizeApplePay,
308
+ processApplePayPayment,
382
309
  updateCheckoutSessionValues,
310
+ updateCustomerEmail,
383
311
  reComputeOrderSummary,
384
- processApplePayPayment,
385
- onCancel,
386
- onError,
387
312
  setContextError,
388
- minorUnitsToCurrencyString,
389
- applePayPaymentMethod,
313
+ selectRate,
314
+ onSuccess,
315
+ onError,
316
+ onCancel,
390
317
  ]);
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
318
  if (!isApplePayAvailable) {
404
319
  return null;
405
320
  }
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 }))] }));
321
+ return (_jsxs("button", { onClick: handleApplePayClick, disabled: processingPayment, style: {
322
+ backgroundColor: '#000',
323
+ color: '#fff',
324
+ border: 'none',
325
+ borderRadius: '4px',
326
+ padding: '12px',
327
+ width: '100%',
328
+ fontSize: '16px',
329
+ fontWeight: '500',
330
+ cursor: processingPayment ? 'not-allowed' : 'pointer',
331
+ opacity: processingPayment ? 0.6 : 1,
332
+ display: 'flex',
333
+ alignItems: 'center',
334
+ justifyContent: 'center',
335
+ gap: '8px',
336
+ }, type: "button", 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
337
  };
408
338
  export default ApplePayButton;