@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.
- package/README.md +1129 -1129
- package/build-cdn.js +113 -113
- package/dist/external-tracker.js +1104 -491
- package/dist/external-tracker.min.js +2 -2
- package/dist/external-tracker.min.js.map +4 -4
- package/dist/react/hooks/useApplePay.js +25 -36
- package/dist/react/hooks/usePaymentPolling.d.ts +9 -3
- package/dist/react/providers/TagadaProvider.js +5 -5
- package/dist/react/utils/money.d.ts +4 -3
- package/dist/react/utils/money.js +39 -6
- package/dist/react/utils/trackingUtils.js +1 -0
- package/dist/v2/core/client.js +34 -2
- package/dist/v2/core/config/environment.js +9 -2
- package/dist/v2/core/funnelClient.d.ts +92 -1
- package/dist/v2/core/funnelClient.js +247 -3
- package/dist/v2/core/resources/apiClient.js +1 -1
- package/dist/v2/core/resources/checkout.d.ts +68 -0
- package/dist/v2/core/resources/funnel.d.ts +15 -0
- package/dist/v2/core/resources/payments.d.ts +50 -3
- package/dist/v2/core/resources/payments.js +38 -7
- package/dist/v2/core/utils/pluginConfig.js +40 -5
- package/dist/v2/core/utils/previewMode.d.ts +3 -0
- package/dist/v2/core/utils/previewMode.js +44 -14
- package/dist/v2/core/utils/previewModeIndicator.d.ts +19 -0
- package/dist/v2/core/utils/previewModeIndicator.js +414 -0
- package/dist/v2/core/utils/tokenStorage.d.ts +4 -0
- package/dist/v2/core/utils/tokenStorage.js +15 -1
- package/dist/v2/index.d.ts +6 -1
- package/dist/v2/index.js +6 -1
- package/dist/v2/react/components/ApplePayButton.d.ts +21 -121
- package/dist/v2/react/components/ApplePayButton.js +221 -290
- package/dist/v2/react/components/FunnelScriptInjector.d.ts +3 -1
- package/dist/v2/react/components/FunnelScriptInjector.js +128 -24
- package/dist/v2/react/components/PreviewModeIndicator.d.ts +46 -0
- package/dist/v2/react/components/PreviewModeIndicator.js +113 -0
- package/dist/v2/react/hooks/useApplePayCheckout.d.ts +16 -0
- package/dist/v2/react/hooks/useApplePayCheckout.js +193 -0
- package/dist/v2/react/hooks/useFunnel.d.ts +42 -6
- package/dist/v2/react/hooks/useFunnel.js +25 -5
- package/dist/v2/react/hooks/usePaymentPolling.d.ts +9 -3
- package/dist/v2/react/hooks/usePaymentPolling.js +31 -9
- package/dist/v2/react/hooks/usePaymentQuery.d.ts +32 -2
- package/dist/v2/react/hooks/usePaymentQuery.js +304 -7
- package/dist/v2/react/hooks/usePaymentRetrieve.d.ts +26 -0
- package/dist/v2/react/hooks/usePaymentRetrieve.js +175 -0
- package/dist/v2/react/hooks/useStepConfig.d.ts +62 -0
- package/dist/v2/react/hooks/useStepConfig.js +52 -0
- package/dist/v2/react/index.d.ts +9 -3
- package/dist/v2/react/index.js +5 -1
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +27 -19
- package/dist/v2/react/providers/TagadaProvider.js +7 -7
- package/dist/v2/standalone/external-tracker.d.ts +2 -0
- package/dist/v2/standalone/external-tracker.js +6 -3
- package/package.json +112 -112
- package/dist/v2/react/hooks/useApplePay.d.ts +0 -16
- 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
|
-
*
|
|
4
|
+
* Clean rewrite based on working CMS implementation
|
|
5
5
|
*/
|
|
6
|
-
import { useCallback, useEffect,
|
|
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 {
|
|
8
|
+
import { usePaymentQuery } from '../hooks/usePaymentQuery';
|
|
11
9
|
import { getBasisTheoryApiKey } from '../../../react/config/payment';
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
18
|
-
// Get Basis Theory API key from config (auto-detects environment)
|
|
30
|
+
// Get Basis Theory API key
|
|
19
31
|
const basistheoryPublicKey = useMemo(() => getBasisTheoryApiKey(), []);
|
|
20
|
-
//
|
|
21
|
-
const
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
74
|
-
|
|
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
|
-
//
|
|
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 (
|
|
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(`
|
|
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('
|
|
126
|
+
console.error('Tokenizing Apple Pay failed:', error);
|
|
145
127
|
throw error;
|
|
146
128
|
}
|
|
147
|
-
}, []);
|
|
148
|
-
// Process
|
|
149
|
-
const
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
return paymentResult;
|
|
146
|
+
// Return the result (contains payment and order)
|
|
147
|
+
return result;
|
|
204
148
|
}
|
|
205
149
|
catch (error) {
|
|
206
|
-
console.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
|
-
|
|
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
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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
|
-
|
|
309
|
+
tokenizeApplePay,
|
|
310
|
+
processApplePayPayment,
|
|
382
311
|
updateCheckoutSessionValues,
|
|
312
|
+
updateCustomerEmail,
|
|
383
313
|
reComputeOrderSummary,
|
|
384
|
-
processApplePayPayment,
|
|
385
|
-
onCancel,
|
|
386
|
-
onError,
|
|
387
314
|
setContextError,
|
|
388
|
-
|
|
389
|
-
|
|
315
|
+
onSuccess,
|
|
316
|
+
onError,
|
|
317
|
+
onCancel,
|
|
390
318
|
]);
|
|
391
|
-
//
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
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
|
|
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
|
*/
|